aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/typer/RefChecks.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer/RefChecks.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/RefChecks.scala1526
1 files changed, 1526 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
new file mode 100644
index 000000000..46bdbf3b3
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -0,0 +1,1526 @@
+package dotty.tools.dotc
+package typer
+
+import transform._
+import core._
+import config._
+import Symbols._, SymDenotations._, Types._, Contexts._, Decorators._, Flags._, Names._, NameOps._
+import StdNames._, Denotations._, Scopes._, Constants.Constant, SymUtils._
+import Annotations._
+import util.Positions._
+import scala.collection.{ mutable, immutable }
+import ast._
+import Trees._
+import TreeTransforms._
+import util.DotClass
+import scala.util.{Try, Success, Failure}
+import config.{ScalaVersion, NoScalaVersion}
+import Decorators._
+import typer.ErrorReporting._
+import DenotTransformers._
+import ValueClasses.isDerivedValueClass
+
+object RefChecks {
+ import tpd._
+
+ private def isDefaultGetter(name: Name): Boolean =
+ name.isTermName && name.asTermName.defaultGetterIndex >= 0
+
+ private val defaultMethodFilter = new NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = isDefaultGetter(name)
+ }
+
+ /** Only one overloaded alternative is allowed to define default arguments */
+ private def checkOverloadedRestrictions(clazz: Symbol)(implicit ctx: Context): Unit = {
+ // Using the default getters (such as methodName$default$1) as a cheap way of
+ // finding methods with default parameters. This way, we can limit the members to
+ // those with the DEFAULTPARAM flag, and infer the methods. Looking for the methods
+ // directly requires inspecting the parameter list of every one. That modification
+ // shaved 95% off the time spent in this method.
+
+ for (
+ defaultGetterClass <- List(clazz, clazz.companionModule.moduleClass);
+ if defaultGetterClass.isClass
+ ) {
+ val defaultGetterNames = defaultGetterClass.asClass.memberNames(defaultMethodFilter)
+ val defaultMethodNames = defaultGetterNames map (_.asTermName.defaultGetterToMethod)
+
+ for (name <- defaultMethodNames) {
+ val methods = clazz.info.member(name).alternatives.map(_.symbol)
+ val haveDefaults = methods.filter(_.hasDefaultParams)
+ if (haveDefaults.length > 1) {
+ val owners = haveDefaults map (_.owner)
+ // constructors of different classes are allowed to have defaults
+ if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size)
+ ctx.error(
+ "in " + clazz +
+ ", multiple overloaded alternatives of " + haveDefaults.head +
+ " define default arguments" + (
+ if (owners.forall(_ == clazz)) "."
+ else ".\nThe members with defaults are defined in " + owners.map(_.showLocated).mkString("", " and ", ".")),
+ clazz.pos)
+ }
+ }
+ }
+
+ // Check for doomed attempt to overload applyDynamic
+ if (clazz derivesFrom defn.DynamicClass) {
+ for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.symbol.typeParams.length)) {
+ ctx.error("implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)",
+ m1.symbol.pos)
+ }
+ }
+ }
+
+ /** Check that self type of this class conforms to self types of parents.
+ * and required classes.
+ */
+ private def checkParents(cls: Symbol)(implicit ctx: Context): Unit = cls.info match {
+ case cinfo: ClassInfo =>
+ def checkSelfConforms(other: TypeRef, category: String, relation: String) = {
+ val otherSelf = other.givenSelfType.asSeenFrom(cls.thisType, other.classSymbol)
+ if (otherSelf.exists && !(cinfo.selfType <:< otherSelf))
+ ctx.error(ex"$category: self type ${cinfo.selfType} of $cls does not conform to self type $otherSelf of $relation ${other.classSymbol}", cls.pos)
+ }
+ for (parent <- cinfo.classParents)
+ checkSelfConforms(parent, "illegal inheritance", "parent")
+ for (reqd <- cinfo.givenSelfType.classSymbols)
+ checkSelfConforms(reqd.typeRef, "missing requirement", "required")
+ case _ =>
+ }
+
+ /** Check that a class and its companion object to not both define
+ * a class or module with same name
+ */
+ private def checkCompanionNameClashes(cls: Symbol)(implicit ctx: Context): Unit =
+ if (!(cls.owner is ModuleClass)) {
+ val other = cls.owner.linkedClass.info.decl(cls.name)
+ if (other.symbol.isClass)
+ ctx.error(s"name clash: ${cls.owner} defines $cls" + "\n" +
+ s"and its companion ${cls.owner.companionModule} also defines $other",
+ cls.pos)
+ }
+
+ // Override checking ------------------------------------------------------------
+
+ /** 1. Check all members of class `clazz` for overriding conditions.
+ * That is for overriding member M and overridden member O:
+ *
+ * 1.1. M must have the same or stronger access privileges as O.
+ * 1.2. O must not be final.
+ * 1.3. O is deferred, or M has `override` modifier.
+ * 1.4. If O is stable, then so is M.
+ * // @M: LIFTED 1.5. Neither M nor O are a parameterized type alias
+ * 1.6. If O is a type alias, then M is an alias of O.
+ * 1.7. If O is an abstract type then
+ * 1.7.1 either M is an abstract type, and M's bounds are sharper than O's bounds.
+ * or M is a type alias or class which conforms to O's bounds.
+ * 1.7.2 higher-order type arguments must respect bounds on higher-order type parameters -- @M
+ * (explicit bounds and those implied by variance annotations) -- @see checkKindBounds
+ * 1.8. If O and M are values, then
+ * 1.8.1 M's type is a subtype of O's type, or
+ * 1.8.2 M is of type []S, O is of type ()T and S <: T, or
+ * 1.8.3 M is of type ()S, O is of type []T and S <: T, or
+ * 1.9. If M is a macro def, O cannot be deferred unless there's a concrete method overriding O.
+ * 1.10. If M is not a macro def, O cannot be a macro def.
+ * 2. Check that only abstract classes have deferred members
+ * 3. Check that concrete classes do not have deferred definitions
+ * that are not implemented in a subclass.
+ * 4. Check that every member with an `override` modifier
+ * overrides some other member.
+ * TODO check that classes are not overridden
+ * TODO This still needs to be cleaned up; the current version is a staright port of what was there
+ * before, but it looks too complicated and method bodies are far too large.
+ */
+ private def checkAllOverrides(clazz: Symbol)(implicit ctx: Context): Unit = {
+ val self = clazz.thisType
+ var hasErrors = false
+
+ case class MixinOverrideError(member: Symbol, msg: String)
+
+ val mixinOverrideErrors = new mutable.ListBuffer[MixinOverrideError]()
+
+ def printMixinOverrideErrors(): Unit = {
+ mixinOverrideErrors.toList match {
+ case List() =>
+ case List(MixinOverrideError(_, msg)) =>
+ ctx.error(msg, clazz.pos)
+ case MixinOverrideError(member, msg) :: others =>
+ val others1 = others.map(_.member).filter(_.name != member.name).distinct
+ def othersMsg = {
+ val others1 = others.map(_.member)
+ .filter(_.name != member.name)
+ .map(_.show).distinct
+ if (others1.isEmpty) ""
+ else i";\n other members with override errors are:: $others1%, %"
+ }
+ ctx.error(msg + othersMsg, clazz.pos)
+ }
+ }
+
+ def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz)
+ def infoStringWithLocation(sym: Symbol) = infoString0(sym, true)
+
+ def infoString0(sym: Symbol, showLocation: Boolean) = {
+ val sym1 = sym.underlyingSymbol
+ def info = self.memberInfo(sym1)
+ i"${if (showLocation) sym1.showLocated else sym1}${
+ if (sym1.isAliasType) i", which equals ${info.bounds.hi}"
+ else if (sym1.isAbstractType) i" with bounds$info"
+ else if (sym1.is(Module)) ""
+ else if (sym1.isTerm) i" of type $info"
+ else ""
+ }"
+ }
+
+ /* Check that all conditions for overriding `other` by `member`
+ * of class `clazz` are met.
+ */
+ def checkOverride(member: Symbol, other: Symbol): Unit = {
+ def memberTp = self.memberInfo(member)
+ def otherTp = self.memberInfo(other)
+
+ ctx.debuglog("Checking validity of %s overriding %s".format(member.showLocated, other.showLocated))
+
+ def noErrorType = !memberTp.isErroneous && !otherTp.isErroneous
+
+ def overrideErrorMsg(msg: String): String = {
+ val isConcreteOverAbstract =
+ (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred)
+ val addendum =
+ if (isConcreteOverAbstract)
+ ";\n (Note that %s is abstract,\n and is therefore overridden by concrete %s)".format(
+ infoStringWithLocation(other),
+ infoStringWithLocation(member))
+ else if (ctx.settings.debug.value)
+ err.typeMismatchMsg(memberTp, otherTp)
+ else ""
+
+ "overriding %s;\n %s %s%s".format(
+ infoStringWithLocation(other), infoString(member), msg, addendum)
+ }
+
+ def emitOverrideError(fullmsg: String) =
+ if (!(hasErrors && member.is(Synthetic) && member.is(Module))) {
+ // suppress errors relating toi synthetic companion objects if other override
+ // errors (e.g. relating to the companion class) have already been reported.
+ if (member.owner == clazz) ctx.error(fullmsg, member.pos)
+ else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
+ hasErrors = true
+ }
+
+ def overrideError(msg: String) = {
+ if (noErrorType)
+ emitOverrideError(overrideErrorMsg(msg))
+ }
+
+ def autoOverride(sym: Symbol) =
+ sym.is(Synthetic) && (
+ desugar.isDesugaredCaseClassMethodName(member.name) || // such names are added automatically, can't have an override preset.
+ sym.is(Module)) // synthetic companion
+
+ def overrideAccessError() = {
+ ctx.log(i"member: ${member.showLocated} ${member.flags}") // DEBUG
+ ctx.log(i"other: ${other.showLocated} ${other.flags}") // DEBUG
+ val otherAccess = (other.flags & AccessFlags).toString
+ overrideError("has weaker access privileges; it should be " +
+ (if (otherAccess == "") "public" else "at least " + otherAccess))
+ }
+
+ def compatibleTypes =
+ if (member.isType) { // intersection of bounds to refined types must be nonempty
+ member.is(BaseTypeArg) ||
+ (memberTp frozen_<:< otherTp) || {
+ val jointBounds = (memberTp.bounds & otherTp.bounds).bounds
+ jointBounds.lo frozen_<:< jointBounds.hi
+ }
+ }
+ else
+ isDefaultGetter(member.name) || // default getters are not checked for compatibility
+ memberTp.overrides(otherTp)
+
+ def domain(sym: Symbol): Set[Name] = sym.info.namedTypeParams.map(_.name)
+
+ //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG
+
+ // return if we already checked this combination elsewhere
+ if (member.owner != clazz) {
+ def deferredCheck = member.is(Deferred) || !other.is(Deferred)
+ def subOther(s: Symbol) = s derivesFrom other.owner
+ def subMember(s: Symbol) = s derivesFrom member.owner
+
+ if (subOther(member.owner) && deferredCheck) {
+ //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG
+ return
+ }
+ val parentSymbols = clazz.info.parents.map(_.typeSymbol)
+ if (parentSymbols exists (p => subOther(p) && subMember(p) && deferredCheck)) {
+ //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG
+ return
+ }
+ if (parentSymbols forall (p => subOther(p) == subMember(p))) {
+ //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG
+ return
+ }
+ }
+
+ /* Is the intersection between given two lists of overridden symbols empty? */
+ def intersectionIsEmpty(syms1: Iterator[Symbol], syms2: Iterator[Symbol]) = {
+ val set2 = syms2.toSet
+ !(syms1 exists (set2 contains _))
+ }
+
+ // o: public | protected | package-protected (aka java's default access)
+ // ^-may be overridden by member with access privileges-v
+ // m: public | public/protected | public/protected/package-protected-in-same-package-as-o
+
+ if (member.is(Private)) // (1.1)
+ overrideError("has weaker access privileges; it should not be private")
+
+ // todo: align accessibility implication checking with isAccessible in Contexts
+ val ob = other.accessBoundary(member.owner)
+ val mb = member.accessBoundary(member.owner)
+ def isOverrideAccessOK = (
+ (member.flags & AccessFlags).isEmpty // member is public
+ || // - or -
+ (!other.is(Protected) || member.is(Protected)) && // if o is protected, so is m, and
+ (ob.isContainedIn(mb) || other.is(JavaProtected)) // m relaxes o's access boundary,
+ // or o is Java defined and protected (see #3946)
+ )
+ if (!isOverrideAccessOK) {
+ overrideAccessError()
+ } else if (other.isClass) {
+ overrideError("cannot be used here - class definitions cannot be overridden")
+ } else if (!other.is(Deferred) && member.isClass) {
+ overrideError("cannot be used here - classes can only override abstract types")
+ } else if (other.isEffectivelyFinal) { // (1.2)
+ overrideError(i"cannot override final member ${other.showLocated}")
+ } else if (!other.is(Deferred) &&
+ !isDefaultGetter(other.name) &&
+ !member.isAnyOverride) {
+ // (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to
+ // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket.
+ if (autoOverride(member))
+ member.setFlag(Override)
+ else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner))
+ emitOverrideError(
+ clazz + " inherits conflicting members:\n "
+ + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member)
+ + "\n(Note: this can be resolved by declaring an override in " + clazz + ".)")
+ else
+ overrideError("needs `override' modifier")
+ } else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) {
+ overrideError("needs `abstract override' modifiers")
+ } else if (member.is(Override) && other.is(Accessor) &&
+ other.accessedFieldOrGetter.is(Mutable, butNot = Lazy)) {
+ // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here.
+ // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches.
+ if (!ctx.settings.overrideVars.value)
+ overrideError("cannot override a mutable variable")
+ } else if (member.isAnyOverride &&
+ !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) &&
+ !member.is(Deferred) && !other.is(Deferred) &&
+ intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) {
+ overrideError("cannot override a concrete member without a third member that's overridden by both " +
+ "(this rule is designed to prevent ``accidental overrides'')")
+ } else if (other.isStable && !member.isStable) { // (1.4)
+ overrideError("needs to be a stable, immutable value")
+ } else if (member.is(ModuleVal) && !other.isRealMethod && !other.is(Deferred | Lazy)) {
+ overrideError("may not override a concrete non-lazy value")
+ } else if (member.is(Lazy, butNot = Module) && !other.isRealMethod && !other.is(Lazy)) {
+ overrideError("may not override a non-lazy value")
+ } else if (other.is(Lazy) && !other.isRealMethod && !member.is(Lazy)) {
+ overrideError("must be declared lazy to override a lazy value")
+ } else if (other.is(Deferred) && member.is(Macro) && member.extendedOverriddenSymbols.forall(_.is(Deferred))) { // (1.9)
+ overrideError("cannot be used here - term macros cannot override abstract methods")
+ } else if (other.is(Macro) && !member.is(Macro)) { // (1.10)
+ overrideError("cannot be used here - only term macros can override term macros")
+ } else if (!compatibleTypes) {
+ overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp))
+ } else if (member.isType && domain(member) != domain(other)) {
+ overrideError("has different named type parameters: "+
+ i"[${domain(member).toList}%, %] instead of [${domain(other).toList}%, %]")
+ } else {
+ checkOverrideDeprecated()
+ }
+ }
+
+ /* TODO enable; right now the annotation is scala-private, so cannot be seen
+ * here.
+ */
+ def checkOverrideDeprecated() = { /*
+ if (other.hasDeprecatedOverridingAnnotation) {
+ val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse ""
+ val msg = s"overriding ${other.fullLocationString} is deprecated$suffix"
+ unit.deprecationWarning(member.pos, msg)
+ }*/
+ }
+
+ try {
+ val opc = new OverridingPairs.Cursor(clazz)
+ while (opc.hasNext) {
+ checkOverride(opc.overriding, opc.overridden)
+ opc.next()
+ }
+ } catch {
+ case ex: MergeError =>
+ val addendum = ex.tp1 match {
+ case tp1: ClassInfo =>
+ "\n(Note that having same-named member classes in types of a mixin composition is no longer allowed)"
+ case _ => ""
+ }
+ ctx.error(ex.getMessage + addendum, clazz.pos)
+ }
+ printMixinOverrideErrors()
+
+ // Verifying a concrete class has nothing unimplemented.
+ if (!clazz.is(AbstractOrTrait)) {
+ val abstractErrors = new mutable.ListBuffer[String]
+ def abstractErrorMessage =
+ // a little formatting polish
+ if (abstractErrors.size <= 2) abstractErrors mkString " "
+ else abstractErrors.tail.mkString(abstractErrors.head + ":\n", "\n", "")
+
+ def abstractClassError(mustBeMixin: Boolean, msg: String): Unit = {
+ def prelude = (
+ if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible"
+ else if (mustBeMixin) clazz + " needs to be a mixin"
+ else clazz + " needs to be abstract") + ", since"
+
+ if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg)
+ else abstractErrors += msg
+ }
+
+ def hasJavaErasedOverriding(sym: Symbol): Boolean =
+ !ctx.erasurePhase.exists || // can't do the test, assume the best
+ ctx.atPhase(ctx.erasurePhase.next) { implicit ctx =>
+ clazz.info.nonPrivateMember(sym.name).hasAltWith { alt =>
+ alt.symbol.is(JavaDefined, butNot = Deferred) &&
+ !sym.owner.derivesFrom(alt.symbol.owner) &&
+ alt.matches(sym)
+ }
+ }
+
+ def ignoreDeferred(member: SingleDenotation) =
+ member.isType ||
+ member.symbol.is(SuperAccessor) || // not yet synthesized
+ member.symbol.is(JavaDefined) && hasJavaErasedOverriding(member.symbol)
+
+ // 2. Check that only abstract classes have deferred members
+ def checkNoAbstractMembers(): Unit = {
+ // Avoid spurious duplicates: first gather any missing members.
+ val missing = clazz.thisType.abstractTermMembers.filterNot(ignoreDeferred)
+ // Group missing members by the name of the underlying symbol,
+ // to consolidate getters and setters.
+ val grouped: Map[Name, Seq[SingleDenotation]] = missing groupBy (_.symbol.underlyingSymbol.name)
+ // Dotty deviation: Added type annotation for `grouped`.
+ // The inferred type is Map[Symbol#ThisName, Seq[SingleDenotation]]
+ // but then the definition of isMultiple fails with an error:
+ // RefChecks.scala:379: error: type mismatch:
+ // found : underlying.ThisName
+ // required: dotty.tools.dotc.core.Symbols.Symbol#ThisName
+ //
+ // val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1
+ // ^
+ // As far as I can see, the complaint is correct, even under the
+ // old reading where Symbol#ThisName means x.ThisName forSome { val x }
+
+ val missingMethods = grouped.toList flatMap {
+ case (name, syms) =>
+ val withoutSetters = syms filterNot (_.symbol.isSetter)
+ if (withoutSetters.nonEmpty) withoutSetters else syms
+ }
+
+ def stubImplementations: List[String] = {
+ // Grouping missing methods by the declaring class
+ val regrouped = missingMethods.groupBy(_.symbol.owner).toList
+ def membersStrings(members: List[SingleDenotation]) =
+ members.sortBy(_.symbol.name.toString).map(_.showDcl + " = ???")
+
+ if (regrouped.tail.isEmpty)
+ membersStrings(regrouped.head._2)
+ else (regrouped.sortBy("" + _._1.name) flatMap {
+ case (owner, members) =>
+ ("// Members declared in " + owner.fullName) +: membersStrings(members) :+ ""
+ }).init
+ }
+
+ // If there are numerous missing methods, we presume they are aware of it and
+ // give them a nicely formatted set of method signatures for implementing.
+ if (missingMethods.size > 1) {
+ abstractClassError(false, "it has " + missingMethods.size + " unimplemented members.")
+ val preface =
+ """|/** As seen from %s, the missing signatures are as follows.
+ | * For convenience, these are usable as stub implementations.
+ | */
+ |""".stripMargin.format(clazz)
+ abstractErrors += stubImplementations.map(" " + _ + "\n").mkString(preface, "", "")
+ return
+ }
+
+ for (member <- missing) {
+ val memberSym = member.symbol
+ def undefined(msg: String) =
+ abstractClassError(false, s"${member.showDcl} is not defined $msg")
+ val underlying = memberSym.underlyingSymbol
+
+ // Give a specific error message for abstract vars based on why it fails:
+ // It could be unimplemented, have only one accessor, or be uninitialized.
+ if (underlying.is(Mutable)) {
+ val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1
+
+ // If both getter and setter are missing, squelch the setter error.
+ if (memberSym.isSetter && isMultiple) ()
+ else undefined(
+ if (memberSym.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)"
+ else if (memberSym.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)"
+ else err.abstractVarMessage(memberSym))
+ } else if (underlying.is(Method)) {
+ // If there is a concrete method whose name matches the unimplemented
+ // abstract method, and a cursory examination of the difference reveals
+ // something obvious to us, let's make it more obvious to them.
+ val abstractParams = underlying.info.firstParamTypes
+ val matchingName = clazz.info.nonPrivateMember(underlying.name).alternatives
+ val matchingArity = matchingName filter { m =>
+ !m.symbol.is(Deferred) &&
+ m.info.firstParamTypes.length == abstractParams.length
+ }
+
+ matchingArity match {
+ // So far so good: only one candidate method
+ case concrete :: Nil =>
+ val mismatches =
+ abstractParams.zip(concrete.info.firstParamTypes)
+ .filterNot { case (x, y) => x =:= y }
+ mismatches match {
+ // Only one mismatched parameter: say something useful.
+ case (pa, pc) :: Nil =>
+ val abstractSym = pa.typeSymbol
+ val concreteSym = pc.typeSymbol
+ def subclassMsg(c1: Symbol, c2: Symbol) =
+ s": ${c1.showLocated} is a subclass of ${c2.showLocated}, but method parameter types must match exactly."
+ val addendum =
+ if (abstractSym == concreteSym) {
+ val paArgs = pa.argInfos
+ val pcArgs = pc.argInfos
+ val paConstr = pa.withoutArgs(paArgs)
+ val pcConstr = pc.withoutArgs(pcArgs)
+ (paConstr, pcConstr) match {
+ case (TypeRef(pre1, _), TypeRef(pre2, _)) =>
+ if (pre1 =:= pre2) ": their type parameters differ"
+ else ": their prefixes (i.e. enclosing instances) differ"
+ case _ =>
+ ""
+ }
+ } else if (abstractSym isSubClass concreteSym)
+ subclassMsg(abstractSym, concreteSym)
+ else if (concreteSym isSubClass abstractSym)
+ subclassMsg(concreteSym, abstractSym)
+ else ""
+
+ undefined(s"\n(Note that ${pa.show} does not match ${pc.show}$addendum)")
+ case xs =>
+ undefined(s"\n(The class implements a member with a different type: ${concrete.showDcl})")
+ }
+ case Nil =>
+ undefined("")
+ case concretes =>
+ undefined(s"\n(The class implements members with different types: ${concretes.map(_.showDcl)}%\n %)")
+ }
+ } else undefined("")
+ }
+ }
+
+ // 3. Check that concrete classes do not have deferred definitions
+ // that are not implemented in a subclass.
+ // Note that this is not the same as (2); In a situation like
+ //
+ // class C { def m: Int = 0}
+ // class D extends C { def m: Int }
+ //
+ // (3) is violated but not (2).
+ def checkNoAbstractDecls(bc: Symbol): Unit = {
+ for (decl <- bc.info.decls) {
+ if (decl.is(Deferred) && !ignoreDeferred(decl)) {
+ val impl = decl.matchingMember(clazz.thisType)
+ if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) {
+ val impl1 = clazz.thisType.nonPrivateMember(decl.name) // DEBUG
+ ctx.log(i"${impl1}: ${impl1.info}") // DEBUG
+ ctx.log(i"${clazz.thisType.memberInfo(decl)}") // DEBUG
+ abstractClassError(false, "there is a deferred declaration of " + infoString(decl) +
+ " which is not implemented in a subclass" + err.abstractVarMessage(decl))
+ }
+ }
+ }
+ if (bc.asClass.superClass.is(Abstract))
+ checkNoAbstractDecls(bc.asClass.superClass)
+ }
+
+ checkNoAbstractMembers()
+ if (abstractErrors.isEmpty)
+ checkNoAbstractDecls(clazz)
+
+ if (abstractErrors.nonEmpty)
+ ctx.error(abstractErrorMessage, clazz.pos)
+ } else if (clazz.is(Trait) && !(clazz derivesFrom defn.AnyValClass)) {
+ // For non-AnyVal classes, prevent abstract methods in interfaces that override
+ // final members in Object; see #4431
+ for (decl <- clazz.info.decls) {
+ // Have to use matchingSymbol, not a method involving overridden symbols,
+ // because the scala type system understands that an abstract method here does not
+ // override a concrete method in Object. The jvm, however, does not.
+ val overridden = decl.matchingDecl(defn.ObjectClass, defn.ObjectType)
+ if (overridden.is(Final))
+ ctx.error("trait cannot redefine final method from class AnyRef", decl.pos)
+ }
+ }
+
+ /* Returns whether there is a symbol declared in class `inclazz`
+ * (which must be different from `clazz`) whose name and type
+ * seen as a member of `class.thisType` matches `member`'s.
+ */
+ def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = {
+
+ def isSignatureMatch(sym: Symbol) = !sym.isTerm ||
+ clazz.thisType.memberInfo(sym).matchesLoosely(member.info)
+
+ /* The rules for accessing members which have an access boundary are more
+ * restrictive in java than scala. Since java has no concept of package nesting,
+ * a member with "default" (package-level) access can only be accessed by members
+ * in the exact same package. Example:
+ *
+ * package a.b;
+ * public class JavaClass { void foo() { } }
+ *
+ * The member foo() can be accessed only from members of package a.b, and not
+ * nested packages like a.b.c. In the analogous scala class:
+ *
+ * package a.b
+ * class ScalaClass { private[b] def foo() = () }
+ *
+ * The member IS accessible to classes in package a.b.c. The javaAccessCheck logic
+ * is restricting the set of matching signatures according to the above semantics.
+ */
+ def javaAccessCheck(sym: Symbol) = (
+ !inclazz.is(JavaDefined) // not a java defined member
+ || !sym.privateWithin.exists // no access boundary
+ || sym.is(Protected) // marked protected in java, thus accessible to subclasses
+ || sym.privateWithin == member.enclosingPackageClass // exact package match
+ )
+ def classDecls = inclazz.info.nonPrivateDecl(member.name)
+
+ (inclazz != clazz) &&
+ classDecls.hasAltWith(d => isSignatureMatch(d.symbol) && javaAccessCheck(d.symbol))
+ }
+
+ // 4. Check that every defined member with an `override` modifier overrides some other member.
+ for (member <- clazz.info.decls)
+ if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) {
+ // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG
+
+ val nonMatching = clazz.info.member(member.name).altsWith(alt => alt.owner != clazz && !alt.is(Final))
+ def issueError(suffix: String) =
+ ctx.error(i"$member overrides nothing$suffix", member.pos)
+ nonMatching match {
+ case Nil =>
+ issueError("")
+ case ms =>
+ val superSigs = ms.map(_.showDcl).mkString("\n")
+ issueError(s".\nNote: the super classes of ${member.owner} contain the following, non final members named ${member.name}:\n${superSigs}")
+ }
+ member.resetFlag(Override)
+ member.resetFlag(AbsOverride)
+ }
+ }
+
+ // Note: if a symbol has both @deprecated and @migration annotations and both
+ // warnings are enabled, only the first one checked here will be emitted.
+ // I assume that's a consequence of some code trying to avoid noise by suppressing
+ // warnings after the first, but I think it'd be better if we didn't have to
+ // arbitrarily choose one as more important than the other.
+ private def checkUndesiredProperties(sym: Symbol, pos: Position)(implicit ctx: Context): Unit = {
+ // If symbol is deprecated, and the point of reference is not enclosed
+ // in either a deprecated member or a scala bridge method, issue a warning.
+ if (sym.isDeprecated && !ctx.owner.ownersIterator.exists(_.isDeprecated)) {
+ ctx.deprecationWarning("%s%s is deprecated%s".format(
+ sym, sym.showLocated, sym.deprecationMessage map (": " + _) getOrElse "", pos))
+ }
+ // Similar to deprecation: check if the symbol is marked with @migration
+ // indicating it has changed semantics between versions.
+ if (sym.hasAnnotation(defn.MigrationAnnot) && ctx.settings.Xmigration.value != NoScalaVersion) {
+ val symVersion: scala.util.Try[ScalaVersion] = sym.migrationVersion.get
+ val changed = symVersion match {
+ case scala.util.Success(v) =>
+ ctx.settings.Xmigration.value < v
+ case Failure(ex) =>
+ ctx.warning(s"${sym.showLocated} has an unparsable version number: ${ex.getMessage()}", pos)
+ false
+ }
+ if (changed)
+ ctx.warning(s"${sym.showLocated} has changed semantics in version $symVersion:\n${sym.migrationMessage.get}")
+ }
+ /* (Not enabled yet)
+ * See an explanation of compileTimeOnly in its scaladoc at scala.annotation.compileTimeOnly.
+ *
+ if (sym.isCompileTimeOnly) {
+ def defaultMsg =
+ sm"""Reference to ${sym.fullLocationString} should not have survived past type checking,
+ |it should have been processed and eliminated during expansion of an enclosing macro."""
+ // The getOrElse part should never happen, it's just here as a backstop.
+ ctx.error(sym.compileTimeOnlyMessage getOrElse defaultMsg, pos)
+ }*/
+ }
+
+ /** Check that a deprecated val or def does not override a
+ * concrete, non-deprecated method. If it does, then
+ * deprecation is meaningless.
+ */
+ private def checkDeprecatedOvers(tree: Tree)(implicit ctx: Context): Unit = {
+ val symbol = tree.symbol
+ if (symbol.isDeprecated) {
+ val concrOvers =
+ symbol.allOverriddenSymbols.filter(sym =>
+ !sym.isDeprecated && !sym.is(Deferred))
+ if (!concrOvers.isEmpty)
+ ctx.deprecationWarning(
+ symbol.toString + " overrides concrete, non-deprecated symbol(s):" +
+ concrOvers.map(_.name.decode).mkString(" ", ", ", ""), tree.pos)
+ }
+ }
+
+ /** Verify classes extending AnyVal meet the requirements */
+ private def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = {
+ def checkValueClassMember(stat: Tree) = stat match {
+ case _: ValDef if !stat.symbol.is(ParamAccessor) =>
+ ctx.error(s"value class may not define non-parameter field", stat.pos)
+ case _: DefDef if stat.symbol.isConstructor =>
+ ctx.error(s"value class may not define secondary constructor", stat.pos)
+ case _: MemberDef | _: Import | EmptyTree =>
+ // ok
+ case _ =>
+ ctx.error(s"value class may not contain initialization statements", stat.pos)
+ }
+ if (isDerivedValueClass(clazz)) {
+ if (clazz.is(Trait))
+ ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos)
+ if (clazz.is(Abstract))
+ ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos)
+ if (!clazz.isStatic)
+ ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos)
+ else {
+ val clParamAccessors = clazz.asClass.paramAccessors.filter(sym => sym.isTerm && !sym.is(Method))
+ clParamAccessors match {
+ case List(param) =>
+ if (param.is(Mutable))
+ ctx.error("value class parameter must not be a var", param.pos)
+ case _ =>
+ ctx.error("value class needs to have exactly one val parameter", clazz.pos)
+ }
+ }
+ stats.foreach(checkValueClassMember)
+ }
+ }
+
+ type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)]
+
+ class OptLevelInfo extends DotClass {
+ def levelAndIndex: LevelAndIndex = Map()
+ def enterReference(sym: Symbol, pos: Position): Unit = ()
+ }
+
+ /** A class to help in forward reference checking */
+ class LevelInfo(outerLevelAndIndex: LevelAndIndex, stats: List[Tree])(implicit ctx: Context)
+ extends OptLevelInfo {
+ override val levelAndIndex: LevelAndIndex =
+ ((outerLevelAndIndex, 0) /: stats) {(mi, stat) =>
+ val (m, idx) = mi
+ val m1 = stat match {
+ case stat: MemberDef => m.updated(stat.symbol, (this, idx))
+ case _ => m
+ }
+ (m1, idx + 1)
+ }._1
+ var maxIndex: Int = Int.MinValue
+ var refPos: Position = _
+ var refSym: Symbol = _
+
+ override def enterReference(sym: Symbol, pos: Position): Unit =
+ if (sym.exists && sym.owner.isTerm)
+ levelAndIndex.get(sym) match {
+ case Some((level, idx)) if (level.maxIndex < idx) =>
+ level.maxIndex = idx
+ level.refPos = pos
+ level.refSym = sym
+ case _ =>
+ }
+ }
+
+ val NoLevelInfo = new OptLevelInfo()
+}
+import RefChecks._
+
+/** Post-attribution checking and transformation, which fulfills the following roles
+ *
+ * 1. This phase performs the following checks.
+ *
+ * - only one overloaded alternative defines default arguments
+ * - applyDynamic methods are not overloaded
+ * - all overrides conform to rules laid down by `checkAllOverrides`.
+ * - any value classes conform to rules laid down by `checkDerivedValueClass`.
+ * - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals).
+ * - no forward reference in a local block jumps over a non-lazy val definition.
+ * - a class and its companion object do not both define a class or module with the same name.
+ *
+ * 2. It warns about references to symbols labeled deprecated or migration.
+
+ * 3. It performs the following transformations:
+ *
+ * - if (true) A else B --> A
+ * if (false) A else B --> B
+ * - macro definitions are eliminated.
+ *
+ * 4. It makes members not private where necessary. The following members
+ * cannot be private in the Java model:
+ * - term members of traits
+ * - the primary constructor of a value class
+ * - the parameter accessor of a value class
+ * - members accessed from an inner or companion class.
+ * All these members are marked as NotJavaPrivate.
+ * Unlike in Scala 2.x not-private members keep their name. It is
+ * up to the backend to find a unique expanded name for them. The
+ * rationale to do name changes that late is that they are very fragile.
+
+ * todo: But RefChecks is not done yet. It's still a somewhat dirty port from the Scala 2 version.
+ * todo: move untrivial logic to their own mini-phases
+ */
+class RefChecks extends MiniPhase { thisTransformer =>
+
+ import tpd._
+
+ override def phaseName: String = "refchecks"
+
+ val treeTransform = new Transform(NoLevelInfo)
+
+ class Transform(currentLevel: RefChecks.OptLevelInfo = RefChecks.NoLevelInfo) extends TreeTransform {
+ def phase = thisTransformer
+
+ override def prepareForStats(trees: List[Tree])(implicit ctx: Context) = {
+ // println(i"preparing for $trees%; %, owner = ${ctx.owner}")
+ if (ctx.owner.isTerm) new Transform(new LevelInfo(currentLevel.levelAndIndex, trees))
+ else this
+ }
+
+ override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees
+
+ override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo) = {
+ checkDeprecatedOvers(tree)
+ val sym = tree.symbol
+ if (sym.exists && sym.owner.isTerm && !sym.is(Lazy))
+ currentLevel.levelAndIndex.get(sym) match {
+ case Some((level, symIdx)) if symIdx < level.maxIndex =>
+ ctx.debuglog("refsym = " + level.refSym)
+ ctx.error(s"forward reference extends over definition of $sym", level.refPos)
+ case _ =>
+ }
+ tree
+ }
+
+ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = {
+ checkDeprecatedOvers(tree)
+ if (tree.symbol is Macro) EmptyTree else tree
+ }
+
+ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = try {
+ val cls = ctx.owner
+ checkOverloadedRestrictions(cls)
+ checkParents(cls)
+ checkCompanionNameClashes(cls)
+ checkAllOverrides(cls)
+ checkDerivedValueClass(cls, tree.body)
+ tree
+ } catch {
+ case ex: MergeError =>
+ ctx.error(ex.getMessage, tree.pos)
+ tree
+ }
+
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = {
+ checkUndesiredProperties(tree.symbol, tree.pos)
+ currentLevel.enterReference(tree.symbol, tree.pos)
+ tree
+ }
+
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = {
+ checkUndesiredProperties(tree.symbol, tree.pos)
+ tree
+ }
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = {
+ if (isSelfConstrCall(tree)) {
+ assert(currentLevel.isInstanceOf[LevelInfo], ctx.owner + "/" + i"$tree")
+ val level = currentLevel.asInstanceOf[LevelInfo]
+ if (level.maxIndex > 0) {
+ // An implementation restriction to avoid VerifyErrors and lazyvals mishaps; see SI-4717
+ ctx.debuglog("refsym = " + level.refSym)
+ ctx.error("forward reference not allowed from self constructor invocation", level.refPos)
+ }
+ }
+ tree
+ }
+
+ override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) =
+ tree.cond.tpe match {
+ case ConstantType(value) => if (value.booleanValue) tree.thenp else tree.elsep
+ case _ => tree
+ }
+
+ override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo) = {
+ currentLevel.enterReference(tree.tpe.typeSymbol, tree.pos)
+ tree
+ }
+
+ override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ tree.fun match {
+ case fun@Select(qual, selector) =>
+ val sym = tree.symbol
+
+ if (sym == defn.Any_isInstanceOf) {
+ val argType = tree.args.head.tpe
+ val qualCls = qual.tpe.widen.classSymbol
+ val argCls = argType.classSymbol
+ if (qualCls.isPrimitiveValueClass && !argCls.isPrimitiveValueClass) ctx.error("isInstanceOf cannot test if value types are references", tree.pos)
+ }
+ case _ =>
+ }
+ tree
+ }
+ }
+}
+
+/* todo: rewrite and re-enable
+
+// Comparison checking -------------------------------------------------------
+
+ object normalizeAll extends TypeMap {
+ def apply(tp: Type) = mapOver(tp).normalize
+ }
+
+ def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match {
+ case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply =>
+ unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567
+ case _ =>
+ }
+
+ private def isObjectOrAnyComparisonMethod(sym: Symbol) = sym match {
+ case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true
+ case _ => false
+ }
+ /** Check the sensibility of using the given `equals` to compare `qual` and `other`. */
+ private def checkSensibleEquals(pos: Position, qual: Tree, name: Name, sym: Symbol, other: Tree) = {
+ def isReferenceOp = sym == Object_eq || sym == Object_ne
+ def isNew(tree: Tree) = tree match {
+ case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true
+ case _ => false
+ }
+ def underlyingClass(tp: Type): Symbol = {
+ val sym = tp.widen.typeSymbol
+ if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi)
+ else sym
+ }
+ val actual = underlyingClass(other.tpe)
+ val receiver = underlyingClass(qual.tpe)
+ def onTrees[T](f: List[Tree] => T) = f(List(qual, other))
+ def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual))
+
+ // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol`
+ def typesString = normalizeAll(qual.tpe.widen)+" and " + normalizeAll(other.tpe.widen)
+
+ /* Symbols which limit the warnings we can issue since they may be value types */
+ val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass)
+
+ // Whether def equals(other: Any) has known behavior: it is the default
+ // inherited from java.lang.Object, or it is a synthetically generated
+ // case equals. TODO - more cases are warnable if the target is a synthetic
+ // equals.
+ def isUsingWarnableEquals = {
+ val m = receiver.info.member(nme.equals_)
+ ((m == Object_equals) || (m == Any_equals) || isMethodCaseEquals(m))
+ }
+ def isMethodCaseEquals(m: Symbol) = m.isSynthetic && m.owner.isCase
+ def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_))
+ // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere.
+ def isUsingDefaultScalaOp = sym == Object_== || sym == Object_!= || sym == Any_== || sym == 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 = (NullTpe <:< receiver.info) || (NullTpe <:< 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)
+ def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass
+ def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass
+ // includes java.lang.Number if appropriate [SI-5779]
+ def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s)
+ def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s)
+ // 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
+ )
+
+ // Have we already determined that the comparison is non-sensible? I mean, non-sensical?
+ var isNonSensible = false
+
+ def nonSensibleWarning(what: String, alwaysEqual: Boolean) = {
+ val msg = alwaysEqual == (name == nme.EQ || name == nme.eq)
+ unit.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg")
+ isNonSensible = true
+ }
+ def nonSensible(pre: String, alwaysEqual: Boolean) =
+ nonSensibleWarning(s"${pre}values of types $typesString", alwaysEqual)
+ def nonSensiblyEq() = nonSensible("", alwaysEqual = true)
+ def nonSensiblyNeq() = nonSensible("", alwaysEqual = false)
+ def nonSensiblyNew() = nonSensibleWarning("a fresh object", alwaysEqual = false)
+
+ def unrelatedMsg = name match {
+ case nme.EQ | nme.eq => "never compare equal"
+ case _ => "always compare unequal"
+ }
+ def unrelatedTypes() = if (!isNonSensible) {
+ val weaselWord = if (isEitherValueClass) "" else " most likely"
+ unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg")
+ }
+
+ if (nullCount == 2) // null == null
+ nonSensiblyEq()
+ else if (nullCount == 1) {
+ if (onSyms(_ exists isPrimitiveValueClass)) // null == 5
+ nonSensiblyNeq()
+ else if (onTrees( _ exists isNew)) // null == new AnyRef
+ nonSensiblyNew()
+ }
+ else if (isBoolean(receiver)) {
+ if (!isBoolean(actual) && !isMaybeValue(actual)) // true == 5
+ nonSensiblyNeq()
+ }
+ else if (isUnit(receiver)) {
+ if (isUnit(actual)) // () == ()
+ nonSensiblyEq()
+ else if (!isUnit(actual) && !isMaybeValue(actual)) // () == "abc"
+ nonSensiblyNeq()
+ }
+ else if (isNumeric(receiver)) {
+ if (!isNumeric(actual))
+ if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc"
+ nonSensiblyNeq()
+ }
+ else if (isWarnable && !isCaseEquals) {
+ if (isNew(qual)) // new X == y
+ nonSensiblyNew()
+ else if (isNew(other) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y
+ nonSensiblyNew()
+ else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual) && !actual.isRefinementClass) { // object X, Y; X == Y
+ if (isEitherNullable)
+ nonSensible("non-null ", false)
+ else
+ nonSensiblyNeq()
+ }
+ }
+
+ // 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
+ 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 (ObjectTpe <:< common)
+ unrelatedTypes()
+ }
+ // 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) {
+ def thisCase = receiver.info.member(nme.equals_).owner
+ actual.info.baseClasses.find(_.isCase) match {
+ case Some(p) if p != thisCase => nonSensible("case class ", false)
+ case None =>
+ // stronger message on (Some(1) == None)
+ //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq()
+ //else
+ // if a class, it must be super to thisCase (and receiver) since not <: thisCase
+ if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq()
+ else if (!haveSubclassRelationship) warnIfLubless()
+ case _ =>
+ }
+ }
+ // warn only if they have no common supertype below Object
+ else if (!haveSubclassRelationship) {
+ warnIfLubless()
+ }
+ }
+ }
+ /** Sensibility check examines flavors of equals. */
+ def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match {
+ case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) =>
+ checkSensibleEquals(pos, qual, name, fn.symbol, args.head)
+ case _ =>
+ }
+*/
+
+/* --------------- Overflow -------------------------------------------------
+ *
+
+ def accessFlagsToString(sym: Symbol) = flagsToString(
+ sym getFlag (PRIVATE | PROTECTED),
+ if (sym.hasAccessBoundary) "" + sym.privateWithin.name else ""
+ )
+
+ def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match {
+ case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) =>
+ rtp1 <:< rtp2
+ case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
+ rtp1 <:< rtp2
+ case (TypeRef(_, sym, _), _) if sym.isModuleClass =>
+ overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix)
+ case _ =>
+ def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner)
+
+ (tp1 <:< tp2) || ( // object override check
+ tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && {
+ val cb1 = classBoundAsSeen(tp1)
+ val cb2 = classBoundAsSeen(tp2)
+ (cb1 <:< cb2) && {
+ log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2))
+ true
+ }
+ }
+ )
+ }
+ private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean)(implicit ctx: Context) = tp match {
+ case TypeRef(pre, sym, args) =>
+ tree match {
+ case tt: TypeTree if tt.original == null => // SI-7783 don't warn about inferred types
+ // FIXME: reconcile this check with one in resetAttrs
+ case _ => checkUndesiredProperties(sym, tree.pos)
+ }
+ if (sym.isJavaDefined)
+ sym.typeParams foreach (_.cookJavaRawInfo())
+ if (!tp.isHigherKinded && !skipBounds)
+ checkBounds(tree, pre, sym.owner, sym.typeParams, args)
+ case _ =>
+ }
+
+ private def checkTypeRefBounds(tp: Type, tree: Tree) = {
+ var skipBounds = false
+ tp match {
+ case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass =>
+ skipBounds = true
+ underlying
+ case TypeRef(pre, sym, args) =>
+ if (!tp.isHigherKinded && !skipBounds)
+ checkBounds(tree, pre, sym.owner, sym.typeParams, args)
+ tp
+ case _ =>
+ tp
+ }
+ }
+
+ private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach { tp =>
+ checkTypeRef(tp, tree, skipBounds = false)
+ checkTypeRefBounds(tp, tree)
+ }
+ private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f
+
+ private def applyRefchecksToAnnotations(tree: Tree)(implicit ctx: Context): Unit = {
+ def applyChecks(annots: List[Annotation]) = {
+ checkAnnotations(annots map (_.atp), tree)
+ transformTrees(annots flatMap (_.args))
+ }
+
+ tree match {
+ case m: MemberDef =>
+ val sym = m.symbol
+ applyChecks(sym.annotations)
+ // validate implicitNotFoundMessage
+ analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn =>
+ unit.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn")
+ }
+
+ case tpt@TypeTree() =>
+ if (tpt.original != null) {
+ tpt.original foreach {
+ case dc@TypeTreeWithDeferredRefCheck() =>
+ applyRefchecksToAnnotations(dc.check()) // #2416
+ case _ =>
+ }
+ }
+
+ doTypeTraversal(tree) {
+ case tp @ AnnotatedType(annots, _) =>
+ applyChecks(annots)
+ case tp =>
+ }
+ case _ =>
+ }
+ }
+
+ private def transformCaseApply(tree: Tree, ifNot: => Unit) = {
+ val sym = tree.symbol
+
+ def isClassTypeAccessible(tree: Tree): Boolean = tree match {
+ case TypeApply(fun, targs) =>
+ isClassTypeAccessible(fun)
+ case Select(module, apply) =>
+ ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`;
+ // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`.
+ treeInfo.isQualifierSafeToElide(module) &&
+ // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case,
+ // the companion class is actually not a ClassSymbol, but a reference to an abstract type.
+ module.symbol.companionClass.isClass
+ )
+ }
+
+ val doTransform =
+ sym.isRealMethod &&
+ sym.isCase &&
+ sym.name == nme.apply &&
+ isClassTypeAccessible(tree)
+
+ if (doTransform) {
+ tree foreach {
+ case i@Ident(_) =>
+ enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()`
+ case _ =>
+ }
+ toConstructor(tree.pos, tree.tpe)
+ }
+ else {
+ ifNot
+ tree
+ }
+ }
+
+ private def transformApply(tree: Apply): Tree = tree match {
+ case Apply(
+ Select(qual, nme.filter | nme.withFilter),
+ List(Function(
+ List(ValDef(_, pname, tpt, _)),
+ Match(_, CaseDef(pat1, _, _) :: _))))
+ if ((pname startsWith nme.CHECK_IF_REFUTABLE_STRING) &&
+ isIrrefutable(pat1, tpt.tpe) && (qual.tpe <:< tree.tpe)) =>
+
+ transform(qual)
+
+ case Apply(fn, args) =>
+ // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability
+ // analyses in the pattern matcher
+ if (!inPattern) {
+ checkImplicitViewOptionApply(tree.pos, fn, args)
+ checkSensible(tree.pos, fn, args)
+ }
+ currentApplication = tree
+ tree
+ }
+ private def transformSelect(tree: Select): Tree = {
+ val Select(qual, _) = tree
+ val sym = tree.symbol
+
+ checkUndesiredProperties(sym, tree.pos)
+ checkDelayedInitSelect(qual, sym, tree.pos)
+
+ if (!sym.exists)
+ devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe)
+ else if (sym.isLocalToThis)
+ varianceValidator.checkForEscape(sym, currentClass)
+
+ def checkSuper(mix: Name) =
+ // term should have been eliminated by super accessors
+ assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), (qual.symbol, sym, mix))
+
+ transformCaseApply(tree,
+ qual match {
+ case Super(_, mix) => checkSuper(mix)
+ case _ =>
+ }
+ )
+ }
+ private def transformIf(tree: If): Tree = {
+ val If(cond, thenpart, elsepart) = tree
+ def unitIfEmpty(t: Tree): Tree =
+ if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitTpe) else t
+
+ cond.tpe match {
+ case ConstantType(value) =>
+ val res = if (value.booleanValue) thenpart else elsepart
+ unitIfEmpty(res)
+ case _ => tree
+ }
+ }
+
+ // Warning about nullary methods returning Unit. TODO: move to lint
+ private def checkNullaryMethodReturnType(sym: Symbol) = sym.tpe match {
+ case NullaryMethodType(restpe) if restpe.typeSymbol == UnitClass =>
+ // this may be the implementation of e.g. a generic method being parameterized
+ // on Unit, in which case we had better let it slide.
+ val isOk = (
+ sym.isGetter
+ || (sym.name containsName nme.DEFAULT_GETTER_STRING)
+ || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType))
+ )
+ if (!isOk)
+ unit.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead")
+ case _ => ()
+ }
+
+ /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */
+ def toConstructor(pos: Position, tpe: Type)(implicit ctx: Context): Tree = {
+ val rtpe = tpe.finalResultType
+ assert(rtpe.typeSymbol.is(Case), tpe)
+ New(rtpe).withPos(pos).select(rtpe.typeSymbol.primaryConstructor)
+ }
+ private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = pat match {
+ case Apply(_, args) =>
+ val clazz = pat.tpe.typeSymbol
+ clazz == seltpe.typeSymbol &&
+ clazz.isCaseClass &&
+ (args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable)
+ case Typed(pat, tpt) =>
+ seltpe <:< tpt.tpe
+ case Ident(tpnme.WILDCARD) =>
+ true
+ case Bind(_, pat) =>
+ isIrrefutable(pat, seltpe)
+ case _ =>
+ false
+ }
+ 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
+ && !otherSym.isTypeParameterOrSkolem
+ && !otherSym.isExistentiallyBound
+ && (otherSym isLessAccessibleThan memberSym)
+ && (otherSym isLessAccessibleThan memberSym.enclClass)
+ )
+ private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = {
+ val extras = other match {
+ case TypeRef(pre, _, args) =>
+ // checking the prefix here gives us spurious errors on e.g. a private[process]
+ // object which contains a type alias, which normalizes to a visible type.
+ args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym))
+ case _ =>
+ Nil
+ }
+ if (lessAccessible(other.typeSymbol, memberSym)) other.typeSymbol :: extras
+ else extras
+ }
+ private def warnLessAccessible(otherSym: Symbol, memberSym: Symbol) {
+ val comparison = accessFlagsToString(memberSym) match {
+ case "" => ""
+ case acc => " is " + acc + " but"
+ }
+ val cannot =
+ if (memberSym.isDeferred) "may be unable to provide a concrete implementation of"
+ else "may be unable to override"
+
+ unit.warning(memberSym.pos,
+ "%s%s references %s %s.".format(
+ memberSym.fullLocationString, comparison,
+ accessFlagsToString(otherSym), otherSym
+ ) + "\nClasses which cannot access %s %s %s.".format(
+ otherSym.decodedName, cannot, memberSym.decodedName)
+ )
+ }
+
+ /** Warn about situations where a method signature will include a type which
+ * has more restrictive access than the method itself.
+ */
+ private def checkAccessibilityOfReferencedTypes(tree: Tree) {
+ val member = tree.symbol
+
+ def checkAccessibilityOfType(tpe: Type) {
+ val inaccessible = lessAccessibleSymsInType(tpe, member)
+ // if the unnormalized type is accessible, that's good enough
+ if (inaccessible.isEmpty) ()
+ // or if the normalized type is, that's good too
+ else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) ()
+ // otherwise warn about the inaccessible syms in the unnormalized type
+ else inaccessible foreach (sym => warnLessAccessible(sym, member))
+ }
+
+ // types of the value parameters
+ mapParamss(member)(p => checkAccessibilityOfType(p.tpe))
+ // upper bounds of type parameters
+ member.typeParams.map(_.info.bounds.hi.widen) foreach checkAccessibilityOfType
+ }
+
+ private def checkByNameRightAssociativeDef(tree: DefDef) {
+ tree match {
+ case DefDef(_, name, _, params :: _, _, _) =>
+ if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol)))
+ unit.warning(tree.pos,
+ "by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.")
+ case _ =>
+ }
+ }
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = {
+ //val savedLocalTyper = localTyper
+ try {
+ val sym = tree.symbol
+ checkOverloadedRestrictions(ctx.owner)
+ checkAllOverrides(ctx.owner)
+ checkAnyValSubclass(ctx.owner)
+ if (ctx.owner.isDerivedValueClass)
+ ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler!
+ tree
+
+
+ // Apply RefChecks to annotations. Makes sure the annotations conform to
+ // type bounds (bug #935), issues deprecation warnings for symbols used
+ // inside annotations.
+ // applyRefchecksToAnnotations(tree) ???
+ var result: Tree = tree match {
+ case tree: ValOrDefDef =>
+ // move to lint:
+ // if (settings.warnNullaryUnit)
+ // checkNullaryMethodReturnType(sym)
+ // if (settings.warnInaccessible) {
+ // if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic)
+ // checkAccessibilityOfReferencedTypes(tree)
+ // }
+ // tree match {
+ // case dd: DefDef => checkByNameRightAssociativeDef(dd)
+ // case _ =>
+ // }
+ tree
+
+ case Template(constr, parents, self, body) =>
+ // localTyper = localTyper.atOwner(tree, currentOwner)
+ checkOverloadedRestrictions(ctx.owner)
+ checkAllOverrides(ctx.owner)
+ checkAnyValSubclass(ctx.owner)
+ if (ctx.owner.isDerivedValueClass)
+ ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler!
+ tree
+
+ case tpt: TypeTree =>
+ transform(tpt.original)
+ tree
+
+ case TypeApply(fn, args) =>
+ checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe))
+ transformCaseApply(tree, ())
+
+ case x @ Apply(_, _) =>
+ transformApply(x)
+
+ case x @ If(_, _, _) =>
+ transformIf(x)
+
+ case New(tpt) =>
+ enterReference(tree.pos, tpt.tpe.typeSymbol)
+ tree
+
+ case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) =>
+ unit.error(tree.pos, "no `: _*' annotation allowed here\n" +
+ "(such annotations are only allowed in arguments to *-parameters)")
+ tree
+
+ case Ident(name) =>
+ checkUndesiredProperties(sym, tree.pos)
+ transformCaseApply(tree,
+ if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) {
+ assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug
+ enterReference(tree.pos, sym)
+ }
+ )
+
+ case x @ Select(_, _) =>
+ transformSelect(x)
+
+ case UnApply(fun, args) =>
+ transform(fun) // just make sure we enterReference for unapply symbols, note that super.transform(tree) would not transform(fun)
+ // transformTrees(args) // TODO: is this necessary? could there be forward references in the args??
+ // probably not, until we allow parameterised extractors
+ tree
+
+
+ case _ => tree
+ }
+
+ // skip refchecks in patterns....
+ result = result match {
+ case CaseDef(pat, guard, body) =>
+ val pat1 = savingInPattern {
+ inPattern = true
+ transform(pat)
+ }
+ treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))
+ case LabelDef(_, _, _) if treeInfo.hasSynthCaseSymbol(result) =>
+ savingInPattern {
+ inPattern = true
+ deriveLabelDef(result)(transform)
+ }
+ case Apply(fun, args) if fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol) =>
+ savingInPattern {
+ // SI-7756 If we were in a translated pattern, we can now switch out of pattern mode, as the label apply signals
+ // that we are in the user-supplied code in the case body.
+ //
+ // Relies on the translation of:
+ // (null: Any) match { case x: List[_] => x; x.reverse; case _ => }'
+ // to:
+ // <synthetic> val x2: List[_] = (x1.asInstanceOf[List[_]]: List[_]);
+ // matchEnd4({ x2; x2.reverse}) // case body is an argument to a label apply.
+ inPattern = false
+ super.transform(result)
+ }
+ case ValDef(_, _, _, _) if treeInfo.hasSynthCaseSymbol(result) =>
+ deriveValDef(result)(transform) // SI-7716 Don't refcheck the tpt of the synthetic val that holds the selector.
+ case _ =>
+ super.transform(result)
+ }
+ result match {
+ case ClassDef(_, _, _, _)
+ | TypeDef(_, _, _, _) =>
+ if (result.symbol.isLocalToBlock || result.symbol.isTopLevel)
+ varianceValidator.traverse(result)
+ case tt @ TypeTree() if tt.original != null =>
+ varianceValidator.traverse(tt.original) // See SI-7872
+ case _ =>
+ }
+
+ checkUnexpandedMacro(result)
+
+ result
+ } catch {
+ case ex: TypeError =>
+ if (settings.debug) ex.printStackTrace()
+ unit.error(tree.pos, ex.getMessage())
+ tree
+ } finally {
+ localTyper = savedLocalTyper
+ currentApplication = savedCurrentApplication
+ }
+ }
+*/
+