diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index a118e95bb5a8..c1e31140f411 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -459,7 +459,7 @@ object desugar { ListOfNil } else if (isCaseClass && originalVparamss.head.exists(_.mods.isOneOf(GivenOrImplicit))) { - ctx.error("Case classes should have a non-implicit parameter list", namePos) + ctx.error(CaseClassMissingNonImplicitParamList(cdef), namePos) ListOfNil } else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = false)) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala index e2aecdfbb018..41399a285620 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala @@ -151,7 +151,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] { IllegalCyclicTypeReferenceID, MissingTypeParameterInTypeAppID, ImplicitTypesCanOnlyBeFunctionTypesID, - ErasedTypesCanOnlyBeFunctionTypesID + ErasedTypesCanOnlyBeFunctionTypesID, + CaseClassMissingNonImplicitParamListID def errorNumber = ordinal - 2 } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 739a8f92a3b5..fe124b4ede06 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -2395,4 +2395,16 @@ object messages { val msg: String = "Types with erased keyword can only be function types `erased (...) => ...`" val explanation: String = "" } + + case class CaseClassMissingNonImplicitParamList(cdef: untpd.TypeDef)(implicit ctx: Context) + extends Message(CaseClassMissingNonImplicitParamListID) { + val kind: String = "Syntax" + val msg: String = + em"""|A ${hl("case class")} must have at least one non-implicit parameter list""" + + val explanation: String = + em"""|${cdef.name} must have at least one non-implicit parameter list, + | if you're aiming to have a case class parametrized only by implicit ones, you should + | add an explicit ${hl("()")} as a parameter list to ${cdef.name}.""".stripMargin + } } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index d89a5479d0d2..2859f701852a 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -1710,4 +1710,32 @@ class ErrorMessagesTests extends ErrorMessagesTest { val ErasedTypesCanOnlyBeFunctionTypes() :: Nil = messages assertEquals("Types with erased keyword can only be function types `erased (...) => ...`", messages.head.msg) } + + @Test def caseClassMissingParamListSuccessful = + checkMessagesAfter(FrontEnd.name) ("case class Test()") + .expectNoErrors + + @Test def caseClassMissingParamListFailed = + checkMessagesAfter(FrontEnd.name) ("case class Test") + .expect { + (ictx, messages) => + implicit val ctx: Context = ictx + assertMessageCount(1, messages) + val CaseClassMissingParamList(tpe) :: Nil = messages + assertEquals("A case class must have at least one parameter list", messages.head.msg) + } + + @Test def caseClassMissingNonImplicitParamListSuccessful = + checkMessagesAfter(FrontEnd.name) ("case class Test()(given foo: String)") + .expectNoErrors + + @Test def caseClassMissingNonImplicitParamListFailed = + checkMessagesAfter(FrontEnd.name) ("case class Test(given foo: String)") + .expect { + (ictx, messages) => + implicit val ctx: Context = ictx + assertMessageCount(1, messages) + val CaseClassMissingNonImplicitParamList(tpe) :: Nil = messages + assertEquals("A case class must have at least one non-implicit parameter list", messages.head.msg) + } }