diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 166cfd6c7b21..5f5cbc5625bc 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -57,6 +57,7 @@ public enum ErrorMessageID { CyclicReferenceInvolvingImplicitID, SuperQualMustBeParentID, AmbiguousImportID, + AmbiguousOverloadID, ReassignmentToValID, ; diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index d93f2c7299ec..3d9cde9235ae 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -18,6 +18,7 @@ import dotc.parsing.Tokens import printing.Highlighting._ import printing.Formatting import ErrorMessageID._ +import Denotations.SingleDenotation import dotty.tools.dotc.core.SymDenotations.SymDenotation object messages { @@ -1193,7 +1194,6 @@ object messages { extends Message(SuperQualMustBeParentID) { val msg = hl"""|$qual does not name a parent of $cls""" - val kind = "Reference" private val parents: Seq[String] = (cls.info.parents map (_.name.show)).sorted @@ -1208,15 +1208,13 @@ object messages { } case class VarArgsParamMustComeLast()(implicit ctx: Context) - extends Message(IncorrectRepeatedParameterSyntaxID) { - override def msg: String = "varargs parameter must come last" - - override def kind: String = "Syntax" - - override def explanation: String = + extends Message(IncorrectRepeatedParameterSyntaxID) { + val msg = "varargs parameter must come last" + val kind = "Syntax" + val explanation = hl"""|The varargs field must be the last field in the method signature. |Attempting to define a field in a method signature after a varargs field is an error. - |""".stripMargin + |""" } case class AmbiguousImport(name: Names.Name, newPrec: Int, prevPrec: Int, prevCtx: Context)(implicit ctx: Context) @@ -1253,6 +1251,25 @@ object messages { |""" } + case class AmbiguousOverload(tree: tpd.Tree, alts: List[SingleDenotation], pt: Type)( + err: typer.ErrorReporting.Errors)( + implicit ctx: Context) + extends Message(AmbiguousOverloadID) { + + private val all = if (alts.length == 2) "both" else "all" + val msg = + s"""|Ambiguous overload. The ${err.overloadedAltsStr(alts)} + |$all match ${err.expectedTypeStr(pt)}""".stripMargin + val kind = "Reference" + val explanation = + hl"""|There are ${alts.length} methods that could be referenced as the compiler knows too little + |about the expected type. + |You may specify the expected type e.g. by + |- assigning it to a value with a specified type, or + |- adding a type ascription as in `${"instance.myMethod: String => Int"}` + |""" + } + case class ReassignmentToVal(name: Names.Name)(implicit ctx: Context) extends Message(ReassignmentToValID) { val kind = "Reference" diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 557e9b3e41d8..6c481e5e462e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1791,7 +1791,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%, %") val alts = altDenots map (alt => TermRef.withSigAndDenot(ref.prefix, ref.name, alt.info.signature, alt)) - def expectedStr = err.expectedTypeStr(pt) resolveOverloaded(alts, pt) match { case alt :: Nil => adapt(tree.withType(alt), pt, original) @@ -1799,7 +1798,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def noMatches = errorTree(tree, em"""none of the ${err.overloadedAltsStr(altDenots)} - |match $expectedStr""") + |match ${err.expectedTypeStr(pt)}""") def hasEmptyParams(denot: SingleDenotation) = denot.info.paramInfoss == ListOfNil pt match { case pt: FunProto => @@ -1812,10 +1811,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } case alts => val remainingDenots = alts map (_.denot.asInstanceOf[SingleDenotation]) - def all = if (remainingDenots.length == 2) "both" else "all" - errorTree(tree, - em"""Ambiguous overload. The ${err.overloadedAltsStr(remainingDenots)} - |$all match $expectedStr""") + errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt)(err)) } } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index 53043ab57206..20c5e2905f06 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -4,6 +4,7 @@ package reporting import core.Contexts.Context import diagnostic.messages._ +import dotty.tools.dotc.core.Types.WildcardType import dotty.tools.dotc.parsing.Tokens import org.junit.Assert._ import org.junit.{Ignore, Test} @@ -359,6 +360,31 @@ class ErrorMessagesTests extends ErrorMessagesTest { assertEquals(namedImport, prevPrec) } + @Test def ambiugousOverloadWithWildcard = + checkMessagesAfter("frontend") { + """object Context { + | trait A { + | def foo(s: String): String + | def foo: String = foo("foo") + | } + | object B extends A { + | def foo(s: String): String = s + | } + | B.foo + |} + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + val defn = ictx.definitions + + assertMessageCount(1, messages) + val AmbiguousOverload(tree, List(alt1, alt2), pt: WildcardType) :: Nil = messages + assertEquals("method foo", alt1.show) + assertEquals("(s: String)String", alt1.info.show) + assertEquals("method foo", alt2.show) + } + @Test def reassignmentToVal = checkMessagesAfter("frontend") { """ @@ -368,10 +394,10 @@ class ErrorMessagesTests extends ErrorMessagesTest { |} """.stripMargin } - .expect { (ictx, messages) => - implicit val ctx: Context = ictx - assertMessageCount(1, messages) - val ReassignmentToVal(name) :: Nil = messages - assertEquals("value", name.show) - } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + assertMessageCount(1, messages) + val ReassignmentToVal(name) :: Nil = messages + assertEquals("value", name.show) + } }