diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/RefChecks.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 46 |
1 files changed, 36 insertions, 10 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index c462b8f8ef..41f54f84e4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -144,7 +144,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // This has become noisy with implicit classes. if (settings.lint && settings.developer) { clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym => - val alts = clazz.info.decl(sym.name).alternatives + // implicit classes leave both a module symbol and a method symbol as residue + val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule) if (alts.size > 1) alts foreach (x => unit.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds")) } @@ -965,12 +966,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val s = fn.symbol (s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=) } + def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual) + // Whether the operands+operator represent a warnable combo (assuming anyrefs) // Looking for comparisons performed with ==/!= in combination with either an // equals method inherited from Object or a case class synthetic equals (for // which we know the logic.) def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info) + def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) @@ -982,6 +986,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // used to short-circuit unrelatedTypes check if both sides are special def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) val nullCount = onSyms(_ filter (_ == NullClass) size) + def isNonsenseValueClassCompare = ( + !haveSubclassRelationship + && isUsingDefaultScalaOp + && isEitherValueClass + && !isCaseEquals + ) def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) @@ -993,10 +1003,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def nonSensiblyNeq() = nonSensible("", false) def nonSensiblyNew() = nonSensibleWarning("a fresh object", false) + def unrelatedMsg = name match { + case nme.EQ | nme.eq => "never compare equal" + case _ => "always compare unequal" + } def unrelatedTypes() = { - val msg = if (name == nme.EQ || name == nme.eq) - "never compare equal" else "always compare unequal" - unit.warning(pos, typesString + " are unrelated: they will most likely " + msg) + val weaselWord = if (isEitherValueClass) "" else " most likely" + unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") } if (nullCount == 2) // null == null @@ -1035,15 +1048,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } + // warn if one but not the other is a derived value class + // this is especially important to enable transitioning from + // regular to value classes without silent failures. + if (isNonsenseValueClassCompare) + unrelatedTypes() // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean - if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { + else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { // better to have lubbed and lost def warnIfLubless(): Unit = { val common = global.lub(List(actual.tpe, receiver.tpe)) if (ObjectClass.tpe <:< common) unrelatedTypes() } - def eitherSubclasses = (actual isSubClass receiver) || (receiver isSubClass actual) // warn if actual has a case parent that is not same as receiver's; // if actual is not a case, then warn if no common supertype, as below if (isCaseEquals) { @@ -1056,14 +1073,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans //else // if a class, it must be super to thisCase (and receiver) since not <: thisCase if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq() - else if (!eitherSubclasses) warnIfLubless() + else if (!haveSubclassRelationship) warnIfLubless() case _ => } } - else if (actual isSubClass receiver) () - else if (receiver isSubClass actual) () // warn only if they have no common supertype below Object - else { + else if (!haveSubclassRelationship) { warnIfLubless() } } @@ -1261,6 +1276,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } + private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = { + def isLikelyUninitialized = ( + (sym.owner isSubClass DelayedInitClass) + && !qual.tpe.isInstanceOf[ThisType] + && sym.accessedOrSelf.isVal + ) + if (settings.lint.value && isLikelyUninitialized) + unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value") + } + private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = ( (otherSym != NoSymbol) && !otherSym.isProtected @@ -1464,6 +1489,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if(settings.Xmigration.value != NoScalaVersion) checkMigration(sym, tree.pos) checkCompileTimeOnly(sym, tree.pos) + checkDelayedInitSelect(qual, sym, tree.pos) if (sym eq NoSymbol) devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe) |