diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/RefChecks.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 88 |
1 files changed, 48 insertions, 40 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 03da1a4f09..c0510dd33d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -777,51 +777,59 @@ abstract class RefChecks extends InfoTransform { } def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match { - case Select(qual, name) if (args.length == 1) => + case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 => + def isReferenceOp = name == nme.eq || name == nme.ne def isNew(tree: Tree) = tree match { case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true case _ => false } - name match { - case nme.EQ | nme.NE | nme.LT | nme.GT | nme.LE | nme.GE => - def underlyingClass(tp: Type): Symbol = { - var sym = tp.widen.typeSymbol - while (sym.isAbstractType) - sym = sym.info.bounds.hi.widen.typeSymbol - sym - } - val formal = underlyingClass(fn.tpe.params.head.tpe) - val actual = underlyingClass(args.head.tpe) - val receiver = underlyingClass(qual.tpe) - def nonSensibleWarning(what: String, alwaysEqual: Boolean) = - unit.warning(pos, "comparing "+what+" using `"+name.decode+"' will always yield "+ - (alwaysEqual == (name == nme.EQ || name == nme.LE || name == nme.GE))) - def nonSensible(pre: String, alwaysEqual: Boolean) = - nonSensibleWarning(pre+"values of types "+normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen), - alwaysEqual) // @MAT normalize for consistency in error message, otherwise part is normalized due to use of `typeSymbol', but the rest isn't - def hasObjectEquals = receiver.info.member(nme.equals_) == Object_equals - // see if it has any == methods beyond Any's and AnyRef's: ticket #2240 - def hasOverloadedEqEq = receiver.info.member(nme.EQ).alternatives.length > 2 - - if (formal == UnitClass && actual == UnitClass) - nonSensible("", true) - else if ((receiver == BooleanClass || receiver == UnitClass) && - !(receiver isSubClass actual)) - nonSensible("", false) - else if (isNumericValueClass(receiver) && - !isNumericValueClass(actual) && - !(forMSIL || (actual isSubClass BoxedNumberClass)) && - !(receiver isSubClass actual)) - nonSensible("", false) - else if ((receiver hasFlag FINAL) && hasObjectEquals && !isValueClass(receiver) && - !(receiver isSubClass actual) && receiver != NullClass && actual != NullClass && - (name == nme.EQ || name == nme.LE)) - nonSensible("non-null ", false) - else if ((isNew(qual) || isNew(args.head)) && hasObjectEquals && - (name == nme.EQ || name == nme.NE) && !hasOverloadedEqEq) - nonSensibleWarning("a fresh object", false) - case _ => + def underlyingClass(tp: Type): Symbol = { + var sym = tp.widen.typeSymbol + while (sym.isAbstractType) + sym = sym.info.bounds.hi.widen.typeSymbol + sym + } + val actual = underlyingClass(args.head.tpe) + val receiver = underlyingClass(qual.tpe) + + // Whether def equals(other: Any) is overridden + def isUsingDefaultEquals = { + val m = receiver.info.member(nme.equals_) + (m == Object_equals) || (m == Any_equals) + } + // Whether this == or != is actually an overloaded version + def isUsingDefaultScalaOp = { + val s = fn.symbol + (s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=) || (s == Object_eq) || (s == Object_ne) + } + // Whether the operands+operator represent a warnable combo (assuming anyrefs) + def isWarnable = isReferenceOp || (isUsingDefaultEquals && isUsingDefaultScalaOp) + def isValueOrBoxed(s: Symbol) = isNumericValueClass(s) || (s isSubClass BoxedNumberClass) + def isEitherNull = (receiver == NullClass) || (actual == NullClass) + def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info) + + def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { + val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) + unit.warning(pos, "comparing "+what+" using `"+name.decode+"' will always yield " + msg) + } + + // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol' + def nonSensible(pre: String, alwaysEqual: Boolean) = + nonSensibleWarning(pre+"values of types "+normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen), + alwaysEqual) + + if (receiver == BooleanClass && !(receiver isSubClass actual)) // true == 5 + nonSensible("", false) + else if (receiver == UnitClass && actual == UnitClass) // () == () + nonSensible("", true) + else if (isNumericValueClass(receiver) && !isValueOrBoxed(actual) && !forMSIL) // 5 == "abc" + nonSensible("", false) + else if (isWarnable) { + if (receiver.isFinal && !isEitherNull && !(receiver isSubClass actual)) // object X, Y; X == Y + nonSensible((if (isEitherNullable) "non-null " else ""), false) + else if (isNew(qual) || (isNew(args.head) && (receiver.isFinal || isReferenceOp))) // new X == y or object X ; X == new Y + nonSensibleWarning("a fresh object", false) } case _ => } |