@@ -954,162 +954,166 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
954
954
case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true
955
955
case _ => false
956
956
}
957
- def checkSensible (pos : Position , fn : Tree , args : List [Tree ]) = fn match {
958
- case Select (qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) =>
959
- // Make sure the 'eq' or 'ne' method is the one in AnyRef.
960
- def isReferenceOp = fn.symbol == Object_eq || fn.symbol == Object_ne
961
- def isNew (tree : Tree ) = tree match {
962
- case Function (_, _)
963
- | Apply (Select (New (_), nme.CONSTRUCTOR ), _) => true
964
- case _ => false
965
- }
966
- def underlyingClass (tp : Type ): Symbol = {
967
- val sym = tp.widen.typeSymbol
968
- if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi)
969
- else sym
970
- }
971
- val actual = underlyingClass(args.head.tpe)
972
- val receiver = underlyingClass(qual.tpe)
973
- def onTrees [T ](f : List [Tree ] => T ) = f(List (qual, args.head))
974
- def onSyms [T ](f : List [Symbol ] => T ) = f(List (receiver, actual))
975
-
976
- // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol`
977
- def typesString = normalizeAll(qual.tpe.widen)+ " and " + normalizeAll(args.head.tpe.widen)
978
-
979
- /* Symbols which limit the warnings we can issue since they may be value types */
980
- val isMaybeValue = Set [Symbol ](AnyClass , AnyRefClass , AnyValClass , ObjectClass , ComparableClass , JavaSerializableClass )
981
-
982
- // Whether def equals(other: Any) has known behavior: it is the default
983
- // inherited from java.lang.Object, or it is a synthetically generated
984
- // case equals. TODO - more cases are warnable if the target is a synthetic
985
- // equals.
986
- def isUsingWarnableEquals = {
987
- val m = receiver.info.member(nme.equals_)
988
- ((m == Object_equals ) || (m == Any_equals ) || isMethodCaseEquals(m))
989
- }
990
- def isMethodCaseEquals (m : Symbol ) = m.isSynthetic && m.owner.isCase
991
- def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_))
992
- // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere.
993
- def isUsingDefaultScalaOp = {
994
- val s = fn.symbol
995
- (s == Object_== ) || (s == Object_!= ) || (s == Any_== ) || (s == Any_!= )
996
- }
997
- def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual)
998
-
999
- // Whether the operands+operator represent a warnable combo (assuming anyrefs)
1000
- // Looking for comparisons performed with ==/!= in combination with either an
1001
- // equals method inherited from Object or a case class synthetic equals (for
1002
- // which we know the logic.)
1003
- def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals)
1004
- def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info)
1005
- def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass
1006
- def isBoolean (s : Symbol ) = unboxedValueClass(s) == BooleanClass
1007
- def isUnit (s : Symbol ) = unboxedValueClass(s) == UnitClass
1008
- def isNumeric (s : Symbol ) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s)
1009
- def isScalaNumber (s : Symbol ) = s isSubClass ScalaNumberClass
1010
- def isJavaNumber (s : Symbol ) = s isSubClass JavaNumberClass
1011
- // includes java.lang.Number if appropriate [SI-5779]
1012
- def isAnyNumber (s : Symbol ) = isScalaNumber(s) || isJavaNumber(s)
1013
- def isMaybeAnyValue (s : Symbol ) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s)
1014
- // used to short-circuit unrelatedTypes check if both sides are special
1015
- def isSpecial (s : Symbol ) = isMaybeAnyValue(s) || isAnyNumber(s)
1016
- val nullCount = onSyms(_ filter (_ == NullClass ) size)
1017
- def isNonsenseValueClassCompare = (
1018
- ! haveSubclassRelationship
1019
- && isUsingDefaultScalaOp
1020
- && isEitherValueClass
1021
- && ! isCaseEquals
1022
- )
957
+ /** Check the sensibility of using the given `equals` to compare `qual` and `other`. */
958
+ private def checkSensibleEquals (pos : Position , qual : Tree , name : Name , sym : Symbol , other : Tree ) = {
959
+ def isReferenceOp = sym == Object_eq || sym == Object_ne
960
+ def isNew (tree : Tree ) = tree match {
961
+ case Function (_, _) | Apply (Select (New (_), nme.CONSTRUCTOR ), _) => true
962
+ case _ => false
963
+ }
964
+ def underlyingClass (tp : Type ): Symbol = {
965
+ val sym = tp.widen.typeSymbol
966
+ if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi)
967
+ else sym
968
+ }
969
+ val actual = underlyingClass(other.tpe)
970
+ val receiver = underlyingClass(qual.tpe)
971
+ def onTrees [T ](f : List [Tree ] => T ) = f(List (qual, other))
972
+ def onSyms [T ](f : List [Symbol ] => T ) = f(List (receiver, actual))
973
+
974
+ // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol`
975
+ def typesString = normalizeAll(qual.tpe.widen)+ " and " + normalizeAll(other.tpe.widen)
976
+
977
+ /* Symbols which limit the warnings we can issue since they may be value types */
978
+ val isMaybeValue = Set [Symbol ](AnyClass , AnyRefClass , AnyValClass , ObjectClass , ComparableClass , JavaSerializableClass )
979
+
980
+ // Whether def equals(other: Any) has known behavior: it is the default
981
+ // inherited from java.lang.Object, or it is a synthetically generated
982
+ // case equals. TODO - more cases are warnable if the target is a synthetic
983
+ // equals.
984
+ def isUsingWarnableEquals = {
985
+ val m = receiver.info.member(nme.equals_)
986
+ ((m == Object_equals ) || (m == Any_equals ) || isMethodCaseEquals(m))
987
+ }
988
+ def isMethodCaseEquals (m : Symbol ) = m.isSynthetic && m.owner.isCase
989
+ def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_))
990
+ // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere.
991
+ def isUsingDefaultScalaOp = sym == Object_== || sym == Object_!= || sym == Any_== || sym == Any_!=
992
+ def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual)
993
+
994
+ // Whether the operands+operator represent a warnable combo (assuming anyrefs)
995
+ // Looking for comparisons performed with ==/!= in combination with either an
996
+ // equals method inherited from Object or a case class synthetic equals (for
997
+ // which we know the logic.)
998
+ def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals)
999
+ def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info)
1000
+ def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass
1001
+ def isBoolean (s : Symbol ) = unboxedValueClass(s) == BooleanClass
1002
+ def isUnit (s : Symbol ) = unboxedValueClass(s) == UnitClass
1003
+ def isNumeric (s : Symbol ) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s)
1004
+ def isScalaNumber (s : Symbol ) = s isSubClass ScalaNumberClass
1005
+ def isJavaNumber (s : Symbol ) = s isSubClass JavaNumberClass
1006
+ // includes java.lang.Number if appropriate [SI-5779]
1007
+ def isAnyNumber (s : Symbol ) = isScalaNumber(s) || isJavaNumber(s)
1008
+ def isMaybeAnyValue (s : Symbol ) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s)
1009
+ // used to short-circuit unrelatedTypes check if both sides are special
1010
+ def isSpecial (s : Symbol ) = isMaybeAnyValue(s) || isAnyNumber(s)
1011
+ val nullCount = onSyms(_ filter (_ == NullClass ) size)
1012
+ def isNonsenseValueClassCompare = (
1013
+ ! haveSubclassRelationship
1014
+ && isUsingDefaultScalaOp
1015
+ && isEitherValueClass
1016
+ && ! isCaseEquals
1017
+ )
1023
1018
1024
- def nonSensibleWarning (what : String , alwaysEqual : Boolean ) = {
1025
- val msg = alwaysEqual == (name == nme.EQ || name == nme.eq)
1026
- unit.warning(pos, " comparing " + what+ " using `" + name.decode+ " ' will always yield " + msg)
1027
- }
1028
- def nonSensible (pre : String , alwaysEqual : Boolean ) =
1029
- nonSensibleWarning(pre+ " values of types " + typesString, alwaysEqual)
1030
- def nonSensiblyEq () = nonSensible(" " , true )
1031
- def nonSensiblyNeq () = nonSensible(" " , false )
1032
- def nonSensiblyNew () = nonSensibleWarning(" a fresh object" , false )
1033
-
1034
- def unrelatedMsg = name match {
1035
- case nme.EQ | nme.eq => " never compare equal"
1036
- case _ => " always compare unequal"
1037
- }
1038
- def unrelatedTypes () = {
1039
- val weaselWord = if (isEitherValueClass) " " else " most likely"
1040
- unit.warning(pos, s " $typesString are unrelated: they will $weaselWord $unrelatedMsg" )
1041
- }
1019
+ // Have we already determined that the comparison is non-sensible? I mean, non-sensical?
1020
+ var isNonSensible = false
1021
+
1022
+ def nonSensibleWarning (what : String , alwaysEqual : Boolean ) = {
1023
+ val msg = alwaysEqual == (name == nme.EQ || name == nme.eq)
1024
+ unit.warning(pos, s " comparing $what using ` ${name.decode}' will always yield $msg" )
1025
+ isNonSensible = true
1026
+ }
1027
+ def nonSensible (pre : String , alwaysEqual : Boolean ) =
1028
+ nonSensibleWarning(s " ${pre}values of types $typesString" , alwaysEqual)
1029
+ def nonSensiblyEq () = nonSensible(" " , alwaysEqual = true )
1030
+ def nonSensiblyNeq () = nonSensible(" " , alwaysEqual = false )
1031
+ def nonSensiblyNew () = nonSensibleWarning(" a fresh object" , alwaysEqual = false )
1032
+
1033
+ def unrelatedMsg = name match {
1034
+ case nme.EQ | nme.eq => " never compare equal"
1035
+ case _ => " always compare unequal"
1036
+ }
1037
+ def unrelatedTypes () = if (! isNonSensible) {
1038
+ val weaselWord = if (isEitherValueClass) " " else " most likely"
1039
+ unit.warning(pos, s " $typesString are unrelated: they will $weaselWord $unrelatedMsg" )
1040
+ }
1042
1041
1043
- if (nullCount == 2 ) // null == null
1042
+ if (nullCount == 2 ) // null == null
1043
+ nonSensiblyEq()
1044
+ else if (nullCount == 1 ) {
1045
+ if (onSyms(_ exists isPrimitiveValueClass)) // null == 5
1046
+ nonSensiblyNeq()
1047
+ else if (onTrees( _ exists isNew)) // null == new AnyRef
1048
+ nonSensiblyNew()
1049
+ }
1050
+ else if (isBoolean(receiver)) {
1051
+ if (! isBoolean(actual) && ! isMaybeValue(actual)) // true == 5
1052
+ nonSensiblyNeq()
1053
+ }
1054
+ else if (isUnit(receiver)) {
1055
+ if (isUnit(actual)) // () == ()
1044
1056
nonSensiblyEq()
1045
- else if (nullCount == 1 ) {
1046
- if (onSyms(_ exists isPrimitiveValueClass)) // null == 5
1047
- nonSensiblyNeq()
1048
- else if (onTrees( _ exists isNew)) // null == new AnyRef
1049
- nonSensiblyNew()
1050
- }
1051
- else if (isBoolean(receiver)) {
1052
- if (! isBoolean(actual) && ! isMaybeValue(actual)) // true == 5
1057
+ else if (! isUnit(actual) && ! isMaybeValue(actual)) // () == "abc"
1058
+ nonSensiblyNeq()
1059
+ }
1060
+ else if (isNumeric(receiver)) {
1061
+ if (! isNumeric(actual))
1062
+ if (isUnit(actual) || isBoolean(actual) || ! isMaybeValue(actual)) // 5 == "abc"
1053
1063
nonSensiblyNeq()
1054
- }
1055
- else if (isUnit(receiver)) {
1056
- if (isUnit(actual)) // () == ()
1057
- nonSensiblyEq()
1058
- else if (! isUnit(actual) && ! isMaybeValue(actual)) // () == "abc"
1064
+ }
1065
+ else if (isWarnable && ! isCaseEquals) {
1066
+ if (isNew(qual)) // new X == y
1067
+ nonSensiblyNew()
1068
+ else if (isNew(other) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y
1069
+ nonSensiblyNew()
1070
+ else if (receiver.isEffectivelyFinal && ! (receiver isSubClass actual) && ! actual.isRefinementClass) { // object X, Y; X == Y
1071
+ if (isEitherNullable)
1072
+ nonSensible(" non-null " , false )
1073
+ else
1059
1074
nonSensiblyNeq()
1060
1075
}
1061
- else if (isNumeric(receiver)) {
1062
- if (! isNumeric(actual))
1063
- if (isUnit(actual) || isBoolean(actual) || ! isMaybeValue(actual)) // 5 == "abc"
1064
- nonSensiblyNeq()
1076
+ }
1077
+
1078
+ // warn if one but not the other is a derived value class
1079
+ // this is especially important to enable transitioning from
1080
+ // regular to value classes without silent failures.
1081
+ if (isNonsenseValueClassCompare)
1082
+ unrelatedTypes()
1083
+ // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean
1084
+ else if (isWarnable && nullCount == 0 && ! (isSpecial(receiver) && isSpecial(actual))) {
1085
+ // better to have lubbed and lost
1086
+ def warnIfLubless (): Unit = {
1087
+ val common = global.lub(List (actual.tpe, receiver.tpe))
1088
+ if (ObjectTpe <:< common)
1089
+ unrelatedTypes()
1065
1090
}
1066
- else if (isWarnable && ! isCaseEquals) {
1067
- if (isNew(qual)) // new X == y
1068
- nonSensiblyNew()
1069
- else if (isNew(args.head) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y
1070
- nonSensiblyNew()
1071
- else if (receiver.isEffectivelyFinal && ! (receiver isSubClass actual) && ! actual.isRefinementClass) { // object X, Y; X == Y
1072
- if (isEitherNullable)
1073
- nonSensible(" non-null " , false )
1074
- else
1075
- nonSensiblyNeq()
1091
+ // warn if actual has a case parent that is not same as receiver's;
1092
+ // if actual is not a case, then warn if no common supertype, as below
1093
+ if (isCaseEquals) {
1094
+ def thisCase = receiver.info.member(nme.equals_).owner
1095
+ actual.info.baseClasses.find(_.isCase) match {
1096
+ case Some (p) if p != thisCase => nonSensible(" case class " , false )
1097
+ case None =>
1098
+ // stronger message on (Some(1) == None)
1099
+ // if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq()
1100
+ // else
1101
+ // if a class, it must be super to thisCase (and receiver) since not <: thisCase
1102
+ if (! actual.isTrait && ! (receiver isSubClass actual)) nonSensiblyNeq()
1103
+ else if (! haveSubclassRelationship) warnIfLubless()
1104
+ case _ =>
1076
1105
}
1077
1106
}
1078
-
1079
- // warn if one but not the other is a derived value class
1080
- // this is especially important to enable transitioning from
1081
- // regular to value classes without silent failures.
1082
- if (isNonsenseValueClassCompare)
1083
- unrelatedTypes()
1084
- // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean
1085
- else if (isWarnable && nullCount == 0 && ! (isSpecial(receiver) && isSpecial(actual))) {
1086
- // better to have lubbed and lost
1087
- def warnIfLubless (): Unit = {
1088
- val common = global.lub(List (actual.tpe, receiver.tpe))
1089
- if (ObjectTpe <:< common)
1090
- unrelatedTypes()
1091
- }
1092
- // warn if actual has a case parent that is not same as receiver's;
1093
- // if actual is not a case, then warn if no common supertype, as below
1094
- if (isCaseEquals) {
1095
- def thisCase = receiver.info.member(nme.equals_).owner
1096
- actual.info.baseClasses.find(_.isCase) match {
1097
- case Some (p) if p != thisCase => nonSensible(" case class " , false )
1098
- case None =>
1099
- // stronger message on (Some(1) == None)
1100
- // if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq()
1101
- // else
1102
- // if a class, it must be super to thisCase (and receiver) since not <: thisCase
1103
- if (! actual.isTrait && ! (receiver isSubClass actual)) nonSensiblyNeq()
1104
- else if (! haveSubclassRelationship) warnIfLubless()
1105
- case _ =>
1106
- }
1107
- }
1108
- // warn only if they have no common supertype below Object
1109
- else if (! haveSubclassRelationship) {
1110
- warnIfLubless()
1111
- }
1107
+ // warn only if they have no common supertype below Object
1108
+ else if (! haveSubclassRelationship) {
1109
+ warnIfLubless()
1112
1110
}
1111
+ }
1112
+ }
1113
+ /** Sensibility check examines flavors of equals. */
1114
+ def checkSensible (pos : Position , fn : Tree , args : List [Tree ]) = fn match {
1115
+ case Select (qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) =>
1116
+ checkSensibleEquals(pos, qual, name, fn.symbol, args.head)
1113
1117
case _ =>
1114
1118
}
1115
1119
@@ -1526,7 +1530,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
1526
1530
transform(qual)
1527
1531
1528
1532
case Apply (fn, args) =>
1529
- // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability analyses in the pattern matcher
1533
+ // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability
1534
+ // analyses in the pattern matcher
1530
1535
if (! inPattern) {
1531
1536
checkImplicitViewOptionApply(tree.pos, fn, args)
1532
1537
checkSensible(tree.pos, fn, args)
0 commit comments