Skip to content

Commit 96a7617

Browse files
authored
Merge pull request scala#5622 from edmundnoble/extra-errs
Improved error messages for identically named, differently prefixed types
2 parents f2e05c2 + 466e52b commit 96a7617

File tree

7 files changed

+74
-39
lines changed

7 files changed

+74
-39
lines changed

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ trait Contexts { self: Analyzer =>
6262

6363
def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) {
6464
for (imps <- allImportInfos.remove(unit)) {
65-
for (imp <- imps.reverse.distinct) {
65+
for (imp <- imps.distinct.reverse) {
6666
val used = allUsedSelectors(imp)
6767

6868
imp.tree.selectors filterNot (s => isMaskImport(s) || used(s)) foreach { sel =>

src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ abstract class TreeCheckers extends Analyzer {
112112
else if (prevTrees exists (t => (t eq tree) || (t.symbol == sym)))
113113
()
114114
else {
115-
val s1 = (prevTrees map wholetreestr).sorted.distinct
115+
val s1 = (prevTrees map wholetreestr).distinct.sorted
116116
val s2 = wholetreestr(tree)
117117
if (s1 contains s2) ()
118118
else movedMsgs += ("\n** %s moved:\n** Previously:\n%s\n** Currently:\n%s".format(ownerstr(sym), s1 mkString ", ", s2))

src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import scala.collection.mutable.ListBuffer
1111
import scala.util.control.Exception.ultimately
1212
import symtab.Flags._
1313
import PartialFunction._
14+
import scala.annotation.tailrec
1415

1516
/** An interface to enable higher configurability of diagnostic messages
1617
* regarding type errors. This is barely a beginning as error messages are
@@ -274,19 +275,54 @@ trait TypeDiagnostics {
274275
if (AnyRefTpe <:< req) notAnyRefMessage(found) else ""
275276
}
276277

278+
def finalOwners(tpe: Type): Boolean = (tpe.prefix == NoPrefix) || recursivelyFinal(tpe)
279+
280+
@tailrec
281+
final def recursivelyFinal(tpe: Type): Boolean = {
282+
val prefix = tpe.prefix
283+
if (prefix != NoPrefix) {
284+
if (prefix.typeSymbol.isFinal) {
285+
recursivelyFinal(prefix)
286+
} else {
287+
false
288+
}
289+
} else {
290+
true
291+
}
292+
}
293+
277294
// TODO - figure out how to avoid doing any work at all
278295
// when the message will never be seen. I though context.reportErrors
279296
// being false would do that, but if I return "<suppressed>" under
280297
// that condition, I see it.
281298
def foundReqMsg(found: Type, req: Type): String = {
282-
def baseMessage = (
283-
";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) +
284-
"\n required: " + req + existentialContext(req) + explainAlias(req)
285-
)
286-
( withDisambiguation(Nil, found, req)(baseMessage)
287-
+ explainVariance(found, req)
288-
+ explainAnyVsAnyRef(found, req)
289-
)
299+
val foundWiden = found.widen
300+
val reqWiden = req.widen
301+
val sameNamesDifferentPrefixes =
302+
foundWiden.typeSymbol.name == reqWiden.typeSymbol.name &&
303+
foundWiden.prefix.typeSymbol != reqWiden.prefix.typeSymbol
304+
val easilyMistakable =
305+
sameNamesDifferentPrefixes &&
306+
!req.typeSymbol.isConstant &&
307+
finalOwners(foundWiden) && finalOwners(reqWiden) &&
308+
!found.typeSymbol.isTypeParameterOrSkolem && !req.typeSymbol.isTypeParameterOrSkolem
309+
310+
if (easilyMistakable) {
311+
val longestNameLength = foundWiden.nameAndArgsString.length max reqWiden.nameAndArgsString.length
312+
val paddedFoundName = foundWiden.nameAndArgsString.padTo(longestNameLength, ' ')
313+
val paddedReqName = reqWiden.nameAndArgsString.padTo(longestNameLength, ' ')
314+
";\n found : " + (paddedFoundName + s" (in ${found.prefix.typeSymbol.fullNameString}) ") + explainAlias(found) +
315+
"\n required: " + (paddedReqName + s" (in ${req.prefix.typeSymbol.fullNameString}) ") + explainAlias(req)
316+
} else {
317+
def baseMessage = {
318+
";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) +
319+
"\n required: " + req + existentialContext(req) + explainAlias(req)
320+
}
321+
(withDisambiguation(Nil, found, req)(baseMessage)
322+
+ explainVariance(found, req)
323+
+ explainAnyVsAnyRef(found, req)
324+
)
325+
}
290326
}
291327

292328
def typePatternAdvice(sym: Symbol, ptSym: Symbol) = {
@@ -315,14 +351,6 @@ trait TypeDiagnostics {
315351
def restoreName() = sym.name = savedName
316352
def modifyName(f: String => String) = sym setName newTypeName(f(sym.name.toString))
317353

318-
/** Prepend java.lang, scala., or Predef. if this type originated
319-
* in one of those.
320-
*/
321-
def qualifyDefaultNamespaces() = {
322-
val intersect = Set(trueOwner, aliasOwner) intersect UnqualifiedOwners
323-
if (intersect.nonEmpty && tp.typeSymbolDirect.name == tp.typeSymbol.name) preQualify()
324-
}
325-
326354
// functions to manipulate the name
327355
def preQualify() = modifyName(trueOwner.fullName + "." + _)
328356
def postQualify() = if (!(postQualifiedWith contains trueOwner)) { postQualifiedWith ::= trueOwner; modifyName(_ + "(in " + trueOwner + ")") }
@@ -414,12 +442,6 @@ trait TypeDiagnostics {
414442
if (td1 string_== td2)
415443
tds foreach (_.nameQualify())
416444

417-
// If they have the same simple name, and either of them is in the
418-
// scala package or predef, qualify with scala so it is not confusing why
419-
// e.g. java.util.Iterator and Iterator are different types.
420-
if (td1 name_== td2)
421-
tds foreach (_.qualifyDefaultNamespaces())
422-
423445
// If they still print identically:
424446
// a) If they are type parameters with different owners, append (in <owner>)
425447
// b) Failing that, the best we can do is append "(some other)" to the latter.

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,8 @@ trait Types
967967
*/
968968
def directObjectString = safeToString
969969

970+
def nameAndArgsString = typeSymbol.name.toString
971+
970972
/** A test whether a type contains any unification type variables.
971973
* Overridden with custom logic except where trivially true.
972974
*/
@@ -2321,6 +2323,8 @@ trait Types
23212323
private def preString = if (needsPreString) pre.prefixString else ""
23222324
private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]")
23232325

2326+
override def nameAndArgsString = typeSymbol.name.toString + argsString
2327+
23242328
private def refinementDecls = fullyInitializeScope(decls) filter (sym => sym.isPossibleInRefinement && sym.isPublic)
23252329
private def refinementString = (
23262330
if (sym.isStructuralRefinement)
@@ -2728,6 +2732,19 @@ trait Types
27282732
arg.toString
27292733
}
27302734

2735+
override def nameAndArgsString: String = underlying match {
2736+
case TypeRef(_, sym, args) if !settings.debug && isRepresentableWithWildcards =>
2737+
sym.name + wildcardArgsString(quantified.toSet, args).mkString("[", ",", "]")
2738+
case TypeRef(_, sym, args) =>
2739+
sym.name + args.mkString("[", ",", "]") + existentialClauses
2740+
case _ => underlying.typeSymbol.name + existentialClauses
2741+
}
2742+
2743+
private def existentialClauses = {
2744+
val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }")
2745+
if (settings.explaintypes) "(" + str + ")" else str
2746+
}
2747+
27312748
/** An existential can only be printed with wildcards if:
27322749
* - the underlying type is a typeref
27332750
* - every quantified variable appears at most once as a type argument and
@@ -2746,7 +2763,7 @@ trait Types
27462763
tpe.typeSymbol.isRefinementClass && (tpe.parents exists isQuantified)
27472764
}
27482765
val (wildcardArgs, otherArgs) = args partition (arg => qset contains arg.typeSymbol)
2749-
wildcardArgs.distinct == wildcardArgs &&
2766+
wildcardArgs.toSet.size == wildcardArgs.size &&
27502767
!(otherArgs exists (arg => isQuantified(arg))) &&
27512768
!(wildcardArgs exists (arg => isQuantified(arg.typeSymbol.info.bounds))) &&
27522769
!(qset contains sym) &&
@@ -2756,17 +2773,13 @@ trait Types
27562773
}
27572774

27582775
override def safeToString: String = {
2759-
def clauses = {
2760-
val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }")
2761-
if (settings.explaintypes) "(" + str + ")" else str
2762-
}
27632776
underlying match {
27642777
case TypeRef(pre, sym, args) if !settings.debug && isRepresentableWithWildcards =>
27652778
"" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]")
27662779
case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) =>
2767-
"(" + underlying + ")" + clauses
2780+
"(" + underlying + ")" + existentialClauses
27682781
case _ =>
2769-
"" + underlying + clauses
2782+
"" + underlying + existentialClauses
27702783
}
27712784
}
27722785

test/files/neg/no-predef.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
no-predef.scala:2: error: type mismatch;
2-
found : scala.Long(5L)
3-
required: java.lang.Long
2+
found : Long (in scala)
3+
required: Long (in java.lang)
44
def f1 = 5L: java.lang.Long
55
^
66
no-predef.scala:3: error: type mismatch;
7-
found : java.lang.Long
8-
required: scala.Long
7+
found : Long (in java.lang)
8+
required: Long (in scala)
99
def f2 = new java.lang.Long(5) : Long
1010
^
1111
no-predef.scala:4: error: value map is not a member of String

test/files/neg/t2102.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
t2102.scala:2: error: type mismatch;
2-
found : java.util.Iterator[Int]
3-
required: scala.collection.Iterator[_]
2+
found : Iterator[Int] (in java.util)
3+
required: Iterator[_] (in scala.collection)
44
val x: Iterator[_] = new java.util.ArrayList[Int]().iterator
55
^
66
one error found

test/files/neg/type-diagnostics.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
type-diagnostics.scala:4: error: type mismatch;
2-
found : scala.collection.Set[String]
3-
required: scala.collection.immutable.Set[String]
2+
found : Set[String] (in scala.collection)
3+
required: Set[String] (in scala.collection.immutable)
44
def f = Calculator("Hello", binding.keySet: collection.Set[String])
55
^
66
type-diagnostics.scala:13: error: type mismatch;

0 commit comments

Comments
 (0)