Skip to content

Commit ab652d1

Browse files
authored
Merge pull request #1609 from dotty-staging/patmat-test
add back the forgotten test file for patmat exhuastivity check
2 parents 64a23e0 + 7332e9c commit ab652d1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+257
-417
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,4 +768,23 @@ object messages {
768768
| - null
769769
|"""
770770
}
771+
772+
case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context)
773+
extends Message(29) {
774+
val kind = "Pattern Match Exhaustivity"
775+
val msg =
776+
hl"""|match may not be exhaustive.
777+
|
778+
|It would fail on: $uncovered"""
779+
780+
781+
val explanation = ""
782+
}
783+
784+
case class MatchCaseUnreachable()(implicit ctx: Context)
785+
extends Message(30) {
786+
val kind = s"""Match ${hl"case"} Unreachable"""
787+
val msg = "unreachable code"
788+
val explanation = ""
789+
}
771790
}

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import core.Symbols._
1212
import core.StdNames._
1313
import core.NameOps._
1414
import core.Constants._
15+
import reporting.diagnostic.messages._
1516

1617
/** Space logic for checking exhaustivity and unreachability of pattern matching
1718
*
@@ -586,13 +587,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
586587
val patternSpace = cases.map(x => project(x.pat)).reduce((a, b) => Or(List(a, b)))
587588
val uncovered = simplify(minus(Typ(selTyp, true), patternSpace))
588589

589-
if (uncovered != Empty) {
590-
ctx.warning(
591-
"match may not be exhaustive.\n" +
592-
s"It would fail on the following input: " +
593-
show(uncovered), _match.pos
594-
)
595-
}
590+
if (uncovered != Empty)
591+
ctx.warning(PatternMatchExhaustivity(show(uncovered)), _match.pos)
596592
}
597593

598594
def checkRedundancy(_match: Match): Unit = {
@@ -612,7 +608,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
612608
val curr = project(cases(i).pat)
613609

614610
if (isSubspace(curr, prevs)) {
615-
ctx.warning("unreachable code", cases(i).body.pos)
611+
ctx.warning(MatchCaseUnreachable(), cases(i).body.pos)
616612
}
617613
}
618614
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package dotty.tools
2+
package dotc
3+
package reporting
4+
5+
import scala.collection.mutable
6+
import util.SourcePosition
7+
import core.Contexts._
8+
import Reporter._
9+
import java.io.PrintWriter
10+
import scala.reflect.internal.util._
11+
import diagnostic.{ Message, MessageContainer, NoExplanation }
12+
import diagnostic.messages._
13+
14+
class TestReporter(writer: PrintWriter) extends Reporter
15+
with UniqueMessagePositions with HideNonSensicalMessages {
16+
17+
import MessageContainer._
18+
19+
/** maximal number of error messages to be printed */
20+
protected def ErrorLimit = 100
21+
22+
def printPos(pos: SourcePosition): Unit =
23+
if (pos.exists) {
24+
if (pos.outer.exists) {
25+
writer.println(s"\ninlined at ${pos.outer}:\n")
26+
printPos(pos.outer)
27+
}
28+
}
29+
30+
/** Prints the message with the given position indication. */
31+
def printMessageAndPos(msg: String, pos: SourcePosition)(implicit ctx: Context): Unit = {
32+
val posStr = s"${pos.line + 1}: "
33+
writer.println(posStr + msg)
34+
printPos(pos)
35+
}
36+
37+
override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
38+
// Here we add extra information that we should know about the error message
39+
val extra = m.contained match {
40+
case pm: PatternMatchExhaustivity => s": ${pm.uncovered}"
41+
case _ => ""
42+
}
43+
44+
m match {
45+
case m: Error =>
46+
printMessageAndPos(m.contained.kind + extra, m.pos)
47+
case w: Warning =>
48+
printMessageAndPos(w.contained.kind + extra, w.pos)
49+
case _ =>
50+
}
51+
}
52+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package test.transform
2+
3+
import java.io._
4+
5+
import scala.io.Source._
6+
import scala.reflect.io.Directory
7+
import org.junit.Test
8+
import dotty.tools.dotc.Main
9+
import dotty.tools.dotc.reporting.TestReporter
10+
11+
class PatmatExhaustivityTest {
12+
val testsDir = "./tests/patmat"
13+
// stop-after: patmatexhaust-huge.scala crash compiler
14+
val options = List("-color:never", "-Ystop-after:splitter", "-Ycheck-all-patmat")
15+
16+
private def compileFile(file: File) = {
17+
val stringBuffer = new StringWriter()
18+
val reporter = new TestReporter(new PrintWriter(stringBuffer))
19+
20+
try {
21+
Main.process((file.getPath::options).toArray, reporter, null)
22+
} catch {
23+
case e: Throwable =>
24+
println(s"Compile $file exception:")
25+
e.printStackTrace()
26+
}
27+
28+
val actual = stringBuffer.toString.trim
29+
val checkFilePath = file.getAbsolutePath.stripSuffix(".scala") + ".check"
30+
val checkContent =
31+
if (new File(checkFilePath).exists)
32+
fromFile(checkFilePath).getLines.mkString("\n").trim
33+
else ""
34+
35+
(file, checkContent, actual)
36+
}
37+
38+
/** A single test with multiple files grouped in a folder */
39+
private def compileDir(file: File) = {
40+
val stringBuffer = new StringWriter()
41+
val reporter = new TestReporter(new PrintWriter(stringBuffer))
42+
43+
val files = Directory(file.getPath).list.toList
44+
.filter(f => f.extension == "scala" || f.extension == "java" )
45+
.map(_.jfile.getPath)
46+
47+
try {
48+
Main.process((options ++ files).toArray, reporter, null)
49+
} catch {
50+
case e: Throwable =>
51+
println(s"Compile $file exception:")
52+
e.printStackTrace()
53+
}
54+
55+
val actual = stringBuffer.toString.trim
56+
val checkFilePath = file.getPath + File.separator + "expected.check"
57+
val checkContent =
58+
if (new File(checkFilePath).exists)
59+
fromFile(checkFilePath).getLines.mkString("\n").trim
60+
else ""
61+
62+
(file, checkContent, actual)
63+
}
64+
65+
@Test def patmatExhaustivity: Unit = {
66+
val res = Directory(testsDir).list.toList
67+
.filter(f => f.extension == "scala" || f.isDirectory)
68+
.map { f =>
69+
if (f.isDirectory)
70+
compileDir(f.jfile)
71+
else
72+
compileFile(f.jfile)
73+
}
74+
75+
val failed = res.filter { case (_, expected, actual) => expected != actual }
76+
val ignored = Directory(testsDir).list.toList.filter(_.extension == "ignore")
77+
78+
failed.foreach { case (file, expected, actual) =>
79+
println(s"\n----------------- incorrect output for $file --------------\n" +
80+
s"Expected:\n---------\n$expected\n\nActual:\n-------\n$actual\n"
81+
)
82+
}
83+
84+
val msg = s"Total: ${res.length + ignored.length}, Failed: ${failed.length}, Ignored: ${ignored.length}"
85+
86+
assert(failed.length == 0, msg)
87+
88+
println(msg)
89+
}
90+
}

tests/patmat/NonAbstractSealed.check

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
./tests/patmat/NonAbstractSealed.scala:6: warning: match may not be exhaustive.
2-
It would fail on the following input: _: A
3-
(null: A) match {
4-
^
5-
one warning found
1+
6: Pattern Match Exhaustivity: _: A

tests/patmat/enum/expected.check

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,2 @@
1-
./tests/patmat/enum/patmat-enum.scala:4: warning: match may not be exhaustive.
2-
It would fail on the following input: SATURDAY, FRIDAY, THURSDAY, SUNDAY
3-
day match {
4-
^
5-
./tests/patmat/enum/patmat-enum.scala:15: warning: match may not be exhaustive.
6-
It would fail on the following input: SATURDAY, FRIDAY, THURSDAY
7-
day match {
8-
^
9-
two warnings found
1+
4: Pattern Match Exhaustivity: SATURDAY, FRIDAY, THURSDAY, SUNDAY
2+
15: Pattern Match Exhaustivity: SATURDAY, FRIDAY, THURSDAY

tests/patmat/exhausting.check

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,6 @@
1-
./tests/patmat/exhausting.scala:21: warning: match may not be exhaustive.
2-
It would fail on the following input: List(_), List(_, _, _)
3-
def fail1[T](xs: List[T]) = xs match {
4-
^
5-
./tests/patmat/exhausting.scala:27: warning: match may not be exhaustive.
6-
It would fail on the following input: Nil
7-
def fail2[T](xs: List[T]) = xs match {
8-
^
9-
./tests/patmat/exhausting.scala:32: warning: match may not be exhaustive.
10-
It would fail on the following input: List(_, _)
11-
def fail3a(xs: List[Int]) = xs match {
12-
^
13-
./tests/patmat/exhausting.scala:39: warning: match may not be exhaustive.
14-
It would fail on the following input: Bar3
15-
def fail3[T](x: Foo[T]) = x match {
16-
^
17-
./tests/patmat/exhausting.scala:44: warning: match may not be exhaustive.
18-
It would fail on the following input: (Bar2, Bar2)
19-
def fail4[T <: AnyRef](xx: (Foo[T], Foo[T])) = xx match {
20-
^
21-
./tests/patmat/exhausting.scala:53: warning: match may not be exhaustive.
22-
It would fail on the following input: (Bar2, Bar2), (Bar2, Bar1), (Bar1, Bar3), (Bar1, Bar2)
23-
def fail5[T](xx: (Foo[T], Foo[T])) = xx match {
24-
^
25-
6 warnings found
1+
21: Pattern Match Exhaustivity: List(_), List(_, _, _)
2+
27: Pattern Match Exhaustivity: Nil
3+
32: Pattern Match Exhaustivity: List(_, _)
4+
39: Pattern Match Exhaustivity: Bar3
5+
44: Pattern Match Exhaustivity: (Bar2, Bar2)
6+
53: Pattern Match Exhaustivity: (Bar2, Bar2), (Bar2, Bar1), (Bar1, Bar3), (Bar1, Bar2)

tests/patmat/gadt.check

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,4 @@
1-
./tests/patmat/gadt.scala:13: warning: match may not be exhaustive.
2-
It would fail on the following input: IntLit(_)
3-
def foo1b(x: Expr[Int]) = x match {
4-
^
5-
./tests/patmat/gadt.scala:22: warning: match may not be exhaustive.
6-
It would fail on the following input: Or(_, _)
7-
def foo2b(x: Expr[Boolean]) = x match {
8-
^
9-
./tests/patmat/gadt.scala:45: warning: match may not be exhaustive.
10-
It would fail on the following input: BooleanLit(_), IntLit(_)
11-
def foo4b(x: Expr[_]) = x match {
12-
^
13-
./tests/patmat/gadt.scala:55: warning: match may not be exhaustive.
14-
It would fail on the following input: Sum(_, _)
15-
def foo5b[T <: Int](x: Expr[T]) = x match {
16-
^
17-
four warnings found
1+
13: Pattern Match Exhaustivity: IntLit(_)
2+
22: Pattern Match Exhaustivity: Or(_, _)
3+
45: Pattern Match Exhaustivity: BooleanLit(_), IntLit(_)
4+
55: Pattern Match Exhaustivity: Sum(_, _)

tests/patmat/i947.check

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
./tests/patmat/i947.scala:10: warning: unreachable code
2-
case ys: List[d18383] => false
3-
^
4-
one warning found
1+
10: Match case Unreachable

tests/patmat/patmat-adt.check

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
1-
./tests/patmat/patmat-adt.scala:7: warning: match may not be exhaustive.
2-
It would fail on the following input: Bad(Good(_)), Good(Bad(_))
3-
def foo1a(x: Odd) = x match { // warning: Good(_: Bad), Bad(_: Good)
4-
^
5-
./tests/patmat/patmat-adt.scala:19: warning: match may not be exhaustive.
6-
It would fail on the following input: Some(_)
7-
def foo2(x: Option[Int]) = x match { // warning: Some(_: Int)
8-
^
9-
./tests/patmat/patmat-adt.scala:24: warning: match may not be exhaustive.
10-
It would fail on the following input: (None, Some(_)), (_, Some(_))
11-
def foo3a[T](x: Option[T]) = (x, x) match { // warning: (Some(_), Some(_)), (None, Some(_))
12-
^
13-
./tests/patmat/patmat-adt.scala:29: warning: match may not be exhaustive.
14-
It would fail on the following input: (None, None), (Some(_), Some(_))
15-
def foo3b[T](x: Option[T]) = (x, x) match { // warning: (Some(_), Some(_)), (None, None)
16-
^
17-
./tests/patmat/patmat-adt.scala:50: warning: match may not be exhaustive.
18-
It would fail on the following input: LetL(BooleanLit), LetL(IntLit)
19-
def foo5(tree: Tree) : Any = tree match {
20-
^
21-
5 warnings found
1+
7: Pattern Match Exhaustivity: Bad(Good(_)), Good(Bad(_))
2+
19: Pattern Match Exhaustivity: Some(_)
3+
24: Pattern Match Exhaustivity: (None, Some(_)), (_, Some(_))
4+
29: Pattern Match Exhaustivity: (None, None), (Some(_), Some(_))
5+
50: Pattern Match Exhaustivity: LetL(BooleanLit), LetL(IntLit)

tests/patmat/patmat-indent.check

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
./tests/patmat/patmat-indent.scala:9: warning: match may not be exhaustive.
2-
It would fail on the following input: Nil
3-
def foo1a[T](l: List[T]) = l match {
4-
^
5-
./tests/patmat/patmat-indent.scala:23: warning: match may not be exhaustive.
6-
It would fail on the following input: _: Boolean
7-
def foo2(b: Boolean) = b match {
8-
^
9-
./tests/patmat/patmat-indent.scala:27: warning: match may not be exhaustive.
10-
It would fail on the following input: _: Int
11-
def foo3(x: Int) = x match {
12-
^
13-
three warnings found
1+
9: Pattern Match Exhaustivity: Nil
2+
23: Pattern Match Exhaustivity: _: Boolean
3+
27: Pattern Match Exhaustivity: _: Int

tests/patmat/patmat-ortype.check

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
./tests/patmat/patmat-ortype.scala:8: warning: match may not be exhaustive.
2-
It would fail on the following input: _: String
3-
def foo2a(x: Int | Double | String) = x match { // _: String not matched
4-
^
5-
./tests/patmat/patmat-ortype.scala:18: warning: match may not be exhaustive.
6-
It would fail on the following input: Some(_: String), None
7-
def foo3(x: Option[Int | Double | String]) = x match { // warning: None, Some(_: String) not matched
8-
^
9-
./tests/patmat/patmat-ortype.scala:36: warning: match may not be exhaustive.
10-
It would fail on the following input: Some(_: String)
11-
def foo5b(x: Option[Int | Double | String]) = x match { // warning: Some(_: String) not matched
12-
^
13-
three warnings found
1+
8: Pattern Match Exhaustivity: _: String
2+
18: Pattern Match Exhaustivity: Some(_: String), None
3+
36: Pattern Match Exhaustivity: Some(_: String)

tests/patmat/patmatexhaust-huge.check

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
./tests/patmat/patmatexhaust-huge.scala:404: warning: match may not be exhaustive.
2-
It would fail on the following input: C397, C392
3-
def f(c: C): Int = c match {
4-
^
5-
one warning found
1+
404: Pattern Match Exhaustivity: C397, C392

tests/patmat/patmatexhaust.check

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,8 @@
1-
./tests/patmat/patmatexhaust.scala:7: warning: match may not be exhaustive.
2-
It would fail on the following input: Baz
3-
def ma1(x:Foo) = x match {
4-
^
5-
./tests/patmat/patmatexhaust.scala:11: warning: match may not be exhaustive.
6-
It would fail on the following input: Bar(_)
7-
def ma2(x:Foo) = x match {
8-
^
9-
./tests/patmat/patmatexhaust.scala:23: warning: match may not be exhaustive.
10-
It would fail on the following input: (Qult(), Qult()), (Kult(_), Kult(_))
11-
def ma3(x:Mult) = (x,x) match { // not exhaustive
12-
^
13-
./tests/patmat/patmatexhaust.scala:49: warning: match may not be exhaustive.
14-
It would fail on the following input: _: Gp
15-
def ma4(x:Deep) = x match { // missing cases: Gu, Gp which is not abstract so must be included
16-
^
17-
./tests/patmat/patmatexhaust.scala:75: warning: match may not be exhaustive.
18-
It would fail on the following input: _: B
19-
def ma9(x: B) = x match {
20-
^
21-
./tests/patmat/patmatexhaust.scala:100: warning: match may not be exhaustive.
22-
It would fail on the following input: _: C1
23-
def ma10(x: C) = x match { // not exhaustive: C1 is not sealed.
24-
^
25-
./tests/patmat/patmatexhaust.scala:114: warning: match may not be exhaustive.
26-
It would fail on the following input: D2(), D1
27-
def ma10(x: C) = x match { // not exhaustive: C1 has subclasses.
28-
^
29-
./tests/patmat/patmatexhaust.scala:126: warning: match may not be exhaustive.
30-
It would fail on the following input: _: C1
31-
def ma10(x: C) = x match { // not exhaustive: C1 is not abstract.
32-
^
33-
8 warnings found
1+
7: Pattern Match Exhaustivity: Baz
2+
11: Pattern Match Exhaustivity: Bar(_)
3+
23: Pattern Match Exhaustivity: (Qult(), Qult()), (Kult(_), Kult(_))
4+
49: Pattern Match Exhaustivity: _: Gp
5+
75: Pattern Match Exhaustivity: _: B
6+
100: Pattern Match Exhaustivity: _: C1
7+
114: Pattern Match Exhaustivity: D2(), D1
8+
126: Pattern Match Exhaustivity: _: C1

0 commit comments

Comments
 (0)