diff options
-rw-r--r-- | src/compiler/scala/reflect/internal/Symbols.scala | 15 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 6 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 26 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 53 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala | 26 | ||||
-rw-r--r-- | test/files/neg/abstract-report.check | 24 | ||||
-rw-r--r-- | test/files/neg/abstract-report.scala | 1 | ||||
-rw-r--r-- | test/files/neg/abstract-report2.check | 99 | ||||
-rw-r--r-- | test/files/neg/abstract-report2.scala | 11 | ||||
-rw-r--r-- | test/files/neg/t2208.check | 2 | ||||
-rw-r--r-- | test/files/neg/t2213.check | 26 | ||||
-rw-r--r-- | test/files/neg/t856.check | 12 | ||||
-rw-r--r-- | test/files/neg/tcpoly_ticket2101.check | 2 |
13 files changed, 250 insertions, 53 deletions
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index db17306768..4ded91c16c 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1900,13 +1900,20 @@ trait Symbols extends api.Symbols { self: SymbolTable => else ExplicitFlags def defaultFlagString = hasFlagsToString(defaultFlagMask) - - /** String representation of symbol's definition */ - def defString = compose( + private def defStringCompose(infoString: String) = compose( defaultFlagString, keyString, - varianceString + nameString + signatureString + varianceString + nameString + infoString ) + /** String representation of symbol's definition. It uses the + * symbol's raw info to avoid forcing types. + */ + def defString = defStringCompose(signatureString) + + /** String representation of symbol's definition, using the supplied + * info rather than the symbol's. + */ + def defStringSeenAs(info: Type) = defStringCompose(infoString(info)) /** Concatenate strings separated by spaces */ private def compose(ss: String*) = ss filter (_ != "") mkString " " 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 diff --git a/test/files/neg/abstract-report.check b/test/files/neg/abstract-report.check new file mode 100644 index 0000000000..bd550f39f4 --- /dev/null +++ b/test/files/neg/abstract-report.check @@ -0,0 +1,24 @@ +abstract-report.scala:1: error: class Unimplemented needs to be abstract, since: +it has 12 unimplemented members. +/** As seen from class Unimplemented, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + // Members declared in scala.collection.GenTraversableOnce + def isTraversableAgain: Boolean = ??? + def toIterator: Iterator[String] = ??? + def toStream: Stream[String] = ??? + + // Members declared in scala.collection.TraversableOnce + def copyToArray[B >: String](xs: Array[B],start: Int,len: Int): Unit = ??? + def exists(p: String => Boolean): Boolean = ??? + def find(p: String => Boolean): Option[String] = ??? + def forall(p: String => Boolean): Boolean = ??? + def foreach[U](f: String => U): Unit = ??? + def hasDefiniteSize: Boolean = ??? + def isEmpty: Boolean = ??? + def seq: scala.collection.TraversableOnce[String] = ??? + def toTraversable: Traversable[String] = ??? + +class Unimplemented extends TraversableOnce[String] { } + ^ +one error found diff --git a/test/files/neg/abstract-report.scala b/test/files/neg/abstract-report.scala new file mode 100644 index 0000000000..538e093547 --- /dev/null +++ b/test/files/neg/abstract-report.scala @@ -0,0 +1 @@ +class Unimplemented extends TraversableOnce[String] { }
\ No newline at end of file diff --git a/test/files/neg/abstract-report2.check b/test/files/neg/abstract-report2.check new file mode 100644 index 0000000000..32d52e00f2 --- /dev/null +++ b/test/files/neg/abstract-report2.check @@ -0,0 +1,99 @@ +abstract-report2.scala:3: error: class Foo needs to be abstract, since: +it has 12 unimplemented members. +/** As seen from class Foo, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + def add(x$1: Int): Boolean = ??? + def addAll(x$1: java.util.Collection[_ <: Int]): Boolean = ??? + def clear(): Unit = ??? + def contains(x$1: Any): Boolean = ??? + def containsAll(x$1: java.util.Collection[_]): Boolean = ??? + def isEmpty(): Boolean = ??? + def iterator(): java.util.Iterator[Int] = ??? + def remove(x$1: Any): Boolean = ??? + def removeAll(x$1: java.util.Collection[_]): Boolean = ??? + def retainAll(x$1: java.util.Collection[_]): Boolean = ??? + def size(): Int = ??? + def toArray[T](x$1: Array[T with Object]): Array[T with Object] = ??? + +class Foo extends Collection[Int] + ^ +abstract-report2.scala:5: error: class Bar needs to be abstract, since: +it has 12 unimplemented members. +/** As seen from class Bar, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + def add(x$1: List[_ <: String]): Boolean = ??? + def addAll(x$1: java.util.Collection[_ <: List[_ <: String]]): Boolean = ??? + def clear(): Unit = ??? + def contains(x$1: Any): Boolean = ??? + def containsAll(x$1: java.util.Collection[_]): Boolean = ??? + def isEmpty(): Boolean = ??? + def iterator(): java.util.Iterator[List[_ <: String]] = ??? + def remove(x$1: Any): Boolean = ??? + def removeAll(x$1: java.util.Collection[_]): Boolean = ??? + def retainAll(x$1: java.util.Collection[_]): Boolean = ??? + def size(): Int = ??? + def toArray[T](x$1: Array[T with Object]): Array[T with Object] = ??? + +class Bar extends Collection[List[_ <: String]] + ^ +abstract-report2.scala:7: error: class Baz needs to be abstract, since: +it has 12 unimplemented members. +/** As seen from class Baz, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + def add(x$1: T): Boolean = ??? + def addAll(x$1: java.util.Collection[_ <: T]): Boolean = ??? + def clear(): Unit = ??? + def contains(x$1: Any): Boolean = ??? + def containsAll(x$1: java.util.Collection[_]): Boolean = ??? + def isEmpty(): Boolean = ??? + def iterator(): java.util.Iterator[T] = ??? + def remove(x$1: Any): Boolean = ??? + def removeAll(x$1: java.util.Collection[_]): Boolean = ??? + def retainAll(x$1: java.util.Collection[_]): Boolean = ??? + def size(): Int = ??? + def toArray[T](x$1: Array[T with Object]): Array[T with Object] = ??? + +class Baz[T] extends Collection[T] + ^ +abstract-report2.scala:11: error: class Dingus needs to be abstract, since: +it has 23 unimplemented members. +/** As seen from class Dingus, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + // Members declared in java.util.Collection + def add(x$1: String): Boolean = ??? + def addAll(x$1: java.util.Collection[_ <: String]): Boolean = ??? + def clear(): Unit = ??? + def contains(x$1: Any): Boolean = ??? + def containsAll(x$1: java.util.Collection[_]): Boolean = ??? + def iterator(): java.util.Iterator[String] = ??? + def remove(x$1: Any): Boolean = ??? + def removeAll(x$1: java.util.Collection[_]): Boolean = ??? + def retainAll(x$1: java.util.Collection[_]): Boolean = ??? + def toArray[T](x$1: Array[T with Object]): Array[T with Object] = ??? + + // Members declared in scala.collection.GenTraversableOnce + def isTraversableAgain: Boolean = ??? + def toIterator: Iterator[(Set[Int], String)] = ??? + def toStream: Stream[(Set[Int], String)] = ??? + + // Members declared in scala.math.Ordering + def compare(x: List[Int],y: List[Int]): Int = ??? + + // Members declared in scala.collection.TraversableOnce + def copyToArray[B >: (Set[Int], String)](xs: Array[B],start: Int,len: Int): Unit = ??? + def exists(p: ((Set[Int], String)) => Boolean): Boolean = ??? + def find(p: ((Set[Int], String)) => Boolean): Option[(Set[Int], String)] = ??? + def forall(p: ((Set[Int], String)) => Boolean): Boolean = ??? + def foreach[U](f: ((Set[Int], String)) => U): Unit = ??? + def hasDefiniteSize: Boolean = ??? + def isEmpty: Boolean = ??? + def seq: scala.collection.TraversableOnce[(Set[Int], String)] = ??? + def toTraversable: Traversable[(Set[Int], String)] = ??? + +class Dingus extends Bippy[String, Set[Int], List[Int]] + ^ +four errors found diff --git a/test/files/neg/abstract-report2.scala b/test/files/neg/abstract-report2.scala new file mode 100644 index 0000000000..b6327b0766 --- /dev/null +++ b/test/files/neg/abstract-report2.scala @@ -0,0 +1,11 @@ +import java.util.Collection + +class Foo extends Collection[Int] + +class Bar extends Collection[List[_ <: String]] + +class Baz[T] extends Collection[T] + +trait Bippy[T1, T2, T3] extends Collection[T1] with TraversableOnce[(T2, String)] with Ordering[T3] + +class Dingus extends Bippy[String, Set[Int], List[Int]]
\ No newline at end of file diff --git a/test/files/neg/t2208.check b/test/files/neg/t2208.check index a97b20cba7..64bb3a77c8 100644 --- a/test/files/neg/t2208.check +++ b/test/files/neg/t2208.check @@ -1,4 +1,4 @@ t2208.scala:7: error: type arguments [Any] do not conform to type Alias's type parameter bounds [X <: Test.A] class C extends Alias[Any] // not ok, normalisation should check bounds before expanding Alias ^ -one error found
\ No newline at end of file +one error found diff --git a/test/files/neg/t2213.check b/test/files/neg/t2213.check index f59503ee2a..9fb3bb2eb7 100644 --- a/test/files/neg/t2213.check +++ b/test/files/neg/t2213.check @@ -1,15 +1,25 @@ t2213.scala:9: error: class C needs to be abstract, since: -value y in class A of type Int is not defined -value x in class A of type Int is not defined -method g in class A of type => Int is not defined -method f in class A of type => Int is not defined +it has 4 unimplemented members. +/** As seen from class C, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + def f: Int = ??? + def g: Int = ??? + val x: Int = ??? + val y: Int = ??? + class C extends A {} ^ t2213.scala:11: error: object creation impossible, since: -value y in class A of type Int is not defined -value x in class A of type Int is not defined -method g in class A of type => Int is not defined -method f in class A of type => Int is not defined +it has 4 unimplemented members. +/** As seen from object Q, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + def f: Int = ??? + def g: Int = ??? + val x: Int = ??? + val y: Int = ??? + object Q extends A { } ^ two errors found diff --git a/test/files/neg/t856.check b/test/files/neg/t856.check index d0bbde6c58..02978e1622 100644 --- a/test/files/neg/t856.check +++ b/test/files/neg/t856.check @@ -1,6 +1,14 @@ t856.scala:3: error: class ComplexRect needs to be abstract, since: -method _2 in trait Product2 of type => Double is not defined -method canEqual in trait Equals of type (that: Any)Boolean is not defined +it has 2 unimplemented members. +/** As seen from class ComplexRect, the missing signatures are as follows. + * For convenience, these are usable as stub implementations. + */ + // Members declared in scala.Equals + def canEqual(that: Any): Boolean = ??? + + // Members declared in scala.Product2 + def _2: Double = ??? + class ComplexRect(val _1:Double, _2:Double) extends Complex { ^ one error found diff --git a/test/files/neg/tcpoly_ticket2101.check b/test/files/neg/tcpoly_ticket2101.check index eac582e8ba..ad0fd8bda2 100644 --- a/test/files/neg/tcpoly_ticket2101.check +++ b/test/files/neg/tcpoly_ticket2101.check @@ -1,4 +1,4 @@ tcpoly_ticket2101.scala:2: error: type arguments [T2,X] do not conform to class T's type parameter bounds [A[Y] <: T[A,B],B] class T2[X] extends T[T2, X] // ill-typed ^ -one error found
\ No newline at end of file +one error found |