Skip to content

Commit 776f25b

Browse files
author
Marc Karassev
committed
Add an error message for "invalid unapply return type" error.
Part of #1589.
1 parent 635d668 commit 776f25b

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-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
@@ -113,6 +113,7 @@ public enum ErrorMessageID {
113113
IllegalStartOfStatementID,
114114
TraitIsExpectedID,
115115
TraitRedefinedFinalMethodFromAnyRefID,
116+
InvalidUnapplyReturnTypeID,
116117
;
117118

118119
public int errorNumber() {

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,4 +1922,56 @@ object messages {
19221922
val msg = hl"Traits cannot redefine final $method from ${"class AnyRef"}."
19231923
val explanation = ""
19241924
}
1925+
1926+
case class InvalidUnapplyReturnType(unapplyResult: Type, unapplyName: Symbol#ThisName)(implicit ctx: Context)
1927+
extends Message(InvalidUnapplyReturnTypeID) {
1928+
val kind = "Type Mismatch"
1929+
val msg = hl"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}."""
1930+
val explanation = if (unapplyName.show == "unapply")
1931+
hl"""
1932+
|To be used as an extractor, an unapply method has to return a type that either:
1933+
| - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")})
1934+
| - is a ${Green("Boolean")}
1935+
| - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")})
1936+
|
1937+
|class A(val i: Int)
1938+
|
1939+
|object B {
1940+
| def unapply(a: A): ${Green("Option[Int]")} = Some(a.i)
1941+
|}
1942+
|
1943+
|object C {
1944+
| def unapply(a: A): ${Green("Boolean")} = a.i == 2
1945+
|}
1946+
|
1947+
|object D {
1948+
| def unapply(a: A): ${Green("(Int, Int)")} = (a.i, a.i)
1949+
|}
1950+
|
1951+
|object Test {
1952+
| def test(a: A) = a match {
1953+
| ${Magenta("case B(1)")} => 1
1954+
| ${Magenta("case a @ C()")} => 2
1955+
| ${Magenta("case D(3, 3)")} => 3
1956+
| }
1957+
|}
1958+
""".stripMargin
1959+
else
1960+
hl"""
1961+
|To be used as an extractor, an unapplySeq method has to return a type which has members
1962+
|${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} where ${Magenta("S <: Seq[V]")} (usually an ${Green("Option[Seq[V]]")}):
1963+
|
1964+
|object CharList {
1965+
| def unapplySeq(s: String): ${Green("Option[Seq[Char]")} = Some(s.toList)
1966+
|
1967+
| "example" match {
1968+
| ${Magenta("case CharList(c1, c2, c3, c4, _, _, _)")} =>
1969+
| println(s"$$c1,$$c2,$$c3,$$c4")
1970+
| case _ =>
1971+
| println("Expected *exactly* 7 characters!")
1972+
| }
1973+
|}
1974+
""".stripMargin
1975+
}
1976+
19251977
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import language.implicitConversions
3333
import reporting.diagnostic.Message
3434
import reporting.trace
3535
import Constants.{Constant, IntTag, LongTag}
36+
import dotty.tools.dotc.reporting.diagnostic.messages.InvalidUnapplyReturnType
3637

3738
import scala.collection.mutable.ListBuffer
3839

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

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

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,4 +1189,44 @@ class ErrorMessagesTests extends ErrorMessagesTest {
11891189
assertEquals("method wait", method.show)
11901190
}
11911191

1192+
@Test def invalidUnapplyReturnType =
1193+
checkMessagesAfter("frontend") {
1194+
"""
1195+
|class A(val i: Int)
1196+
|
1197+
|object A {
1198+
| def unapply(a: A): Int = a.i
1199+
| def test(a: A) = a match {
1200+
| case A() => 1
1201+
| }
1202+
|}
1203+
""".stripMargin
1204+
}.expect { (ictx, messages) =>
1205+
implicit val ctx: Context = ictx
1206+
assertMessageCount(1, messages)
1207+
val InvalidUnapplyReturnType(unapplyResult, unapplyName) :: Nil = messages
1208+
assertEquals("Int", unapplyResult.show)
1209+
assertEquals("unapply", unapplyName.show)
1210+
}
1211+
1212+
@Test def invalidUnapplySeqReturnType =
1213+
checkMessagesAfter("frontend") {
1214+
"""
1215+
|class A(val i: Int)
1216+
|
1217+
|object A {
1218+
| def unapplySeq(a: A): Int = a.i
1219+
| def test(a: A) = a match {
1220+
| case A() => 1
1221+
| }
1222+
|}
1223+
""".stripMargin
1224+
}.expect { (ictx, messages) =>
1225+
implicit val ctx: Context = ictx
1226+
assertMessageCount(1, messages)
1227+
val InvalidUnapplyReturnType(unapplyResult, unapplyName) :: Nil = messages
1228+
assertEquals("Int", unapplyResult.show)
1229+
assertEquals("unapplySeq", unapplyName.show)
1230+
}
1231+
11921232
}

0 commit comments

Comments
 (0)