diff options
author | Adriaan Moors <adriaan@lightbend.com> | 2017-03-02 12:05:31 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-02 12:05:31 -0800 |
commit | 96a7617eb6c90f8891eb39f20a7f41a745a8e195 (patch) | |
tree | eb27529aa499f3e1f09982fdcbd286e2abdb4a2b /src/compiler/scala | |
parent | f2e05c2276cad276f1cc8b2cad393a1acee6ede8 (diff) | |
parent | 466e52ba58604720b2dee35358d6b3545981b5b5 (diff) | |
download | scala-96a7617eb6c90f8891eb39f20a7f41a745a8e195.tar.gz scala-96a7617eb6c90f8891eb39f20a7f41a745a8e195.tar.bz2 scala-96a7617eb6c90f8891eb39f20a7f41a745a8e195.zip |
Merge pull request #5622 from edmundnoble/extra-errs
Improved error messages for identically named, differently prefixed types
Diffstat (limited to 'src/compiler/scala')
3 files changed, 46 insertions, 24 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 503f64a44f..7a3b8d2ab6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -62,7 +62,7 @@ trait Contexts { self: Analyzer => def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) { for (imps <- allImportInfos.remove(unit)) { - for (imp <- imps.reverse.distinct) { + for (imp <- imps.distinct.reverse) { val used = allUsedSelectors(imp) imp.tree.selectors filterNot (s => isMaskImport(s) || used(s)) foreach { sel => diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 990edcd86d..50743a922a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -112,7 +112,7 @@ abstract class TreeCheckers extends Analyzer { else if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) () else { - val s1 = (prevTrees map wholetreestr).sorted.distinct + val s1 = (prevTrees map wholetreestr).distinct.sorted val s2 = wholetreestr(tree) if (s1 contains s2) () else movedMsgs += ("\n** %s moved:\n** Previously:\n%s\n** Currently:\n%s".format(ownerstr(sym), s1 mkString ", ", s2)) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index b66dbf21c0..36b9a65334 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -11,6 +11,7 @@ import scala.collection.mutable.ListBuffer import scala.util.control.Exception.ultimately import symtab.Flags._ import PartialFunction._ +import scala.annotation.tailrec /** An interface to enable higher configurability of diagnostic messages * regarding type errors. This is barely a beginning as error messages are @@ -274,19 +275,54 @@ trait TypeDiagnostics { if (AnyRefTpe <:< req) notAnyRefMessage(found) else "" } + def finalOwners(tpe: Type): Boolean = (tpe.prefix == NoPrefix) || recursivelyFinal(tpe) + + @tailrec + final def recursivelyFinal(tpe: Type): Boolean = { + val prefix = tpe.prefix + if (prefix != NoPrefix) { + if (prefix.typeSymbol.isFinal) { + recursivelyFinal(prefix) + } else { + false + } + } else { + true + } + } + // TODO - figure out how to avoid doing any work at all // when the message will never be seen. I though context.reportErrors // being false would do that, but if I return "<suppressed>" under // that condition, I see it. def foundReqMsg(found: Type, req: Type): String = { - def baseMessage = ( - ";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) + - "\n required: " + req + existentialContext(req) + explainAlias(req) - ) - ( withDisambiguation(Nil, found, req)(baseMessage) - + explainVariance(found, req) - + explainAnyVsAnyRef(found, req) - ) + val foundWiden = found.widen + val reqWiden = req.widen + val sameNamesDifferentPrefixes = + foundWiden.typeSymbol.name == reqWiden.typeSymbol.name && + foundWiden.prefix.typeSymbol != reqWiden.prefix.typeSymbol + val easilyMistakable = + sameNamesDifferentPrefixes && + !req.typeSymbol.isConstant && + finalOwners(foundWiden) && finalOwners(reqWiden) && + !found.typeSymbol.isTypeParameterOrSkolem && !req.typeSymbol.isTypeParameterOrSkolem + + if (easilyMistakable) { + val longestNameLength = foundWiden.nameAndArgsString.length max reqWiden.nameAndArgsString.length + val paddedFoundName = foundWiden.nameAndArgsString.padTo(longestNameLength, ' ') + val paddedReqName = reqWiden.nameAndArgsString.padTo(longestNameLength, ' ') + ";\n found : " + (paddedFoundName + s" (in ${found.prefix.typeSymbol.fullNameString}) ") + explainAlias(found) + + "\n required: " + (paddedReqName + s" (in ${req.prefix.typeSymbol.fullNameString}) ") + explainAlias(req) + } else { + def baseMessage = { + ";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) + + "\n required: " + req + existentialContext(req) + explainAlias(req) + } + (withDisambiguation(Nil, found, req)(baseMessage) + + explainVariance(found, req) + + explainAnyVsAnyRef(found, req) + ) + } } def typePatternAdvice(sym: Symbol, ptSym: Symbol) = { @@ -315,14 +351,6 @@ trait TypeDiagnostics { def restoreName() = sym.name = savedName def modifyName(f: String => String) = sym setName newTypeName(f(sym.name.toString)) - /** Prepend java.lang, scala., or Predef. if this type originated - * in one of those. - */ - def qualifyDefaultNamespaces() = { - val intersect = Set(trueOwner, aliasOwner) intersect UnqualifiedOwners - if (intersect.nonEmpty && tp.typeSymbolDirect.name == tp.typeSymbol.name) preQualify() - } - // functions to manipulate the name def preQualify() = modifyName(trueOwner.fullName + "." + _) def postQualify() = if (!(postQualifiedWith contains trueOwner)) { postQualifiedWith ::= trueOwner; modifyName(_ + "(in " + trueOwner + ")") } @@ -414,12 +442,6 @@ trait TypeDiagnostics { if (td1 string_== td2) tds foreach (_.nameQualify()) - // If they have the same simple name, and either of them is in the - // scala package or predef, qualify with scala so it is not confusing why - // e.g. java.util.Iterator and Iterator are different types. - if (td1 name_== td2) - tds foreach (_.qualifyDefaultNamespaces()) - // If they still print identically: // a) If they are type parameters with different owners, append (in <owner>) // b) Failing that, the best we can do is append "(some other)" to the latter. |