diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 09ca64e7fa8e..d4920b5665a8 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -107,7 +107,9 @@ public enum ErrorMessageID { TailrecNotApplicableID, FailureToEliminateExistentialID, OnlyFunctionsCanBeFollowedByUnderscoreID, - MissingEmptyArgumentListID + MissingEmptyArgumentListID, + DuplicateNamedTypeParameterID, + UndefinedNamedTypeParameterID ; public int errorNumber() { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 74ace0a629f2..4305f9594b68 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1865,4 +1865,18 @@ object messages { |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" } } + + case class DuplicateNamedTypeParameter(name: Name)(implicit ctx: Context) + extends Message(DuplicateNamedTypeParameterID) { + val kind = "Syntax" + val msg = hl"Type parameter $name was defined multiple times." + val explanation = "" + } + + case class UndefinedNamedTypeParameter(undefinedName: Name, definedNames: List[Name])(implicit ctx: Context) + extends Message(UndefinedNamedTypeParameterID) { + val kind = "Syntax" + val msg = hl"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." + val explanation = "" + } } diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 45c8a422b855..338f0c2da0bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -371,9 +371,9 @@ trait TypeAssigner { val namedArgMap = new mutable.HashMap[Name, Type] for (NamedArg(name, arg) <- args) if (namedArgMap.contains(name)) - ctx.error("duplicate name", arg.pos) + ctx.error(DuplicateNamedTypeParameter(name), arg.pos) else if (!paramNames.contains(name)) - ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos) + ctx.error(UndefinedNamedTypeParameter(name, paramNames), arg.pos) else namedArgMap(name) = preCheckKind(arg, paramBoundsByName(name.asTypeName)).tpe diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index ca3bc6b06585..1f8c65a874f7 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -1088,4 +1088,48 @@ class ErrorMessagesTests extends ErrorMessagesTest { val MissingEmptyArgumentList(method) :: Nil = messages assertEquals("method greet", method.show) } + + @Test def duplicateNamedTypeParameter = + checkMessagesAfter("frontend") { + """ + |object Test { + | def f[A, B]() = ??? + | f[A=Any, A=Any]() + | f[B=Any, B=Any]() + |} + | + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + + assertMessageCount(2, messages) + val DuplicateNamedTypeParameter(n2) :: DuplicateNamedTypeParameter(n1) :: Nil = messages + assertEquals("A", n1.show) + assertEquals("B", n2.show) + } + + @Test def undefinedNamedTypeParameter = + checkMessagesAfter("frontend") { + """ + |object Test { + | def f[A, B]() = ??? + | f[A=Any, C=Any]() + | f[C=Any, B=Any]() + |} + | + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + + assertMessageCount(2, messages) + val UndefinedNamedTypeParameter(n2, l2) :: UndefinedNamedTypeParameter(n1, l1) :: Nil = messages + val tpParams = List("A", "B") + assertEquals("C", n1.show) + assertEquals(tpParams, l1.map(_.show)) + assertEquals("C", n2.show) + assertEquals(tpParams, l2.map(_.show)) + + } }