diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index c20fc27f9d1a..889f32e9d06e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -125,6 +125,7 @@ public enum ErrorMessageID { UnableToEmitSwitchID, MissingCompanionForStaticID, PolymorphicMethodMissingTypeInParentID, + NotAValidResultTypeOfUnapplyID ; 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 fb8b5705b604..5f93fa21ba03 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -2074,4 +2074,21 @@ object messages { |$rsym does not override any method in $parentSym. Structural refinement does not allow for |polymorphic methods.""" } + + case class NotAValidResultTypeOfUnapply(sym: Symbol, resultType: Type, args: List[untpd.Tree])(implicit ctx: Context) extends Message(NotAValidResultTypeOfUnapplyID) { + val kind = "Reference" + val resultClassName = resultType.classSymbol.name + val msg = hl"Cannot extract (${sym.name}) from ${sym.owner}. $resultClassName is not a valid extractor return type" + val explanation = + hl"""${sym.name} method of ${sym.owner} has been tried as a candidate to extract ${args.map(_.show).mkString("[", ", ", "]")} + |Its return type: ${s"$resultClassName"} is not a valid return type for an extractor. + | + |The return type of an extractor should be chosen as follows: + | - If it is just a test, return a ${"Boolean"}. For instance ${"case even()"} + | - If it returns a single sub-value of type ${s"$resultClassName"}, return an ${s"Option[$resultClassName]"} + | - If you want to return several sub-values ${"T1,...,Tn"}, group them in an optional tuple ${"Option[(T1,...,Tn)]"}. + """.stripMargin + + } + } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index a49e0f73bea9..f95a6849c32d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -33,7 +33,7 @@ import language.implicitConversions import reporting.diagnostic.Message import reporting.trace import Constants.{Constant, IntTag, LongTag} -import dotty.tools.dotc.reporting.diagnostic.messages.UnapplyInvalidNumberOfArguments +import dotty.tools.dotc.reporting.diagnostic.messages.{NotAValidResultTypeOfUnapply, UnapplyInvalidNumberOfArguments} import scala.collection.mutable.ListBuffer @@ -93,7 +93,8 @@ object Applications { def getTp = extractorMemberType(unapplyResult, nme.get, pos) def fail = { - ctx.error(i"$unapplyResult is not a valid result type of an $unapplyName method of an extractor", pos) + // ctx.error(i"$unapplyResult is not a valid result type of an $unapplyName method of an extractor", pos) + ctx.error(NotAValidResultTypeOfUnapply(unapplyFn.symbol, unapplyResult, args), pos) Nil } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index c94af01eb3cd..6f246eb5282e 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -2,12 +2,10 @@ package dotty.tools package dotc package reporting -import core.Contexts.Context -import diagnostic.messages._ -import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.Flags.FlagSet +import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Types.WildcardType import dotty.tools.dotc.parsing.Tokens +import dotty.tools.dotc.reporting.diagnostic.messages._ import org.junit.Assert._ import org.junit.Test @@ -1294,4 +1292,26 @@ class ErrorMessagesTests extends ErrorMessagesTest { assertEquals("method get", rsym.show) assertEquals("class Object", parentSym.show) } + + @Test def notAValidReturnTypeOfUnapply = + checkMessagesAfter("frontend") { + """ + |object Test { + | object Foo { + | def unapply(arg: String): String = arg.toUpperCase + | } + | val Foo(unextractedValue) = "test" + |} + """.stripMargin + }.expect { (ictx, messages) => + implicit val ctx: Context = ictx + + assertMessageCount(1, messages) + val NotAValidResultTypeOfUnapply(sym, resultType, args) = messages.head + assertEquals("method unapply", sym.show) + assertEquals("String", resultType.show) + assertEquals(1, args.length) + assertEquals("unextractedValue", args.head.show) + } + }