Skip to content

add back the forgotten test file for patmat exhuastivity check #1609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/dotty/tools/dotc/reporting/diagnostic/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -768,4 +768,23 @@ object messages {
| - null
|"""
}

case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context)
extends Message(29) {
val kind = "Pattern Match Exhaustivity"
val msg =
hl"""|match may not be exhaustive.
|
|It would fail on: $uncovered"""


val explanation = ""
}

case class MatchCaseUnreachable()(implicit ctx: Context)
extends Message(30) {
val kind = s"""Match ${hl"case"} Unreachable"""
val msg = "unreachable code"
val explanation = ""
}
}
12 changes: 4 additions & 8 deletions src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import core.Symbols._
import core.StdNames._
import core.NameOps._
import core.Constants._
import reporting.diagnostic.messages._

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

if (uncovered != Empty) {
ctx.warning(
"match may not be exhaustive.\n" +
s"It would fail on the following input: " +
show(uncovered), _match.pos
)
}
if (uncovered != Empty)
ctx.warning(PatternMatchExhaustivity(show(uncovered)), _match.pos)
}

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

if (isSubspace(curr, prevs)) {
ctx.warning("unreachable code", cases(i).body.pos)
ctx.warning(MatchCaseUnreachable(), cases(i).body.pos)
}
}
}
Expand Down
52 changes: 52 additions & 0 deletions test/dotty/tools/dotc/reporting/TestReporter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dotty.tools
package dotc
package reporting

import scala.collection.mutable
import util.SourcePosition
import core.Contexts._
import Reporter._
import java.io.PrintWriter
import scala.reflect.internal.util._
import diagnostic.{ Message, MessageContainer, NoExplanation }
import diagnostic.messages._

class TestReporter(writer: PrintWriter) extends Reporter
with UniqueMessagePositions with HideNonSensicalMessages {

import MessageContainer._

/** maximal number of error messages to be printed */
protected def ErrorLimit = 100

def printPos(pos: SourcePosition): Unit =
if (pos.exists) {
if (pos.outer.exists) {
writer.println(s"\ninlined at ${pos.outer}:\n")
printPos(pos.outer)
}
}

/** Prints the message with the given position indication. */
def printMessageAndPos(msg: String, pos: SourcePosition)(implicit ctx: Context): Unit = {
val posStr = s"${pos.line + 1}: "
writer.println(posStr + msg)
printPos(pos)
}

override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
// Here we add extra information that we should know about the error message
val extra = m.contained match {
case pm: PatternMatchExhaustivity => s": ${pm.uncovered}"
case _ => ""
}

m match {
case m: Error =>
printMessageAndPos(m.contained.kind + extra, m.pos)
case w: Warning =>
printMessageAndPos(w.contained.kind + extra, w.pos)
case _ =>
}
}
}
90 changes: 90 additions & 0 deletions test/test/transform/PatmatExhaustivityTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package test.transform

import java.io._

import scala.io.Source._
import scala.reflect.io.Directory
import org.junit.Test
import dotty.tools.dotc.Main
import dotty.tools.dotc.reporting.TestReporter

class PatmatExhaustivityTest {
val testsDir = "./tests/patmat"
// stop-after: patmatexhaust-huge.scala crash compiler
val options = List("-color:never", "-Ystop-after:splitter", "-Ycheck-all-patmat")

private def compileFile(file: File) = {
val stringBuffer = new StringWriter()
val reporter = new TestReporter(new PrintWriter(stringBuffer))

try {
Main.process((file.getPath::options).toArray, reporter, null)
} catch {
case e: Throwable =>
println(s"Compile $file exception:")
e.printStackTrace()
}

val actual = stringBuffer.toString.trim
val checkFilePath = file.getAbsolutePath.stripSuffix(".scala") + ".check"
val checkContent =
if (new File(checkFilePath).exists)
fromFile(checkFilePath).getLines.mkString("\n").trim
else ""

(file, checkContent, actual)
}

/** A single test with multiple files grouped in a folder */
private def compileDir(file: File) = {
val stringBuffer = new StringWriter()
val reporter = new TestReporter(new PrintWriter(stringBuffer))

val files = Directory(file.getPath).list.toList
.filter(f => f.extension == "scala" || f.extension == "java" )
.map(_.jfile.getPath)

try {
Main.process((options ++ files).toArray, reporter, null)
} catch {
case e: Throwable =>
println(s"Compile $file exception:")
e.printStackTrace()
}

val actual = stringBuffer.toString.trim
val checkFilePath = file.getPath + File.separator + "expected.check"
val checkContent =
if (new File(checkFilePath).exists)
fromFile(checkFilePath).getLines.mkString("\n").trim
else ""

(file, checkContent, actual)
}

@Test def patmatExhaustivity: Unit = {
val res = Directory(testsDir).list.toList
.filter(f => f.extension == "scala" || f.isDirectory)
.map { f =>
if (f.isDirectory)
compileDir(f.jfile)
else
compileFile(f.jfile)
}

val failed = res.filter { case (_, expected, actual) => expected != actual }
val ignored = Directory(testsDir).list.toList.filter(_.extension == "ignore")

failed.foreach { case (file, expected, actual) =>
println(s"\n----------------- incorrect output for $file --------------\n" +
s"Expected:\n---------\n$expected\n\nActual:\n-------\n$actual\n"
)
}

val msg = s"Total: ${res.length + ignored.length}, Failed: ${failed.length}, Ignored: ${ignored.length}"

assert(failed.length == 0, msg)

println(msg)
}
}
6 changes: 1 addition & 5 deletions tests/patmat/NonAbstractSealed.check
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
./tests/patmat/NonAbstractSealed.scala:6: warning: match may not be exhaustive.
It would fail on the following input: _: A
(null: A) match {
^
one warning found
6: Pattern Match Exhaustivity: _: A
11 changes: 2 additions & 9 deletions tests/patmat/enum/expected.check
Original file line number Diff line number Diff line change
@@ -1,9 +1,2 @@
./tests/patmat/enum/patmat-enum.scala:4: warning: match may not be exhaustive.
It would fail on the following input: SATURDAY, FRIDAY, THURSDAY, SUNDAY
day match {
^
./tests/patmat/enum/patmat-enum.scala:15: warning: match may not be exhaustive.
It would fail on the following input: SATURDAY, FRIDAY, THURSDAY
day match {
^
two warnings found
4: Pattern Match Exhaustivity: SATURDAY, FRIDAY, THURSDAY, SUNDAY
15: Pattern Match Exhaustivity: SATURDAY, FRIDAY, THURSDAY
31 changes: 6 additions & 25 deletions tests/patmat/exhausting.check
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
./tests/patmat/exhausting.scala:21: warning: match may not be exhaustive.
It would fail on the following input: List(_), List(_, _, _)
def fail1[T](xs: List[T]) = xs match {
^
./tests/patmat/exhausting.scala:27: warning: match may not be exhaustive.
It would fail on the following input: Nil
def fail2[T](xs: List[T]) = xs match {
^
./tests/patmat/exhausting.scala:32: warning: match may not be exhaustive.
It would fail on the following input: List(_, _)
def fail3a(xs: List[Int]) = xs match {
^
./tests/patmat/exhausting.scala:39: warning: match may not be exhaustive.
It would fail on the following input: Bar3
def fail3[T](x: Foo[T]) = x match {
^
./tests/patmat/exhausting.scala:44: warning: match may not be exhaustive.
It would fail on the following input: (Bar2, Bar2)
def fail4[T <: AnyRef](xx: (Foo[T], Foo[T])) = xx match {
^
./tests/patmat/exhausting.scala:53: warning: match may not be exhaustive.
It would fail on the following input: (Bar2, Bar2), (Bar2, Bar1), (Bar1, Bar3), (Bar1, Bar2)
def fail5[T](xx: (Foo[T], Foo[T])) = xx match {
^
6 warnings found
21: Pattern Match Exhaustivity: List(_), List(_, _, _)
27: Pattern Match Exhaustivity: Nil
32: Pattern Match Exhaustivity: List(_, _)
39: Pattern Match Exhaustivity: Bar3
44: Pattern Match Exhaustivity: (Bar2, Bar2)
53: Pattern Match Exhaustivity: (Bar2, Bar2), (Bar2, Bar1), (Bar1, Bar3), (Bar1, Bar2)
21 changes: 4 additions & 17 deletions tests/patmat/gadt.check
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
./tests/patmat/gadt.scala:13: warning: match may not be exhaustive.
It would fail on the following input: IntLit(_)
def foo1b(x: Expr[Int]) = x match {
^
./tests/patmat/gadt.scala:22: warning: match may not be exhaustive.
It would fail on the following input: Or(_, _)
def foo2b(x: Expr[Boolean]) = x match {
^
./tests/patmat/gadt.scala:45: warning: match may not be exhaustive.
It would fail on the following input: BooleanLit(_), IntLit(_)
def foo4b(x: Expr[_]) = x match {
^
./tests/patmat/gadt.scala:55: warning: match may not be exhaustive.
It would fail on the following input: Sum(_, _)
def foo5b[T <: Int](x: Expr[T]) = x match {
^
four warnings found
13: Pattern Match Exhaustivity: IntLit(_)
22: Pattern Match Exhaustivity: Or(_, _)
45: Pattern Match Exhaustivity: BooleanLit(_), IntLit(_)
55: Pattern Match Exhaustivity: Sum(_, _)
5 changes: 1 addition & 4 deletions tests/patmat/i947.check
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
./tests/patmat/i947.scala:10: warning: unreachable code
case ys: List[d18383] => false
^
one warning found
10: Match case Unreachable
26 changes: 5 additions & 21 deletions tests/patmat/patmat-adt.check
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
./tests/patmat/patmat-adt.scala:7: warning: match may not be exhaustive.
It would fail on the following input: Bad(Good(_)), Good(Bad(_))
def foo1a(x: Odd) = x match { // warning: Good(_: Bad), Bad(_: Good)
^
./tests/patmat/patmat-adt.scala:19: warning: match may not be exhaustive.
It would fail on the following input: Some(_)
def foo2(x: Option[Int]) = x match { // warning: Some(_: Int)
^
./tests/patmat/patmat-adt.scala:24: warning: match may not be exhaustive.
It would fail on the following input: (None, Some(_)), (_, Some(_))
def foo3a[T](x: Option[T]) = (x, x) match { // warning: (Some(_), Some(_)), (None, Some(_))
^
./tests/patmat/patmat-adt.scala:29: warning: match may not be exhaustive.
It would fail on the following input: (None, None), (Some(_), Some(_))
def foo3b[T](x: Option[T]) = (x, x) match { // warning: (Some(_), Some(_)), (None, None)
^
./tests/patmat/patmat-adt.scala:50: warning: match may not be exhaustive.
It would fail on the following input: LetL(BooleanLit), LetL(IntLit)
def foo5(tree: Tree) : Any = tree match {
^
5 warnings found
7: Pattern Match Exhaustivity: Bad(Good(_)), Good(Bad(_))
19: Pattern Match Exhaustivity: Some(_)
24: Pattern Match Exhaustivity: (None, Some(_)), (_, Some(_))
29: Pattern Match Exhaustivity: (None, None), (Some(_), Some(_))
50: Pattern Match Exhaustivity: LetL(BooleanLit), LetL(IntLit)
16 changes: 3 additions & 13 deletions tests/patmat/patmat-indent.check
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
./tests/patmat/patmat-indent.scala:9: warning: match may not be exhaustive.
It would fail on the following input: Nil
def foo1a[T](l: List[T]) = l match {
^
./tests/patmat/patmat-indent.scala:23: warning: match may not be exhaustive.
It would fail on the following input: _: Boolean
def foo2(b: Boolean) = b match {
^
./tests/patmat/patmat-indent.scala:27: warning: match may not be exhaustive.
It would fail on the following input: _: Int
def foo3(x: Int) = x match {
^
three warnings found
9: Pattern Match Exhaustivity: Nil
23: Pattern Match Exhaustivity: _: Boolean
27: Pattern Match Exhaustivity: _: Int
16 changes: 3 additions & 13 deletions tests/patmat/patmat-ortype.check
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
./tests/patmat/patmat-ortype.scala:8: warning: match may not be exhaustive.
It would fail on the following input: _: String
def foo2a(x: Int | Double | String) = x match { // _: String not matched
^
./tests/patmat/patmat-ortype.scala:18: warning: match may not be exhaustive.
It would fail on the following input: Some(_: String), None
def foo3(x: Option[Int | Double | String]) = x match { // warning: None, Some(_: String) not matched
^
./tests/patmat/patmat-ortype.scala:36: warning: match may not be exhaustive.
It would fail on the following input: Some(_: String)
def foo5b(x: Option[Int | Double | String]) = x match { // warning: Some(_: String) not matched
^
three warnings found
8: Pattern Match Exhaustivity: _: String
18: Pattern Match Exhaustivity: Some(_: String), None
36: Pattern Match Exhaustivity: Some(_: String)
6 changes: 1 addition & 5 deletions tests/patmat/patmatexhaust-huge.check
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
./tests/patmat/patmatexhaust-huge.scala:404: warning: match may not be exhaustive.
It would fail on the following input: C397, C392
def f(c: C): Int = c match {
^
one warning found
404: Pattern Match Exhaustivity: C397, C392
41 changes: 8 additions & 33 deletions tests/patmat/patmatexhaust.check
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
./tests/patmat/patmatexhaust.scala:7: warning: match may not be exhaustive.
It would fail on the following input: Baz
def ma1(x:Foo) = x match {
^
./tests/patmat/patmatexhaust.scala:11: warning: match may not be exhaustive.
It would fail on the following input: Bar(_)
def ma2(x:Foo) = x match {
^
./tests/patmat/patmatexhaust.scala:23: warning: match may not be exhaustive.
It would fail on the following input: (Qult(), Qult()), (Kult(_), Kult(_))
def ma3(x:Mult) = (x,x) match { // not exhaustive
^
./tests/patmat/patmatexhaust.scala:49: warning: match may not be exhaustive.
It would fail on the following input: _: Gp
def ma4(x:Deep) = x match { // missing cases: Gu, Gp which is not abstract so must be included
^
./tests/patmat/patmatexhaust.scala:75: warning: match may not be exhaustive.
It would fail on the following input: _: B
def ma9(x: B) = x match {
^
./tests/patmat/patmatexhaust.scala:100: warning: match may not be exhaustive.
It would fail on the following input: _: C1
def ma10(x: C) = x match { // not exhaustive: C1 is not sealed.
^
./tests/patmat/patmatexhaust.scala:114: warning: match may not be exhaustive.
It would fail on the following input: D2(), D1
def ma10(x: C) = x match { // not exhaustive: C1 has subclasses.
^
./tests/patmat/patmatexhaust.scala:126: warning: match may not be exhaustive.
It would fail on the following input: _: C1
def ma10(x: C) = x match { // not exhaustive: C1 is not abstract.
^
8 warnings found
7: Pattern Match Exhaustivity: Baz
11: Pattern Match Exhaustivity: Bar(_)
23: Pattern Match Exhaustivity: (Qult(), Qult()), (Kult(_), Kult(_))
49: Pattern Match Exhaustivity: _: Gp
75: Pattern Match Exhaustivity: _: B
100: Pattern Match Exhaustivity: _: C1
114: Pattern Match Exhaustivity: D2(), D1
126: Pattern Match Exhaustivity: _: C1
Loading