diff options
author | Paul Phillips <paulp@improving.org> | 2011-04-28 16:23:45 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-04-28 16:23:45 +0000 |
commit | 199ec3c10fe7d2b2029ea8ae6a19240b46181435 (patch) | |
tree | f0e7b14111510f19bf89874d3073adf767d16054 /src | |
parent | 9a9f73b80261415e08eb782333470bbb84863894 (diff) | |
download | scala-199ec3c10fe7d2b2029ea8ae6a19240b46181435.tar.gz scala-199ec3c10fe7d2b2029ea8ae6a19240b46181435.tar.bz2 scala-199ec3c10fe7d2b2029ea8ae6a19240b46181435.zip |
Improved the error message for another of the m...
Improved the error message for another of the most common situations
I hear about in newbieland. It could be taken further. If compilation
fails due to an unimplemented abstract method, and there is a concrete
method of the same name and arity, it will do a pairwise analysis of the
parameters and attempt to further explain where you went off the beam if
it feels it can do so sensibly. Such as in the test case:
% scalac S.scala
S.scala:1: error: class S needs to be abstract, since method g in class J of type (y: Int,z: java.util.List)Int is not defined
(Note that java.util.List does not match java.util.List[String]. To implement a raw type, use java.util.List[_])
class S extends J {
^
one error found
No review.
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 3eb5107870..a23e5d704e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -507,6 +507,48 @@ abstract class RefChecks extends InfoTransform { else analyzer.varNotice(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. + val abstractParams = underlying.tpe.paramTypes + val matchingName = clazz.tpe.nonPrivateMembersAdmitting(VBRIDGE) + val matchingArity = matchingName filter { m => + !m.isDeferred && + (m.name == underlying.name) && + (m.tpe.paramTypes.size == underlying.tpe.paramTypes.size) && + (m.tpe.typeParams.size == underlying.tpe.typeParams.size) + } + + matchingArity match { + // So far so good: only one candidate method + case concrete :: Nil => + val mismatches = abstractParams zip concrete.tpe.paramTypes filterNot { case (x, y) => x =:= y } + mismatches match { + // Only one mismatched parameter: say something useful. + case (pa, pc) :: Nil => + val addendum = + if (pa.typeSymbol == pc.typeSymbol) { + // TODO: what is the optimal way to test for a raw type at this point? + // Compilation has already failed so we shouldn't have to worry overmuch + // about forcing types. + if (underlying.isJavaDefined && pa.typeArgs.isEmpty && pa.typeSymbol.typeParams.nonEmpty) + ". To implement a raw type, use %s[_]".format(pa) + else if (pa.prefix =:= pc.prefix) + ": their type parameters differ" + else + ": their prefixes (i.e. enclosing instances) differ" + } + else "" + + undefined("\n(Note that %s does not match %s%s)".format(pa, pc, addendum)) + case xs => + undefined("") + } + case _ => + undefined("") + } + } else undefined("") } |