diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
4 files changed, 74 insertions, 37 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 354eb52913..f2a0091ee1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -293,7 +293,7 @@ trait Infer { else ptBlock("because of an internal error (no accessible symbol)", "sym.ownerChain" -> sym.ownerChain, - "underlying(sym)" -> underlying(sym), + "underlyingSymbol(sym)" -> underlyingSymbol(sym), "pre" -> pre, "site" -> site, "tree" -> tree, @@ -312,7 +312,7 @@ trait Infer { } catch { case ex: MalformedType => if (settings.debug.value) ex.printStackTrace - val sym2 = underlying(sym1) + val sym2 = underlyingSymbol(sym1) val itype = pre.memberType(sym2) new AccessError(tree, sym, pre, "\n because its instance type "+itype+ @@ -1870,7 +1870,7 @@ trait Infer { // @PP: It is improbable this logic shouldn't be in use elsewhere as well. private def location = if (sym.isClassConstructor) context.enclClass.owner else pre.widen def emit(): Tree = { - val realsym = underlying(sym) + val realsym = underlyingSymbol(sym) errorTree(tree, realsym.fullLocationString + " cannot be accessed in " + location + explanation) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index b25c52b324..8dd23d14f3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1403,7 +1403,7 @@ trait Namers { self: Analyzer => !context.tree.isInstanceOf[ExistentialTypeTree] && (!sym.owner.isClass || sym.owner.isModuleClass || sym.owner.isAnonymousClass)) { context.error(sym.pos, - "only classes can have declared but undefined members" + varNotice(sym)) + "only classes can have declared but undefined members" + abstractVarMessage(sym)) sym.resetFlag(DEFERRED) } } @@ -1455,21 +1455,8 @@ trait Namers { self: Analyzer => } } - /** The symbol that which this accessor represents (possibly in part). - * This is used for error messages, where we want to speak in terms - * of the actual declaration or definition, not in terms of the generated setters - * and getters */ - def underlying(member: Symbol): Symbol = - if (member.hasAccessorFlag) { - if (member.isDeferred) { - val getter = if (member.isSetter) member.getter(member.owner) else member - val result = getter.owner.newValue(getter.pos, getter.name.toTermName) - .setInfo(getter.tpe.resultType) - .setFlag(DEFERRED) - if (getter.setter(member.owner) != NoSymbol) result.setFlag(MUTABLE) - result - } else member.accessed - } else member + @deprecated("Use underlyingSymbol instead", "2.10.0") + def underlying(member: Symbol): Symbol = underlyingSymbol(member) /** * Finds the companion module of a class symbol. Calling .companionModule @@ -1506,12 +1493,5 @@ trait Namers { self: Analyzer => if (sym.isTerm) companionClassOf(sym, context) else if (sym.isClass) companionModuleOf(sym, context) else NoSymbol - - /** An explanatory note to be added to error messages - * when there's a problem with abstract var defs */ - def varNotice(sym: Symbol): String = - if (underlying(sym).isVariable) - "\n(Note that variables need to be initialized to be defined)" - else "" } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index ca90bb8e6b..c9237627e7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -247,7 +247,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def infoStringWithLocation(sym: Symbol) = infoString0(sym, true) def infoString0(sym: Symbol, showLocation: Boolean) = { - val sym1 = analyzer.underlying(sym) + val sym1 = analyzer.underlyingSymbol(sym) sym1.toString() + (if (showLocation) sym1.locationString + @@ -516,32 +516,63 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R ) // 2. Check that only abstract classes have deferred members - def checkNoAbstractMembers() = { + def checkNoAbstractMembers(): Unit = { // Avoid spurious duplicates: first gather any missing members. - def memberList = clazz.tpe.nonPrivateMembersAdmitting(VBRIDGE) + def memberList = clazz.info.nonPrivateMembersAdmitting(VBRIDGE) val (missing, rest) = memberList partition (m => m.isDeferred && !ignoreDeferred(m)) - // Group missing members by the underlying symbol. - val grouped = missing groupBy (analyzer underlying _ name) + // Group missing members by the name of the underlying symbol, + // to consolidate getters and setters. + val grouped = missing groupBy (sym => analyzer.underlyingSymbol(sym).name) + val missingMethods = grouped.toList map { + case (name, sym :: Nil) => sym + case (name, syms) => syms.sortBy(!_.isGetter).head + } + + def stubImplementations: List[String] = { + // Grouping missing methods by the declaring class + val regrouped = missingMethods.groupBy(_.owner).toList + def membersStrings(members: List[Symbol]) = + members.sortBy("" + _.name) map (m => m.defStringSeenAs(clazz.tpe memberType m) + " = ???") + + 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) { def undefined(msg: String) = abstractClassError(false, infoString(member) + " is not defined" + msg) - val underlying = analyzer.underlying(member) + val underlying = analyzer.underlyingSymbol(member) // 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.isVariable) { + val isMultiple = grouped.getOrElse(underlying.name, Nil).size > 1 + // If both getter and setter are missing, squelch the setter error. - val isMultiple = grouped(underlying.name).size > 1 - // TODO: messages shouldn't be spread over two files, and varNotice is not a clear name if (member.isSetter && isMultiple) () else undefined( if (member.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)" else if (member.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)" - else analyzer.varNotice(member) + else analyzer.abstractVarMessage(member) ) } else if (underlying.isMethod) { - // 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. @@ -622,7 +653,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R val impl = decl.matchingSymbol(clazz.thisType, admit = VBRIDGE) if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) { abstractClassError(false, "there is a deferred declaration of "+infoString(decl)+ - " which is not implemented in a subclass"+analyzer.varNotice(decl)) + " which is not implemented in a subclass"+analyzer.abstractVarMessage(decl)) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 6c735a2d44..42f8188ac1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -123,6 +123,14 @@ trait TypeDiagnostics { else nameString + " is not a member of " + targetKindString + target + addendum ) } + + /** An explanatory note to be added to error messages + * when there's a problem with abstract var defs */ + def abstractVarMessage(sym: Symbol): String = + if (underlyingSymbol(sym).isVariable) + "\n(Note that variables need to be initialized to be defined)" + else "" + def notAMemberError(pos: Position, qual: Tree, name: Name) = context.error(pos, notAMemberMessage(pos, qual, name)) @@ -161,6 +169,24 @@ trait TypeDiagnostics { "missing parameter type" + suffix } + /** The symbol which the given accessor represents (possibly in part). + * This is used for error messages, where we want to speak in terms + * of the actual declaration or definition, not in terms of the generated setters + * and getters. + */ + def underlyingSymbol(member: Symbol): Symbol = + if (!member.hasAccessorFlag) member + else if (!member.isDeferred) member.accessed + else { + val getter = if (member.isSetter) member.getter(member.owner) else member + val flags = if (getter.setter(member.owner) != NoSymbol) DEFERRED | MUTABLE else DEFERRED + + ( getter.owner.newValue(getter.pos, getter.name.toTermName) + setInfo getter.tpe.resultType + setFlag flags + ) + } + def treeSymTypeMsg(tree: Tree): String = { val sym = tree.symbol def hasParams = tree.tpe.paramSectionCount > 0 |