diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a6118732d4ae..126e54a7b49e 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,8 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckShadowing) :: // Check shadowing elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 312329c0f85d..68671755df37 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.config.SourceVersion import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.rewrites.Rewrites import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory} +import Setting.ChoiceWithHelp import scala.util.chaining._ @@ -156,7 +157,6 @@ private sealed trait VerboseSettings: */ private sealed trait WarningSettings: self: SettingGroup => - import Setting.ChoiceWithHelp val Whelp: Setting[Boolean] = BooleanSetting("-W", "Print a synopsis of warning options.") val XfatalWarnings: Setting[Boolean] = BooleanSetting("-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) @@ -307,6 +307,27 @@ private sealed trait XSettings: } val XmacroSettings: Setting[List[String]] = MultiStringSetting("-Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") + + val Xlint: Setting[List[ChoiceWithHelp[String]]] = UncompleteMultiChoiceHelpSetting( + name = "-Xlint", + helpArg = "advanced warning", + descr = "Enable or disable specific `lint` warnings", + choices = List( + ChoiceWithHelp("all", ""), + ChoiceWithHelp("private-shadow", "Warn if a private field or class parameter shadows a superclass field"), + ChoiceWithHelp("type-parameter-shadow", "Warn when a type parameter shadows a type already in the scope"), + ), + default = Nil + ) + + object XlintHas: + def allOr(s: String)(using Context) = + Xlint.value.pipe(us => us.contains("all") || us.contains(s)) + def privateShadow(using Context) = + allOr("private-shadow") + def typeParameterShadow(using Context) = + allOr("type-parameter-shadow") + end XSettings /** -Y "Forking" as in forked tongue or "Private" settings */ diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 34e5582e8a91..d992f5bdf2ee 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -62,6 +62,7 @@ object Settings: prefix: String = "", aliases: List[String] = Nil, depends: List[(Setting[?], Any)] = Nil, + ignoreInvalidArgs: Boolean = false, propertyClass: Option[Class[?]] = None)(private[Settings] val idx: Int) { private var changed: Boolean = false @@ -104,8 +105,16 @@ object Settings: def fail(msg: String, args: List[String]) = ArgsSummary(sstate, args, errors :+ msg, warnings) + def warn(msg: String, args: List[String]) = + ArgsSummary(sstate, args, errors, warnings :+ msg) + def missingArg = - fail(s"missing argument for option $name", args) + val msg = s"missing argument for option $name" + if ignoreInvalidArgs then warn(msg + ", the tag was ignored", args) else fail(msg, args) + + def invalidChoices(invalid: List[String]) = + val msg = s"invalid choice(s) for $name: ${invalid.mkString(",")}" + if ignoreInvalidArgs then warn(msg + ", the tag was ignored", args) else fail(msg, args) def setBoolean(argValue: String, args: List[String]) = if argValue.equalsIgnoreCase("true") || argValue.isEmpty then update(true, args) @@ -144,7 +153,7 @@ object Settings: choices match case Some(valid) => strings.filterNot(valid.contains) match case Nil => update(strings, args) - case invalid => fail(s"invalid choice(s) for $name: ${invalid.mkString(",")}", args) + case invalid => invalidChoices(invalid) case _ => update(strings, args) case (StringTag, _) if argRest.nonEmpty || choices.exists(_.contains("")) => setString(argRest, args) @@ -287,6 +296,9 @@ object Settings: def MultiChoiceHelpSetting(name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil): Setting[List[ChoiceWithHelp[String]]] = publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases)) + def UncompleteMultiChoiceHelpSetting(name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil): Setting[List[ChoiceWithHelp[String]]] = + publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases, ignoreInvalidArgs = true)) + def IntSetting(name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = publish(Setting(name, descr, default, aliases = aliases)) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala new file mode 100644 index 000000000000..ae69c1596009 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -0,0 +1,314 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.Trees.EmptyTree +import dotty.tools.dotc.transform.MegaPhase +import dotty.tools.dotc.transform.MegaPhase.MiniPhase +import dotty.tools.dotc.report +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Flags.* +import dotty.tools.dotc.util.{Property, SrcPos} +import dotty.tools.dotc.core.Symbols.ClassSymbol +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Flags.EmptyFlags +import dotty.tools.dotc.ast.tpd.TreeTraverser +import dotty.tools.dotc.core.Types.watchList +import dotty.tools.dotc.core.Types.NoType +import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.core.Types +import dotty.tools.dotc.semanticdb.TypeOps +import dotty.tools.dotc.cc.boxedCaptureSet +import dotty.tools.dotc.core.Symbols.NoSymbol +import dotty.tools.dotc.transform.SymUtils.isParamOrAccessor +import scala.collection.mutable +import dotty.tools.dotc.core.Scopes.Scope +import scala.collection.immutable.HashMap +import dotty.tools.dotc.core.Symbols +import dotty.tools.dotc.typer.ImportInfo +import dotty.tools.dotc.ast.untpd.ImportSelector +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.ast.untpd +import dotty.tools.dotc.core.Denotations.SingleDenotation +import dotty.tools.dotc.ast.Trees.Ident +import dotty.tools.dotc.core.Names.TypeName +import dotty.tools.dotc.core.Names.TermName +import dotty.tools.dotc.core.Mode.Type +import dotty.tools.dotc.core.Names.SimpleName + +class CheckShadowing extends MiniPhase: + import CheckShadowing.* + import ShadowingData.* + + private val _key = Property.Key[ShadowingData] + + private def shadowingDataApply[U](f: ShadowingData => U)(using Context): Context = + ctx.property(_key).foreach(f) + ctx + + override def phaseName: String = CheckShadowing.name + + override def description: String = CheckShadowing.description + + override def isRunnable(using Context): Boolean = + super.isRunnable && + ctx.settings.Xlint.value.nonEmpty && + !ctx.isJava + + // Setup before the traversal + override def prepareForUnit(tree: tpd.Tree)(using Context): Context = + val data = ShadowingData() + val fresh = ctx.fresh.setProperty(_key, data) + shadowingDataApply(sd => sd.registerRootImports())(using fresh) + + // Reporting on traversal's end + override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = + shadowingDataApply(sd => + reportShadowing(sd.getShadowingResult) + ) + tree + + // MiniPhase traversal : + + override def prepareForPackageDef(tree: tpd.PackageDef)(using Context): Context = + shadowingDataApply(sd => sd.inNewScope()) + ctx + + override def prepareForTemplate(tree: tpd.Template)(using Context): Context = + shadowingDataApply(sd => sd.inNewScope()) + ctx + + override def prepareForBlock(tree: tpd.Block)(using Context): Context = + shadowingDataApply(sd => sd.inNewScope()) + ctx + + override def prepareForOther(tree: tpd.Tree)(using Context): Context = + importTraverser.traverse(tree) + ctx + + override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = + shadowingDataApply(sd => + sd.registerPrivateShadows(tree) + ) + + override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = + if tree.symbol.isAliasType then // if alias, the parent is the current symbol + nestedTypeTraverser(tree.symbol).traverse(tree.rhs) + if tree.symbol.is(Param) then // if param, the parent is up + val owner = tree.symbol.owner + val parent = if (owner.isConstructor) then owner.owner else owner + nestedTypeTraverser(parent).traverse(tree.rhs)(using ctx.outer) + shadowingDataApply(sd => sd.registerCandidate(parent, tree)) + else + ctx + + + override def transformPackageDef(tree: tpd.PackageDef)(using Context): tpd.Tree = + shadowingDataApply(sd => sd.outOfScope()) + tree + + override def transformBlock(tree: tpd.Block)(using Context): tpd.Tree = + shadowingDataApply(sd => sd.outOfScope()) + tree + + override def transformTemplate(tree: tpd.Template)(using Context): tpd.Tree = + shadowingDataApply(sd => sd.outOfScope()) + tree + + override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = + if tree.symbol.is(Param) && isValidTypeParamOwner(tree.symbol.owner) then // Do not register for constructors the work is done for the Class owned equivalent TypeDef + shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol.owner)(using ctx.outer)) + if tree.symbol.isAliasType then // No need to start outer here, because the TypeDef reached here it's already the parent + shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol)(using ctx)) + tree + + // Helpers : + private def isValidTypeParamOwner(owner: Symbol)(using Context): Boolean = + !owner.isConstructor && !owner.is(Synthetic) && !owner.is(Exported) + + private def reportShadowing(res: ShadowingData.ShadowResult)(using Context): Unit = + res.warnings.sortBy(w => (w.pos.line, w.pos.startPos.column))(using Ordering[(Int, Int)]).foreach { + case PrivateShadowWarning(pos, shadow, shadowed) => + report.warning(s"${shadow.showLocated} shadows field ${shadowed.name} inherited from ${shadowed.owner}", pos) + case TypeParamShadowWarning(pos, shadow, parent, shadowed) => + if shadowed.exists then + report.warning(s"Type parameter ${shadow.name} for $parent shadows the type defined by ${shadowed.showLocated}", pos) + else + report.warning(s"Type parameter ${shadow.name} for $parent shadows an explicitly renamed type : ${shadow.name}", pos) + } + + private def nestedTypeTraverser(parent: Symbol) = new TreeTraverser: + import tpd._ + + override def traverse(tree: tpd.Tree)(using Context): Unit = + tree match + case t:tpd.TypeDef => + val newCtx = shadowingDataApply(sd => + sd.registerCandidate(parent, t) + ) + traverseChildren(tree)(using newCtx) + case _ => + traverseChildren(tree) + end traverse + end nestedTypeTraverser + + // To reach the imports during a miniphase traversal + private def importTraverser = new TreeTraverser: + import tpd._ + + override def traverse(tree: tpd.Tree)(using Context): Unit = + tree match + case t:tpd.Import => + val newCtx = shadowingDataApply(sd => sd.registerImport(t)) + traverseChildren(tree)(using newCtx) + case _ => + traverseChildren(tree) + +end CheckShadowing + + +object CheckShadowing: + + val name = "checkShadowing" + val description = "check for elements shadowing other elements in scope" + + private class ShadowingData: + import dotty.tools.dotc.transform.CheckShadowing.ShadowingData._ + import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} + + private val rootImports = MutSet[SingleDenotation]() + private val explicitsImports = MutStack[MutSet[tpd.Import]]() + private val renamedImports = MutStack[MutMap[SimpleName, Name]]() // original name -> renamed name + + private val typeParamCandidates = MutMap[Symbol, Seq[tpd.TypeDef]]().withDefaultValue(Seq()) + private val typeParamShadowWarnings = MutSet[TypeParamShadowWarning]() + + private val privateShadowWarnings = MutSet[PrivateShadowWarning]() + + def inNewScope()(using Context) = + explicitsImports.push(MutSet()) + renamedImports.push(MutMap()) + + def outOfScope()(using Context) = + explicitsImports.pop() + renamedImports.pop() + + /** Register the Root imports (at once per compilation unit)*/ + def registerRootImports()(using Context) = + val langPackageName = ctx.definitions.JavaLangPackageVal.name.toSimpleName // excludes lang package + rootImports.addAll(ctx.definitions.rootImportTypes.withFilter(_.name.toSimpleName != langPackageName).flatMap(_.typeMembers)) + + /* Register an import encountered in the current scope **/ + def registerImport(imp: tpd.Import)(using Context) = + val renamedImps = imp.selectors.collect(sel => { sel.renamed match + case Ident(rename) => + (sel.name.toSimpleName, rename) + }).toMap + explicitsImports.top += imp + renamedImports.top.addAll(renamedImps) + + /** Register a potential type definition which could shadows a Type already defined */ + def registerCandidate(parent: Symbol, typeDef: tpd.TypeDef) = + val actual = typeParamCandidates.getOrElseUpdate(parent, Seq()) + typeParamCandidates.update(parent, actual.+:(typeDef)) + + /** Compute if there is some TypeParam shadowing and register if it is the case */ + def computeTypeParamShadowsFor(parent: Symbol)(using Context): Unit = + typeParamCandidates(parent).foreach(typeDef => { + val sym = typeDef.symbol + val shadowedType = + lookForRootShadowedType(sym) + .orElse(lookForImportedShadowedType(sym)) + .orElse(lookForUnitShadowedType(sym)) + shadowedType.foreach(shadowed => + if !renamedImports.exists(_.contains(shadowed.name.toSimpleName)) then + typeParamShadowWarnings += TypeParamShadowWarning(typeDef.srcPos, typeDef.symbol, parent, shadowed) + ) + }) + + private def lookForRootShadowedType(symbol: Symbol)(using Context): Option[Symbol] = + rootImports.find(p => p.name.toSimpleName == symbol.name.toSimpleName).map(_.symbol) + + private def lookForImportedShadowedType(symbol: Symbol)(using Context): Option[Symbol] = + explicitsImports + .flatMap(_.flatMap(imp => symbol.isAnImportedType(imp))) + .headOption + + private def lookForUnitShadowedType(symbol: Symbol)(using Context): Option[Symbol] = + if !ctx.owner.exists then + None + else + val declarationScope = ctx.effectiveScope + val res = declarationScope.lookup(symbol.name) + res match + case s: Symbol if s.isType => Some(s) + case _ => lookForUnitShadowedType(symbol)(using ctx.outer) + + /** Register if the valDef is a private declaration that shadows an inherited field */ + def registerPrivateShadows(valDef: tpd.ValDef)(using Context): Unit = + lookForShadowedField(valDef.symbol).foreach(shadowedField => + privateShadowWarnings += PrivateShadowWarning(valDef.startPos, valDef.symbol, shadowedField) + ) + + private def lookForShadowedField(symDecl: Symbol)(using Context): Option[Symbol] = + if symDecl.isPrivate then + val symDeclType = symDecl.info + val bClasses = symDecl.owner.info.baseClasses + bClasses match + case _ :: inherited => + inherited + .map(classSymbol => symDecl.denot.matchingDecl(classSymbol, symDeclType)) + .find(sym => sym.name == symDecl.name) + case Nil => + None + else + None + + /** Get the shadowing analysis's result */ + def getShadowingResult(using Context): ShadowResult = + val privateWarnings: List[ShadowWarning] = + if ctx.settings.XlintHas.privateShadow then + privateShadowWarnings.toList + else + Nil + val typeParamWarnings: List[ShadowWarning] = + if ctx.settings.XlintHas.typeParameterShadow then + typeParamShadowWarnings.toList + else + Nil + ShadowResult(privateWarnings ++ typeParamWarnings) + + extension (sym: Symbol) + /** Looks after any type import symbol in the given import that matches this symbol */ + private def isAnImportedType(imp: tpd.Import)(using Context): Option[Symbol] = + val tpd.Import(qual, sels) = imp + val simpleSelections = qual.tpe.member(sym.name).alternatives + val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) + sels + .find(is => is.rename.toSimpleName == sym.name.toSimpleName).map(_.symbol) + .orElse(typeSelections.map(_.symbol).find(sd => sd.name == sym.name)) + .orElse(simpleSelections.map(_.symbol).find(sd => sd.name == sym.name)) + + end ShadowingData + + private object ShadowingData: + sealed abstract class ShadowWarning(val pos: SrcPos, val shadow: Symbol, val shadowed: Symbol) + + case class PrivateShadowWarning( + override val pos: SrcPos, + override val shadow: Symbol, + override val shadowed: Symbol + ) extends ShadowWarning(pos, shadow, shadowed) + + case class TypeParamShadowWarning( + override val pos: SrcPos, + override val shadow: Symbol, + val shadowParent: Symbol, + override val shadowed: Symbol, + ) extends ShadowWarning(pos, shadow, shadowed) + + /** A container for the results of the shadow elements analysis */ + case class ShadowResult(warnings: List[ShadowWarning]) + +end CheckShadowing + diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a3dbbfb202e7..730ef9a32d2e 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -295,9 +295,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke case UnusedSymbol(t, _, WarnTypes.PatVars) => report.warning(s"unused pattern variable", t) case UnusedSymbol(t, _, WarnTypes.UnsetLocals) => - report.warning(s"unset local variable", t) + report.warning(s"unset local variable, consider using an immutable val instead", t) case UnusedSymbol(t, _, WarnTypes.UnsetPrivates) => - report.warning(s"unset private variable", t) + report.warning(s"unset private variable, consider using an immutable val instead", t) } end CheckUnused @@ -645,6 +645,8 @@ object CheckUnused: imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) ) + + extension (tree: ImportSelector) def boundTpe: Type = tree.bound match { case untpd.TypedSplice(tree1) => tree1.tpe @@ -719,8 +721,7 @@ object CheckUnused: /** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ private def isOverriden(using Context): Boolean = - sym.is(Flags.Override) || - (sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) + sym.is(Flags.Override) || (sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) end extension diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c998dbec6721..c794b100bb42 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -33,6 +33,7 @@ class CompilationTests { compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init")), compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/pos-special/sourcepath/outer", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), + compileFilesInDir("tests/pos", defaultOptions.and("-Xlint:private-shadow", "-Xlint:type-parameter-shadow")), compileFile("tests/pos-special/sourcepath/outer/nested/Test4.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), compileFilesInDir("tests/pos-scala2", defaultOptions.and("-source", "3.0-migration")), compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")), diff --git a/tests/neg/i17612a.check b/tests/neg/i17612a.check new file mode 100644 index 000000000000..04a5b07b6e33 --- /dev/null +++ b/tests/neg/i17612a.check @@ -0,0 +1,32 @@ +-- Error: tests/neg/i17612a.scala:18:15 -------------------------------------------------------------------------------- +18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value x in class Derived shadows field x inherited from class Base +-- Error: tests/neg/i17612a.scala:18:24 -------------------------------------------------------------------------------- +18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value y in class Derived shadows field y inherited from class Base +-- Error: tests/neg/i17612a.scala:20:2 --------------------------------------------------------------------------------- +20 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + | ^ + | value shadowed2 in class Derived shadows field shadowed2 inherited from class Base +-- Error: tests/neg/i17612a.scala:21:2 --------------------------------------------------------------------------------- +21 | private[this] val shadowed3 = 3 + 3 // error + | ^ + | value shadowed3 in class Derived shadows field shadowed3 inherited from class Base +-- Error: tests/neg/i17612a.scala:23:2 --------------------------------------------------------------------------------- +23 | private val shadowed5 = 5 + 5 // error + | ^ + | value shadowed5 in class Derived shadows field shadowed5 inherited from class Base +-- Error: tests/neg/i17612a.scala:34:20 -------------------------------------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value x in class UnderDerived shadows field x inherited from class Base +-- Error: tests/neg/i17612a.scala:34:28 -------------------------------------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value y in class UnderDerived shadows field y inherited from class Base +-- Error: tests/neg/i17612a.scala:34:36 -------------------------------------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value z in class UnderDerived shadows field z inherited from class Base diff --git a/tests/neg/i17612a.scala b/tests/neg/i17612a.scala new file mode 100644 index 000000000000..1145cd76edc0 --- /dev/null +++ b/tests/neg/i17612a.scala @@ -0,0 +1,42 @@ +//> using options -Xfatal-warnings -Xlint:private-shadow + +object i17612a: + class Base(var x: Int, val y: Int, var z: Int): + var shadowed2 = 2 + val shadowed3 = 3 + val shadowed4 = 4 + protected var shadowed5 = 5 + //var shadowed6 = 6 + + val notShadowed = -1 + private val notShadowed2 = -2 + //val fatalOverride = 0 + + def increment(): Unit = + x = x + 1 + + class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + private def hello() = 4 + private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + private[this] val shadowed3 = 3 + 3 // error + //private[Derived] val fatalOverride = 0 // value fatalOverride of type Int has weaker access privileges; it should be public + private val shadowed5 = 5 + 5 // error + private val notShadowed2 = -4 + //protected var shadowed6 = 6 + 6 // variable shadowed6 of type Int has weaker access privileges; it should be public + + def inFunctionScope() = + val notShadowed = -2 // OK + -2 + + override def toString = + s"x : ${x.toString}, y : ${y.toString}" + + class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + + def main(args: Array[String]) = + val derived = new Derived(1, 1, 1) + println(derived.toString) // yields x: '1', as expected + derived.increment() + println(derived.toString) // still x: '1', probably unexpected, for y it never prints the super value, less surprising + println(derived.shadowed2) + println(derived.shadowed3) \ No newline at end of file diff --git a/tests/neg/i17612b.check b/tests/neg/i17612b.check new file mode 100644 index 000000000000..75e8b7312833 --- /dev/null +++ b/tests/neg/i17612b.check @@ -0,0 +1,32 @@ +-- Error: tests/neg/i17612b/i17612b.scala:21:15 ------------------------------------------------------------------------ +21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value x in class Derived shadows field x inherited from trait Base +-- Error: tests/neg/i17612b/i17612b.scala:21:33 ------------------------------------------------------------------------ +21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value y in class Derived shadows field y inherited from trait Base +-- Error: tests/neg/i17612b/i17612b.scala:23:2 ------------------------------------------------------------------------- +23 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + | ^ + | value shadowed2 in class Derived shadows field shadowed2 inherited from trait Base +-- Error: tests/neg/i17612b/i17612b.scala:24:2 ------------------------------------------------------------------------- +24 | private[this] val shadowed3 = 3 + 3 // error + | ^ + | value shadowed3 in class Derived shadows field shadowed3 inherited from trait Base +-- Error: tests/neg/i17612b/i17612b.scala:26:2 ------------------------------------------------------------------------- +26 | private val shadowed5 = 5 + 5 // error + | ^ + | value shadowed5 in class Derived shadows field shadowed5 inherited from trait Base +-- Error: tests/neg/i17612b/i17612b.scala:41:20 ------------------------------------------------------------------------ +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value x in class UnderDerived shadows field x inherited from trait Base +-- Error: tests/neg/i17612b/i17612b.scala:41:28 ------------------------------------------------------------------------ +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value y in class UnderDerived shadows field y inherited from trait Base +-- Error: tests/neg/i17612b/i17612b.scala:41:36 ------------------------------------------------------------------------ +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value z in class UnderDerived shadows field z inherited from trait Base diff --git a/tests/neg/i17612b/i17612b.scala b/tests/neg/i17612b/i17612b.scala new file mode 100644 index 000000000000..b59352f562d0 --- /dev/null +++ b/tests/neg/i17612b/i17612b.scala @@ -0,0 +1,44 @@ +//> using options -Xfatal-warnings -Xlint:private-shadow + +object i17612b: + + trait Base(var x: Int, val y: Int, var z: Int): + var shadowed2 = 2 + val shadowed3 = 3 + val shadowed4 = 4 + protected var shadowed5 = 5 + + val notShadowed = -1 + private val notShadowed2 = -2 + val notShadowedbyLambda = -2 + + def increment(): Unit = + x = x + 1 + + trait BaseB + trait BaseC(var x2: Int) + + class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + private def hello() = 4 + private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + private[this] val shadowed3 = 3 + 3 // error + + private val shadowed5 = 5 + 5 // error + private val notShadowed2 = -4 + + val lambda: Int => Int => Int = + notShadowedbyLambda => + notShadowedbyLambda => + notShadowedbyLambda * 2 + + def inFunctionScope() = + val notShadowed = -2 // OK + -2 + + override def toString = + s"x : ${x.toString}, y : ${y.toString}" + + class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + + def main(args: Array[String]) = + val derived = new Derived(1, 1, 1, 1) diff --git a/tests/neg/i17612b/importTry.scala b/tests/neg/i17612b/importTry.scala new file mode 100644 index 000000000000..879f40ace356 --- /dev/null +++ b/tests/neg/i17612b/importTry.scala @@ -0,0 +1,5 @@ +object importTry: + + trait ImTrait + + class ImClass \ No newline at end of file diff --git a/tests/neg/i17613a.check b/tests/neg/i17613a.check new file mode 100644 index 000000000000..4721b786c82d --- /dev/null +++ b/tests/neg/i17613a.check @@ -0,0 +1,28 @@ +-- Error: tests/neg/i17613a.scala:8:13 --------------------------------------------------------------------------------- +8 | def foobar[D](in: D) = in.toString // error method parameter shadows some other type + | ^ + | Type parameter D for method foobar shadows the type defined by trait D in class B +-- Error: tests/neg/i17613a.scala:9:13 --------------------------------------------------------------------------------- +9 | type MySeq[D] = Seq[D] // error type member's parameter shadows some other type + | ^ + | Type parameter D for type MySeq shadows the type defined by trait D in class B +-- Error: tests/neg/i17613a.scala:11:12 -------------------------------------------------------------------------------- +11 | class Foo[T](t: T): // error class parameter shadows some other type + | ^ + | Type parameter T for class Foo shadows the type defined by type T in class B +-- Error: tests/neg/i17613a.scala:12:11 -------------------------------------------------------------------------------- +12 | def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter + | ^ + | Type parameter T for method bar shadows the type defined by type T in class Foo +-- Error: tests/neg/i17613a.scala:15:12 -------------------------------------------------------------------------------- +15 | class C[M[List[_]]] // error + | ^^^^^^^ + | Type parameter List for class C shadows the type defined by type List in package scala +-- Error: tests/neg/i17613a.scala:16:11 -------------------------------------------------------------------------------- +16 | type E[M[List[_]]] = Int // error + | ^^^^^^^ + | Type parameter List for type E shadows the type defined by type List in package scala +-- Error: tests/neg/i17613a.scala:17:14 -------------------------------------------------------------------------------- +17 | def foo[N[M[List[_]]]] = ??? // error + | ^^^^^^^ + | Type parameter List for method foo shadows the type defined by type List in package scala diff --git a/tests/neg/i17613a.scala b/tests/neg/i17613a.scala new file mode 100644 index 000000000000..d0e413098cec --- /dev/null +++ b/tests/neg/i17613a.scala @@ -0,0 +1,23 @@ +//> using options -Xfatal-warnings -Xlint:type-parameter-shadow + +object i17613a: + class B: + type T = Int + trait D + + def foobar[D](in: D) = in.toString // error method parameter shadows some other type + type MySeq[D] = Seq[D] // error type member's parameter shadows some other type + + class Foo[T](t: T): // error class parameter shadows some other type + def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter + + // even deeply nested... + class C[M[List[_]]] // error + type E[M[List[_]]] = Int // error + def foo[N[M[List[_]]]] = ??? // error + + // ...but not between type parameters in the same list + class F[A, M[N[A]]] + type G[A, M[L[A]]] = Int + def bar[A, N[M[L[A]]]] = ??? + def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg/i17613b.check b/tests/neg/i17613b.check new file mode 100644 index 000000000000..d8cf8618fb27 --- /dev/null +++ b/tests/neg/i17613b.check @@ -0,0 +1,56 @@ +-- Error: tests/neg/i17613b/i17613b.scala:9:13 ------------------------------------------------------------------------- +9 | def foobar[ImTrait](in: D) = in.toString // error + | ^^^^^^^ + | Type parameter ImTrait for method foobar shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg/i17613b/i17613b.scala:10:13 ------------------------------------------------------------------------ +10 | type MySeq[ImTrait] = Seq[D] // error + | ^^^^^^^ + | Type parameter ImTrait for type MySeq shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg/i17613b/i17613b.scala:12:14 ------------------------------------------------------------------------ +12 | def foobar2[ImClass](in: D) = in.toString // error + | ^^^^^^^ + | Type parameter ImClass for method foobar2 shadows the type defined by class ImClass in object importTry +-- Error: tests/neg/i17613b/i17613b.scala:13:14 ------------------------------------------------------------------------ +13 | type MySeq2[ImClass] = Seq[D] // error + | ^^^^^^^ + | Type parameter ImClass for type MySeq2 shadows the type defined by class ImClass in object importTry +-- Error: tests/neg/i17613b/i17613b.scala:16:24 ------------------------------------------------------------------------ +16 | type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error + | ^^^^^^^ + | Type parameter ImTrait for type TypeLambda shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg/i17613b/i17613b.scala:17:21 ------------------------------------------------------------------------ +17 | type PolyFun[A] = [ImTrait] => ImTrait => B // error + | ^^^^^^^ + | Type parameter ImTrait for type PolyFun shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg/i17613b/i17613b.scala:23:12 ------------------------------------------------------------------------ +23 | class Foo[T](t: T): // error class parameter shadows some other type + | ^ + | Type parameter T for class Foo shadows the type defined by type T in class B +-- Error: tests/neg/i17613b/i17613b.scala:27:15 ------------------------------------------------------------------------ +27 | def intType[List1](x: T) = x.toString() // error + | ^^^^^ + | Type parameter List1 for method intType shadows an explicitly renamed type : List1 +-- Error: tests/neg/i17613b/i17613b.scala:32:10 ------------------------------------------------------------------------ +32 | given [Int]: Ordering[Int]() // error + | ^^^ + | Type parameter Int for method given_Ordering_Int shadows the type defined by class Int in package scala +-- Error: tests/neg/i17613b/i17613b.scala:34:12 ------------------------------------------------------------------------ +34 | class C[M[List[_]]] // error List not renamed here + | ^^^^^^^ + | Type parameter List for class C shadows the type defined by type List in package scala +-- Error: tests/neg/i17613b/i17613b.scala:35:11 ------------------------------------------------------------------------ +35 | type E[M[Int[_]]] = Int // error + | ^^^^^^ + | Type parameter Int for type E shadows the type defined by class Int in package scala +-- Error: tests/neg/i17613b/i17613b.scala:37:14 ------------------------------------------------------------------------ +37 | def foo[N[M[List[_]]]] = // error + | ^^^^^^^ + | Type parameter List for method foo shadows the type defined by type List in package scala +-- Error: tests/neg/i17613b/i17613b.scala:40:11 ------------------------------------------------------------------------ +40 | type Z[ImClassR] = Int // error + | ^^^^^^^^ + | Type parameter ImClassR for type Z shadows an explicitly renamed type : ImClassR +-- Error: tests/neg/i17613b/i17613b.scala:41:18 ------------------------------------------------------------------------ +41 | class InnerCl[ImClassR] // error + | ^^^^^^^^ + | Type parameter ImClassR for class InnerCl shadows an explicitly renamed type : ImClassR diff --git a/tests/neg/i17613b/i17613b.scala b/tests/neg/i17613b/i17613b.scala new file mode 100644 index 000000000000..b0c4f11b949c --- /dev/null +++ b/tests/neg/i17613b/i17613b.scala @@ -0,0 +1,44 @@ +//> using options -Xfatal-warnings -Xlint:type-parameter-shadow + +object i17613b: + import importTry._ + class B: + type T = Int + trait D + + def foobar[ImTrait](in: D) = in.toString // error + type MySeq[ImTrait] = Seq[D] // error + + def foobar2[ImClass](in: D) = in.toString // error + type MySeq2[ImClass] = Seq[D] // error + + given [A]: Ordering[Int]() + type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error + type PolyFun[A] = [ImTrait] => ImTrait => B // error + type MatchType[A] = A match { + case String => Int + case ImTrait => Boolean + } + + class Foo[T](t: T): // error class parameter shadows some other type + import scala.collection.immutable.{List => List1} + def bar[List](w: T) = w.toString // no warning due to the explicit import renaming + + def intType[List1](x: T) = x.toString() // error + + type Y[List] = Int // no warning + + given [A]: Ordering[A]() + given [Int]: Ordering[Int]() // error + + class C[M[List[_]]] // error List not renamed here + type E[M[Int[_]]] = Int // error + + def foo[N[M[List[_]]]] = // error + import importTry.{ImClass => ImClassR} + def inner[ImClass] = // no warning + type Z[ImClassR] = Int // error + class InnerCl[ImClassR] // error + 5 + + def main(args: Array[String]) = println("Test for type parameter shadow") \ No newline at end of file diff --git a/tests/neg/i17613b/importTry.scala b/tests/neg/i17613b/importTry.scala new file mode 100644 index 000000000000..879f40ace356 --- /dev/null +++ b/tests/neg/i17613b/importTry.scala @@ -0,0 +1,5 @@ +object importTry: + + trait ImTrait + + class ImClass \ No newline at end of file diff --git a/tests/pos/i16339.scala b/tests/pos/i16339.scala index 49924548bb3f..95062f516354 100644 --- a/tests/pos/i16339.scala +++ b/tests/pos/i16339.scala @@ -2,6 +2,7 @@ sealed trait Get[X, +X2 <: X] case class Bar[Y, Y2 <: Y](value: Y2) extends Get[Y, Y2] + class Test: def t1[Z, Z2 <: Z](get: Get[Z, Z2]) = get match case Bar(_) =>