Skip to content

Commit d8c4ad8

Browse files
committed
Exhaustivity warnings on nested case classes
Fixes #13003, by refixing #12485 (PR #12488). Part of the issue is that isCheckable behaves differently under -Ycheck-all-patmat and our tests only run under that flag. So for starters I added a test variant where that flag isn't used. I'd like to understand why that flag exists to see if we could remove it from guarding the logic and the tests.
1 parent 46d0b05 commit d8c4ad8

File tree

4 files changed

+29
-8
lines changed

4 files changed

+29
-8
lines changed

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,9 @@ class SpaceEngine(using Context) extends SpaceLogic {
804804
}
805805

806806
private def exhaustivityCheckable(sel: Tree): Boolean = {
807+
val cache = collection.mutable.Map.empty[Type, Boolean]
807808
// Possible to check everything, but be compatible with scalac by default
808-
def isCheckable(tp: Type): Boolean =
809+
def isCheckable(tp: Type): Boolean = cache.getOrElseUpdate(tp, {
809810
val tpw = tp.widen.dealias
810811
val classSym = tpw.classSymbol
811812
classSym.is(Sealed) ||
@@ -815,16 +816,13 @@ class SpaceEngine(using Context) extends SpaceLogic {
815816
isCheckable(and.tp1) || isCheckable(and.tp2)
816817
}) ||
817818
tpw.isRef(defn.BooleanClass) ||
818-
classSym.isAllOf(JavaEnumTrait)
819+
classSym.isAllOf(JavaEnumTrait) ||
820+
classSym.is(Case) && productSelectorTypes(tpw, sel.srcPos).exists(isCheckable(_))
821+
})
819822

820823
val res = !sel.tpe.hasAnnotation(defn.UncheckedAnnot) && {
821824
ctx.settings.YcheckAllPatmat.value
822825
|| isCheckable(sel.tpe)
823-
|| {
824-
val tpw = sel.tpe.widen.dealias
825-
val classSym = tpw.classSymbol
826-
classSym.is(Case) && productSelectorTypes(tpw, sel.srcPos).exists(isCheckable(_))
827-
}
828826
}
829827

830828
debug.println(s"exhaustivity checkable: ${sel.show} = $res")

compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ import java.nio.file.{Path => JPath}
1515
import scala.io.Source._
1616
import org.junit.Test
1717

18+
class PatmatDefaultExhaustivityTest extends PatmatExhaustivityTest {
19+
override val testsDir = "tests/patmat-default"
20+
override val options = super.options.filter(_ != "-Ycheck-all-patmat")
21+
}
22+
1823
class PatmatExhaustivityTest {
1924
val testsDir = "tests/patmat"
2025
// stop-after: patmatexhaust-huge.scala crash compiler
21-
val options = List("-pagewidth", "80", "-color:never", "-Ystop-after:explicitSelf", "-Ycheck-all-patmat", "-classpath", TestConfiguration.basicClasspath)
26+
def options = List("-pagewidth", "80", "-color:never", "-Ystop-after:explicitSelf", "-Ycheck-all-patmat", "-classpath", TestConfiguration.basicClasspath)
2227

2328
private def compile(files: Seq[String]): Seq[String] = {
2429
val stringBuffer = new StringWriter()

tests/patmat-default/i13003.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
4: Pattern Match Exhaustivity: One(Two(None))
2+
7: Pattern Match Exhaustivity: Two(None)
3+
10: Pattern Match Exhaustivity: None, Some(None)
4+
13: Pattern Match Exhaustivity: None, Some(None), Some(Some(None))

tests/patmat-default/i13003.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
case class One(two: Two)
2+
case class Two(o: Option[Int])
3+
4+
def matchOneTwo(one: One) = one match
5+
case One(Two(Some(i))) => "match!"
6+
7+
def matchTwo(two: Two) = two match
8+
case Two(Some(i)) => "match!"
9+
10+
def matchOO(oo: Option[Option[Int]]) = oo match
11+
case Some(Some(i)) => "match!"
12+
13+
def matchOOO(ooo: Option[Option[Option[Int]]]) = ooo match
14+
case Some(Some(Some(i))) => "match!"

0 commit comments

Comments
 (0)