diff options
author | Antoine Gourlay <antoine@gourlay.fr> | 2014-07-31 17:52:13 +0200 |
---|---|---|
committer | Antoine Gourlay <antoine@gourlay.fr> | 2014-08-12 15:47:20 +0200 |
commit | 756e551b30461b548ea59e99562634775b7ebd74 (patch) | |
tree | 678fd556b17768d1cf533135bbfd1741df025d8d /src | |
parent | 84d4ebc19a1e54dbe446ef35b71efa7ad3890c19 (diff) | |
download | scala-756e551b30461b548ea59e99562634775b7ebd74.tar.gz scala-756e551b30461b548ea59e99562634775b7ebd74.tar.bz2 scala-756e551b30461b548ea59e99562634775b7ebd74.zip |
SI-5691 lint warning when a type parameter shadows an existing type.
This adds a new lint warning for when a class/method/type-member's
type parameter shadows an existing type: `-Xlint:type-parameter-shadow`.
It excludes type parameters of synthetic methods (the user can't
rename or remove those anyway), otherwise, for example, every case class
triggers the warning.
Also fixes a test that contained wrong java sources (that didn't even
compile...), discovered thanks to the warning.
---
This kind of errors shows up every now and then on the mailing-list, on
stackoverflow, etc. so maybe a warning would be useful.
I was afraid this would yield too many warnings for libraries that are
heavy on type parameters, but no: running this on scalaz and shapeless
HEAD (`v7.1.0-RC1-41-g1cc0a96` and `v2.0.0-M1-225-g78426a0` respectively)
yields 44 warnings. None of them are false positives; they usually come
from:
- scalaz loving using `A` as type parameter, even several levels deep
of parametrized classes/methods
- or calling a type parameter that will hold a map `Map`, or similar,
thus shadowing an existing type
Diffstat (limited to 'src')
3 files changed, 26 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index bec068b56a..4c37633301 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -38,6 +38,7 @@ trait Warnings { warnMissingInterpolator, warnDocDetached, warnPrivateShadow, + warnTypeParameterShadow, warnPolyImplicitOverload, warnOptionImplicit, warnDelayedInit, @@ -83,6 +84,8 @@ trait Warnings { "A ScalaDoc comment appears to be detached from its element.") val warnPrivateShadow = lintflag("private-shadow", "A private field (or class parameter) shadows a superclass field.") + val warnTypeParameterShadow = lintflag("type-parameter-shadow", + "A local type parameter shadows a type already in scope.") val warnPolyImplicitOverload = lintflag("poly-implicit-overload", "Parameterized overloaded implicit methods are not visible as view bounds") val warnOptionImplicit = lintflag("option-implicit", diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 7440f69e93..d18348f51e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -600,6 +600,23 @@ trait TypeDiagnostics { ) } + // warn about class/method/type-members' type parameters that shadow types already in scope + def warnTypeParameterShadow(tparams: List[TypeDef], sym: Symbol): Unit = + if (settings.warnTypeParameterShadow && !isPastTyper && !sym.isSynthetic) { + def enclClassOrMethodOrTypeMember(c: Context): Context = + if (!c.owner.exists || c.owner.isClass || c.owner.isMethod || (c.owner.isType && !c.owner.isParameter)) c + else enclClassOrMethodOrTypeMember(c.outer) + + val tt = tparams.filter(_.name != typeNames.WILDCARD).foreach { tp => + // we don't care about type params shadowing other type params in the same declaration + enclClassOrMethodOrTypeMember(context).outer.lookupSymbol(tp.name, s => s != tp.symbol && s.hasRawInfo && reallyExists(s)) match { + case LookupSucceeded(_, sym2) => context.warning(tp.pos, + s"type parameter ${tp.name} defined in $sym shadows $sym2 defined in ${sym2.owner}. You may want to rename your type parameter, or possibly remove it.") + case _ => + } + } + } + /** Report a type error. * * @param pos The position where to report the error diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 70f44c4fc6..31a15063bd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1749,6 +1749,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper |you want, you must write the annotation class in Java.""".stripMargin) } + warnTypeParameterShadow(tparams1, clazz) + if (!isPastTyper) { for (ann <- clazz.getAnnotation(DeprecatedAttr)) { val m = companionSymbolOf(clazz, context) @@ -2151,6 +2153,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val tparams1 = ddef.tparams mapConserve typedTypeDef val vparamss1 = ddef.vparamss mapConserve (_ mapConserve typedValDef) + warnTypeParameterShadow(tparams1, meth) + meth.annotations.map(_.completeInfo()) for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1) @@ -2227,6 +2231,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val typedMods = typedModifiers(tdef.mods) tdef.symbol.annotations.map(_.completeInfo()) + warnTypeParameterShadow(tparams1, tdef.symbol) + // @specialized should not be pickled when compiling with -no-specialize if (settings.nospecialization && currentRun.compiles(tdef.symbol)) { tdef.symbol.removeAnnotation(definitions.SpecializedClass) |