From 2c408fddc99d00e67818ab44ebb5ac5fd9a20f2b Mon Sep 17 00:00:00 2001 From: Carl Date: Mon, 8 May 2023 21:20:02 +0200 Subject: [PATCH] Implement -Xlint:private-shadow, type-parameter-shadow Respectively warn about : - a private field or a class parameter that shadows a superclass field - a local type parameter that shadows a type already in the scope Fixes : #17612 and #17613 --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../dotty/tools/dotc/config/CliCommand.scala | 2 +- .../tools/dotc/config/ScalaSettings.scala | 26 +- .../tools/dotc/transform/CheckShadowing.scala | 314 ++++++++++++++++++ .../fatal-warnings/i17612a.check | 32 ++ .../fatal-warnings/i17612a.scala | 42 +++ .../fatal-warnings/i17613a.check | 28 ++ .../fatal-warnings/i17613a.scala | 23 ++ .../fatal-warnings/i17613b.check | 44 +++ .../fatal-warnings/i17613b/i17613b.scala | 33 ++ .../fatal-warnings/i17613b/importTry.scala | 5 + 11 files changed, 548 insertions(+), 3 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17612a.check create mode 100644 tests/neg-custom-args/fatal-warnings/i17612a.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17613a.check create mode 100644 tests/neg-custom-args/fatal-warnings/i17613a.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17613b.check create mode 100644 tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a6118732d4ae..743aca5bf90a 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,7 @@ 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, new CheckShadowing) :: // Check for unused and 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/CliCommand.scala b/compiler/src/dotty/tools/dotc/config/CliCommand.scala index 914df040fbf7..de14968cb50b 100644 --- a/compiler/src/dotty/tools/dotc/config/CliCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CliCommand.scala @@ -86,7 +86,7 @@ trait CliCommand: protected def isVerbose(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = s.name.startsWith("-V") && s.name != "-V" protected def isWarning(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = - s.name.startsWith("-W") && s.name != "-W" || s.name == "-Xlint" + s.name.startsWith("-W") && s.name != "-W" || s.name.startsWith("-Xlint") protected def isAdvanced(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = s.name.startsWith("-X") && s.name != "-X" protected def isPrivate(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 0ddde23dc39f..c144364f68ba 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._ @@ -155,7 +156,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")) @@ -306,6 +306,30 @@ 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]]] = MultiChoiceHelpSetting( + name = "-Xlint", + helpArg = "advanced warning", + descr = "Enable or disable specific `lint` warnings", + choices = List( + ChoiceWithHelp("nowarn", ""), + 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 isChoiceSet(s: String)(using Context) = Xlint.value.pipe(us => us.contains(s)) + def allOr(s: String)(using Context) = Xlint.value.pipe(us => us.contains("all") || us.contains(s)) + def nowarn(using Context) = allOr("nowarn") + + 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/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala new file mode 100644 index 000000000000..1c575fdc89a1 --- /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(tree.symbol).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) && !tree.symbol.owner.isConstructor 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 reportShadowing(res: ShadowingData.ShadowResult)(using Context): Unit = + res.warnings.sortBy(w => (w.pos.line, w.pos.startPos.column))(using Ordering[(Int, Int)]).foreach { s => + s match + 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(parent: Symbol) = new TreeTraverser: + import tpd._ + + override def traverse(tree: tpd.Tree)(using Context): Unit = + tree match + case t:tpd.Import => + shadowingDataApply(sd => sd.registerImport(t)) + traverseChildren(tree) + 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 shadowedTypeDefs = MutSet[TypeParamShadowWarning]() + + private val shadowedPrivateDefs = 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) = + ctx.definitions.rootImportTypes.foreach(rimp => println()) + 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 + shadowedTypeDefs += 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.isInImport(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 => + shadowedPrivateDefs += 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 privateShadowWarnings: List[ShadowWarning] = + if ctx.settings.XlintHas.privateShadow then + shadowedPrivateDefs.toList + else + Nil + val typeParamShadowWarnings: List[ShadowWarning] = + if ctx.settings.XlintHas.typeParameterShadow then + shadowedTypeDefs.toList + else + Nil + ShadowResult(privateShadowWarnings ++ typeParamShadowWarnings) + + extension (sym: Symbol) + /** Given an import and accessibility, return the import's symbol that matches import<->this symbol */ + private def isInImport(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 \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i17612a.check b/tests/neg-custom-args/fatal-warnings/i17612a.check new file mode 100644 index 000000000000..fad897b7c5f8 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17612a.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/i17612a.scala b/tests/neg-custom-args/fatal-warnings/i17612a.scala new file mode 100644 index 000000000000..0fb6306b96cb --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17612a.scala @@ -0,0 +1,42 @@ +// scalac: -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-custom-args/fatal-warnings/i17613a.check b/tests/neg-custom-args/fatal-warnings/i17613a.check new file mode 100644 index 000000000000..b0aeb85101a1 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613a.check @@ -0,0 +1,28 @@ +-- Error: tests/neg-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/i17613a.scala b/tests/neg-custom-args/fatal-warnings/i17613a.scala new file mode 100644 index 000000000000..4639bd4b5053 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613a.scala @@ -0,0 +1,23 @@ +// scalac: -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-custom-args/fatal-warnings/i17613b.check b/tests/neg-custom-args/fatal-warnings/i17613b.check new file mode 100644 index 000000000000..ed8ed45e42eb --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613b.check @@ -0,0 +1,44 @@ +-- Error: tests/neg-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/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-custom-args/fatal-warnings/i17613b/i17613b.scala:15:12 --------------------------------------------- +15 | 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-custom-args/fatal-warnings/i17613b/i17613b.scala:19:15 --------------------------------------------- +19 | def intType[List1](x: T) = x.toString() // error + | ^^^^^ + | Type parameter List1 for method intType shadows an explicitly renamed type : List1 +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:23:12 --------------------------------------------- +23 | 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-custom-args/fatal-warnings/i17613b/i17613b.scala:24:11 --------------------------------------------- +24 | 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-custom-args/fatal-warnings/i17613b/i17613b.scala:26:14 --------------------------------------------- +26 | 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-custom-args/fatal-warnings/i17613b/i17613b.scala:29:11 --------------------------------------------- +29 | type Z[ImClassR] = Int // error + | ^^^^^^^^ + | Type parameter ImClassR for type Z shadows an explicitly renamed type : ImClassR +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:30:18 --------------------------------------------- +30 | class InnerCl[ImClassR] // error + | ^^^^^^^^ + | Type parameter ImClassR for class InnerCl shadows an explicitly renamed type : ImClassR diff --git a/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala b/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala new file mode 100644 index 000000000000..d2c1f334dd31 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala @@ -0,0 +1,33 @@ +// scalac: -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 + + 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 + + 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") diff --git a/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala b/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala new file mode 100644 index 000000000000..879f40ace356 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala @@ -0,0 +1,5 @@ +object importTry: + + trait ImTrait + + class ImClass \ No newline at end of file