Skip to content

Commit 0240bf7

Browse files
author
Marc Karassev
committed
Add an error message for "unapply invalid return type" error.
Part of #1589.
1 parent 6cceefc commit 0240bf7

File tree

4 files changed

+94
-1
lines changed

4 files changed

+94
-1
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public enum ErrorMessageID {
115115
TraitRedefinedFinalMethodFromAnyRefID,
116116
PackageNameAlreadyDefinedID,
117117
UnapplyInvalidNumberOfArgumentsID,
118+
UnapplyInvalidReturnTypeID,
118119
StaticFieldsOnlyAllowedInObjectsID,
119120
CyclicInheritanceID,
120121
UnableToExtendSealedClassID,

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,6 +1943,57 @@ object messages {
19431943
|""".stripMargin
19441944
}
19451945

1946+
case class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Symbol#ThisName)(implicit ctx: Context)
1947+
extends Message(UnapplyInvalidReturnTypeID) {
1948+
val kind = "Type Mismatch"
1949+
val msg = hl"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}."""
1950+
val explanation = if (unapplyName.show == "unapply")
1951+
hl"""
1952+
|To be used as an extractor, an unapply method has to return a type that either:
1953+
| - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")})
1954+
| - is a ${Green("Boolean")}
1955+
| - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")})
1956+
|
1957+
|class A(val i: Int)
1958+
|
1959+
|object B {
1960+
| def unapply(a: A): ${Green("Option[Int]")} = Some(a.i)
1961+
|}
1962+
|
1963+
|object C {
1964+
| def unapply(a: A): ${Green("Boolean")} = a.i == 2
1965+
|}
1966+
|
1967+
|object D {
1968+
| def unapply(a: A): ${Green("(Int, Int)")} = (a.i, a.i)
1969+
|}
1970+
|
1971+
|object Test {
1972+
| def test(a: A) = a match {
1973+
| ${Magenta("case B(1)")} => 1
1974+
| ${Magenta("case a @ C()")} => 2
1975+
| ${Magenta("case D(3, 3)")} => 3
1976+
| }
1977+
|}
1978+
""".stripMargin
1979+
else
1980+
hl"""
1981+
|To be used as an extractor, an unapplySeq method has to return a type which has members
1982+
|${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} where ${Magenta("S <: Seq[V]")} (usually an ${Green("Option[Seq[V]]")}):
1983+
|
1984+
|object CharList {
1985+
| def unapplySeq(s: String): ${Green("Option[Seq[Char]")} = Some(s.toList)
1986+
|
1987+
| "example" match {
1988+
| ${Magenta("case CharList(c1, c2, c3, c4, _, _, _)")} =>
1989+
| println(s"$$c1,$$c2,$$c3,$$c4")
1990+
| case _ =>
1991+
| println("Expected *exactly* 7 characters!")
1992+
| }
1993+
|}
1994+
""".stripMargin
1995+
}
1996+
19461997
case class StaticFieldsOnlyAllowedInObjects(member: Symbol)(implicit ctx: Context) extends Message(StaticFieldsOnlyAllowedInObjectsID) {
19471998
val msg = hl"${"@static"} $member in ${member.owner} must be defined inside an ${"object"}."
19481999
val kind = "Syntax"

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import reporting.diagnostic.Message
3434
import reporting.trace
3535
import Constants.{Constant, IntTag, LongTag}
3636
import dotty.tools.dotc.reporting.diagnostic.messages.UnapplyInvalidNumberOfArguments
37+
import dotty.tools.dotc.reporting.diagnostic.messages.UnapplyInvalidReturnType
3738

3839
import scala.collection.mutable.ListBuffer
3940

@@ -93,7 +94,7 @@ object Applications {
9394
def getTp = extractorMemberType(unapplyResult, nme.get, pos)
9495

9596
def fail = {
96-
ctx.error(i"$unapplyResult is not a valid result type of an $unapplyName method of an extractor", pos)
97+
ctx.error(UnapplyInvalidReturnType(unapplyResult, unapplyName), pos)
9798
Nil
9899
}
99100

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,46 @@ class ErrorMessagesTests extends ErrorMessagesTest {
12251225
assertEquals("(class Int, class String)", argTypes.map(_.typeSymbol).mkString("(", ", ", ")"))
12261226
}
12271227

1228+
@Test def unapplyInvalidReturnType =
1229+
checkMessagesAfter("frontend") {
1230+
"""
1231+
|class A(val i: Int)
1232+
|
1233+
|object A {
1234+
| def unapply(a: A): Int = a.i
1235+
| def test(a: A) = a match {
1236+
| case A() => 1
1237+
| }
1238+
|}
1239+
""".stripMargin
1240+
}.expect { (ictx, messages) =>
1241+
implicit val ctx: Context = ictx
1242+
assertMessageCount(1, messages)
1243+
val UnapplyInvalidReturnType(unapplyResult, unapplyName) :: Nil = messages
1244+
assertEquals("Int", unapplyResult.show)
1245+
assertEquals("unapply", unapplyName.show)
1246+
}
1247+
1248+
@Test def unapplySeqInvalidReturnType =
1249+
checkMessagesAfter("frontend") {
1250+
"""
1251+
|class A(val i: Int)
1252+
|
1253+
|object A {
1254+
| def unapplySeq(a: A): Int = a.i
1255+
| def test(a: A) = a match {
1256+
| case A() => 1
1257+
| }
1258+
|}
1259+
""".stripMargin
1260+
}.expect { (ictx, messages) =>
1261+
implicit val ctx: Context = ictx
1262+
assertMessageCount(1, messages)
1263+
val UnapplyInvalidReturnType(unapplyResult, unapplyName) :: Nil = messages
1264+
assertEquals("Int", unapplyResult.show)
1265+
assertEquals("unapplySeq", unapplyName.show)
1266+
}
1267+
12281268
@Test def staticOnlyAllowedInsideObjects =
12291269
checkMessagesAfter("checkStatic") {
12301270
"""

0 commit comments

Comments
 (0)