Skip to content

Commit

Permalink
Merge pull request #9299 from dwijnand/xlint-unsealed-match
Browse files Browse the repository at this point in the history
Switch off exhaustivity warnings for unsealed types (by default)
  • Loading branch information
dwijnand committed Nov 5, 2020
2 parents 7101549 + 35d9bda commit e6a6ac6
Show file tree
Hide file tree
Showing 28 changed files with 21 additions and 175 deletions.
4 changes: 2 additions & 2 deletions project/ScalaOptionParser.scala
Expand Up @@ -83,7 +83,7 @@ object ScalaOptionParser {

// TODO retrieve these data programmatically, ala https://github.com/scala/scala-tool-support/blob/master/bash-completion/src/main/scala/BashCompletion.scala
private def booleanSettingNames = List("-X", "-Xasync", "-Xcheckinit", "-Xdev", "-Xdisable-assertions", "-Xexperimental", "-Xfatal-warnings", "-Xlog-free-terms", "-Xlog-free-types", "-Xlog-implicit-conversions", "-Xlog-implicits", "-Xlog-reflective-calls",
"-Xno-forwarders", "-Xno-patmat-analysis", "-Xno-unsealed-patmat-analysis", "-Xnon-strict-patmat-analysis", "-Xprint-pos", "-Xprint-types", "-Xprompt", "-Xresident", "-Xshow-phases", "-Xverify", "-Y",
"-Xno-forwarders", "-Xno-patmat-analysis", "-Xnon-strict-patmat-analysis", "-Xprint-pos", "-Xprint-types", "-Xprompt", "-Xresident", "-Xshow-phases", "-Xverify", "-Y",
"-Ybreak-cycles", "-Ydebug", "-Ycompact-trees", "-YdisableFlatCpCaching", "-Ydoc-debug",
"-Yide-debug",
"-Yissue-debug", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand",
Expand All @@ -108,7 +108,7 @@ object ScalaOptionParser {
"-g" -> List("line", "none", "notailcails", "source", "vars"),
"-target" -> targetSettingNames)
private def multiChoiceSettingNames = Map[String, List[String]](
"-Xlint" -> List("adapted-args", "nullary-unit", "inaccessible", "nullary-override", "infer-any", "missing-interpolator", "doc-detached", "private-shadow", "type-parameter-shadow", "poly-implicit-overload", "option-implicit", "delayedinit-select", "package-object-classes", "stars-align", "constant", "unused", "eta-zero"),
"-Xlint" -> List("adapted-args", "nullary-unit", "inaccessible", "nullary-override", "infer-any", "missing-interpolator", "doc-detached", "private-shadow", "type-parameter-shadow", "poly-implicit-overload", "option-implicit", "delayedinit-select", "package-object-classes", "stars-align", "strict-unsealed-patmat", "constant", "unused", "eta-zero"),
"-language" -> List("help", "_", "dynamics", "postfixOps", "reflectiveCalls", "implicitConversions", "higherKinds", "existentials", "experimental.macros"),
"-opt" -> List("unreachable-code", "simplify-jumps", "compact-locals", "copy-propagation", "redundant-casts", "box-unbox", "nullness-tracking", "closure-invocations" , "allow-skip-core-module-init", "assume-modules-non-null", "allow-skip-class-loading", "inline", "l:none", "l:default", "l:method", "l:inline", "l:project", "l:classpath"),
"-Ywarn-unused" -> List("imports", "patvars", "privates", "locals", "explicits", "implicits", "params"),
Expand Down
3 changes: 1 addition & 2 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Expand Up @@ -168,8 +168,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
def isAtLeastJunit = isTruthy || XmixinForceForwarders.value == "junit"
}

val nonStrictPatmatAnalysis = BooleanSetting("-Xnon-strict-patmat-analysis", "Disable strict exhaustivity analysis, which assumes guards are false and refutable extractors don't match")
val noUnsealedPatmatAnalysis = BooleanSetting("-Xno-unsealed-patmat-analysis", "Pattern match on an unsealed class without a catch-all.")
val nonStrictPatmatAnalysis = BooleanSetting("-Xnon-strict-patmat-analysis", "Disable strict exhaustivity analysis, which assumes guards are false and refutable extractors don't match")

// XML parsing options
object XxmlSettings extends MultiChoiceEnumeration {
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/scala/tools/nsc/settings/Warnings.scala
Expand Up @@ -177,6 +177,7 @@ trait Warnings {
val DelayedInitSelect = LintWarning("delayedinit-select", "Selecting member of DelayedInit.")
val PackageObjectClasses = LintWarning("package-object-classes", "Class or object defined in package object.")
val StarsAlign = LintWarning("stars-align", "In a pattern, a sequence wildcard `_*` should match all of a repeated parameter.")
val StrictUnsealedPatMat = LintWarning("strict-unsealed-patmat", "Pattern match on an unsealed class without a catch-all.")
val Constant = LintWarning("constant", "Evaluation of a constant arithmetic expression resulted in an error.")
val Unused = LintWarning("unused", "Enable -Wunused:imports,privates,locals,implicits,nowarn.")
val NonlocalReturn = LintWarning("nonlocal-return", "A return statement used an exception for flow control.")
Expand Down Expand Up @@ -208,6 +209,7 @@ trait Warnings {
def warnOptionImplicit = lint contains OptionImplicit
def warnDelayedInit = lint contains DelayedInitSelect
def warnPackageObjectClasses = lint contains PackageObjectClasses
def warnStrictUnsealedPatMat = lint contains StrictUnsealedPatMat
def warnStarsAlign = lint contains StarsAlign
def warnConstant = lint contains Constant
def lintUnused = lint contains Unused
Expand Down
Expand Up @@ -507,7 +507,7 @@ trait MatchAnalysis extends MatchApproximation {

// exhaustivity

def exhaustive(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[String] = if (settings.noUnsealedPatmatAnalysis && uncheckableType(prevBinder.info)) Nil else {
def exhaustive(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[String] = if (!settings.warnStrictUnsealedPatMat && uncheckableType(prevBinder.info)) Nil else {
// customize TreeMakersToProps (which turns a tree of tree makers into a more abstract DAG of tests)
// - approximate the pattern `List()` (unapplySeq on List with empty length) as `Nil`,
// otherwise the common (xs: List[Any]) match { case List() => case x :: xs => } is deemed unexhaustive
Expand Down
13 changes: 0 additions & 13 deletions test/files/neg/patmat-seq-neg.check
@@ -1,9 +1,3 @@
patmat-seq-neg.scala:12: warning: match may not be exhaustive.
def t2: Any = 2 match {
^
patmat-seq-neg.scala:15: warning: match may not be exhaustive.
def t3: Any = 2 match {
^
patmat-seq-neg.scala:15: error: error during expansion of this match (this is a scalac bug).
The underlying error was: type mismatch;
found : scala.collection.mutable.ArrayBuffer[Int]
Expand All @@ -14,15 +8,8 @@ patmat-seq-neg.scala:18: error: error during expansion of this match (this is a
The underlying error was: value toSeq is not a member of Array[Int]
def t4: Any = 2 match {
^
patmat-seq-neg.scala:21: warning: match may not be exhaustive.
def t5: Any = 2 match {
^
patmat-seq-neg.scala:24: warning: match may not be exhaustive.
def t6: Any = 2 match {
^
patmat-seq-neg.scala:24: error: error during expansion of this match (this is a scalac bug).
The underlying error was: value drop is not a member of Array[Int]
def t6: Any = 2 match {
^
4 warnings
3 errors
21 changes: 0 additions & 21 deletions test/files/neg/t11102.check
@@ -1,34 +1,13 @@
t11102.scala:2: warning: match may not be exhaustive.
It would fail on the following input: (x: ImmutableSeq forSome x not in ImmutableCons)
def f(x: ImmutableSeq) = x match {
^
t11102.scala:5: warning: match may not be exhaustive.
It would fail on the following input: (x: MutableSeq forSome x not in MutableCons)
def f(x: MutableSeq) = x match {
^
t11102.scala:5: error: error during expansion of this match (this is a scalac bug).
The underlying error was: type mismatch;
found : Seq[MutableCons] (in scala.collection.mutable)
required: Seq[MutableCons] (in scala.collection.immutable)
def f(x: MutableSeq) = x match {
^
t11102.scala:8: warning: match may not be exhaustive.
It would fail on the following input: (x: CollectionSeq forSome x not in CollectionCons)
def f(x: CollectionSeq) = x match {
^
t11102.scala:8: error: error during expansion of this match (this is a scalac bug).
The underlying error was: type mismatch;
found : Seq[CollectionCons] (in scala.collection)
required: Seq[CollectionCons] (in scala.collection.immutable)
def f(x: CollectionSeq) = x match {
^
t11102.scala:11: warning: match may not be exhaustive.
It would fail on the following input: (x: ScalaSeq forSome x not in ScalaCons)
def f(x: ScalaSeq) = x match {
^
t11102.scala:14: warning: match may not be exhaustive.
It would fail on the following input: (x: DefaultSeq forSome x not in DefaultCons)
def f(x: DefaultSeq) = x match {
^
5 warnings
2 errors
6 changes: 1 addition & 5 deletions test/files/neg/t11746.check
@@ -1,11 +1,7 @@
t11746.scala:16: warning: match may not be exhaustive.
It would fail on the following input: (x: Try forSome x not in Failure)
private def get(a: String): Unit = Try(a) match {
^
t11746.scala:18: warning: failed to determine if e should be inlined:
The method e()Ljava/lang/Throwable; could not be found in the class java/lang/Object or any of its parents.
case Failure(e) => println(e.toString)
^
error: No warnings can be incurred under -Werror.
2 warnings
1 warning
1 error
2 changes: 1 addition & 1 deletion test/files/neg/t5365c.scala
@@ -1,4 +1,4 @@
// scalac: -Xfatal-warnings
// scalac: -Xfatal-warnings -Xlint:strict-unsealed-patmat
object C {
trait Z
final case class Q(i: Int) extends Z
Expand Down
6 changes: 1 addition & 5 deletions test/files/neg/t5898.check
Expand Up @@ -6,10 +6,6 @@ t5898.scala:10: warning: match may not be exhaustive.
It would fail on the following input: C(_)
val D(x) = t
^
t5898.scala:11: warning: match may not be exhaustive.
It would fail on the following input: (x: Any forSome x not in D)
val D(y) = (null: Any)
^
error: No warnings can be incurred under -Werror.
3 warnings
2 warnings
1 error
2 changes: 1 addition & 1 deletion test/files/neg/t7623.scala
@@ -1,4 +1,4 @@
// scalac: -Xlint:stars-align -Xfatal-warnings -Xno-unsealed-patmat-analysis
// scalac: -Xlint:stars-align -Xfatal-warnings
//


Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t8597.scala
@@ -1,4 +1,4 @@
// scalac: -Xfatal-warnings -Xno-unsealed-patmat-analysis
// scalac: -Xfatal-warnings
//
class Unchecked[C] {
def nowarn[T] = (null: Any) match { case _: Some[T] => } // warn (did not warn due to scala/bug#8597)
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t8597b.scala
@@ -1,4 +1,4 @@
// scalac: -Xfatal-warnings -Xno-unsealed-patmat-analysis
// scalac: -Xfatal-warnings
//
object Unchecked {
(null: Any) match {
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t8731.scala
@@ -1,4 +1,4 @@
// scalac: -Xfatal-warnings -Xno-unsealed-patmat-analysis
// scalac: -Xfatal-warnings
//
class C {
// not a compile-time constant due to return type
Expand Down
10 changes: 5 additions & 5 deletions test/files/neg/tailrec-4.check
@@ -1,16 +1,16 @@
tailrec-4.scala:7: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
tailrec-4.scala:6: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
@tailrec def foo: Int = foo + 1
^
tailrec-4.scala:12: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
tailrec-4.scala:11: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
@tailrec def foo: Int = foo + 1
^
tailrec-4.scala:18: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
tailrec-4.scala:17: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
@tailrec def foo: Int = foo + 1
^
tailrec-4.scala:24: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
tailrec-4.scala:23: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
@tailrec def foo: Int = foo + 1
^
tailrec-4.scala:32: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
tailrec-4.scala:31: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position
@tailrec def foo: Int = foo + 1
^
5 errors
1 change: 0 additions & 1 deletion test/files/neg/tailrec-4.scala
@@ -1,4 +1,3 @@
// scalac: -Xno-unsealed-patmat-analysis
import annotation._

object Tail {
Expand Down
6 changes: 1 addition & 5 deletions test/files/neg/unchecked-refinement.check
Expand Up @@ -10,15 +10,11 @@ unchecked-refinement.scala:25: warning: a pattern match on a refinement type is
unchecked-refinement.scala:26: warning: a pattern match on a refinement type is unchecked
/* nowarn - todo */ case x: AnyRef { def size: Int } if b => x.size // this could/should do a static conformance test and not warn
^
unchecked-refinement.scala:18: warning: match may not be exhaustive.
It would fail on the following input: ??
def f3[T, U, V](x: Foo[T, U, V]) = x match {
^
unchecked-refinement.scala:24: warning: match may not be exhaustive.
It would fail on the following inputs: List(_), Nil
def f4(xs: List[Int]) = xs match {
^
warning: 1 feature warning; re-run with -feature for details
error: No warnings can be incurred under -Werror.
7 warnings
6 warnings
1 error
2 changes: 1 addition & 1 deletion test/files/neg/unchecked3.scala
@@ -1,4 +1,4 @@
// scalac: -Xfatal-warnings -Xno-unsealed-patmat-analysis
// scalac: -Xfatal-warnings
//
sealed trait A2[T1]
final class B2[T1, T2] extends A2[T1]
Expand Down
6 changes: 1 addition & 5 deletions test/files/neg/virtpatmat_unreach_select.check
@@ -1,10 +1,6 @@
virtpatmat_unreach_select.scala:12: warning: unreachable code
case WARNING.id => // unreachable
^
virtpatmat_unreach_select.scala:9: warning: match may not be exhaustive.
It would fail on the following input: (x: Int forSome x not in (id, id))
(0: Int) match {
^
error: No warnings can be incurred under -Werror.
2 warnings
1 warning
1 error
File renamed without changes.
3 changes: 0 additions & 3 deletions test/files/run/matchonstream.check
@@ -1,4 +1 @@
matchonstream.scala:2: warning: match may not be exhaustive.
LazyList.from(1) match { case LazyList(1, 2, x @_*) => println(s"It worked! (class: ${x.getClass.getSimpleName})") }
^
It worked! (class: LazyList)
42 changes: 0 additions & 42 deletions test/files/run/patmat-behavior.check
Expand Up @@ -88,30 +88,6 @@ patmat-behavior.scala:87: warning: fruitless type test: a value of type s.C21[A]
patmat-behavior.scala:87: warning: fruitless type test: a value of type s.C21[A] cannot also be a s.C11[A]
def gd6[A](x: C21[A]) = x match { case G00() => ??? ; case G10(x) => x ; case G20(x, y) => x ; case G01(xs @ _*) => xs.head ; case G11(x, ys @ _*) => x ; case G21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:36: warning: match may not be exhaustive.
It would fail on the following inputs: (x: Any forSome x not in (s.C00[?], s.C01[?], s.C10[?], s.C11[?], s.C20[?], s.C21[?])), ??, C01(_), C11(_, _), C21(_, _, _)
def ga1(x: Any) = x match { case C00() => 1 ; case C10(x) => 2 ; case C20(x, y) => 3 ; case C01(xs) => 4 ; case C11(x, ys) => 5 ; case C21(x, y, zs) => 6 }
^
patmat-behavior.scala:37: warning: match may not be exhaustive.
It would fail on the following inputs: (x: Any forSome x not in (s.C00[?], s.C01[?], s.C10[?], s.C11[?], s.C20[?], s.C21[?])), ??, C01(_), C11(_, _), C21(_, _, _)
def ga2(x: Any) = x match { case C00() => 1 ; case C10(x) => 2 ; case C20(x, y) => 3 ; case C01(xs) => 4 ; case C11(x, ys) => 5 ; case C21(x, y, zs) => 6 }
^
patmat-behavior.scala:38: warning: match may not be exhaustive.
It would fail on the following inputs: (x: Any forSome x not in (s.C00[?], s.C01[?], s.C10[?], s.C11[?], s.C20[?], s.C21[?])), ??, C01(_), C11(_, _), C21(_, _, _)
def ga3(x: Any) = x match { case C00() => 1 ; case C10(x) => 2 ; case C20(x, y) => 3 ; case C01(xs) => 4 ; case C11(x, ys) => 5 ; case C21(x, y, zs) => 6 }
^
patmat-behavior.scala:39: warning: match may not be exhaustive.
It would fail on the following inputs: (x: Any forSome x not in (s.C00[?], s.C01[?], s.C10[?], s.C11[?], s.C20[?], s.C21[?])), ??, C01(_), C11(_, _), C21(_, _, _)
def ga4(x: Any) = x match { case C00() => 1 ; case C10(x) => 2 ; case C20(x, y) => 3 ; case C01(xs) => 4 ; case C11(x, ys) => 5 ; case C21(x, y, zs) => 6 }
^
patmat-behavior.scala:40: warning: match may not be exhaustive.
It would fail on the following inputs: (x: Any forSome x not in (s.C00[?], s.C01[?], s.C10[?], s.C11[?], s.C20[?], s.C21[?])), ??, C01(_), C11(_, _), C21(_, _, _)
def ga5(x: Any) = x match { case C00() => 1 ; case C10(x) => 2 ; case C20(x, y) => 3 ; case C01(xs) => 4 ; case C11(x, ys) => 5 ; case C21(x, y, zs) => 6 }
^
patmat-behavior.scala:41: warning: match may not be exhaustive.
It would fail on the following inputs: (x: Any forSome x not in (s.C00[?], s.C01[?], s.C10[?], s.C11[?], s.C20[?], s.C21[?])), ??, C01(_), C11(_, _), C21(_, _, _)
def ga6(x: Any) = x match { case C00() => 1 ; case C10(x) => 2 ; case C20(x, y) => 3 ; case C01(xs) => 4 ; case C11(x, ys) => 5 ; case C21(x, y, zs) => 6 }
^
patmat-behavior.scala:43: warning: match may not be exhaustive.
It would fail on the following inputs: C00(), C01(_), C10(_), C11(_, _), C20(_, _), C21(_, _, _)
def gb1[A](x: C[A]) = x match { case E00() => ??? ; case E10(x) => x ; case E20(x, y) => x ; case E01(xs @ _*) => xs.head ; case E11(x, ys @ _*) => x ; case E21(x, y, zs @ _*) => x }
Expand Down Expand Up @@ -160,24 +136,6 @@ patmat-behavior.scala:55: warning: match may not be exhaustive.
It would fail on the following inputs: C00(), C01(_), C10(_), C11(_, _), C20(_, _), C21(_, _, _)
def gc6[A](x: C[A]) = x match { case F00() => ??? ; case F10(x) => x ; case F20(x, y) => x ; case F01(xs @ _*) => xs.head ; case F11(x, ys @ _*) => x ; case F21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:57: warning: match may not be exhaustive.
def gd1[A, B <: C[A]](x: B) = x match { case F00() => ??? ; case F10(x) => x ; case F20(x, y) => x ; case F01(xs @ _*) => xs.head ; case F11(x, ys @ _*) => x ; case F21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:58: warning: match may not be exhaustive.
def gd2[A, B <: C[A]](x: B) = x match { case F00() => ??? ; case F10(x) => x ; case F20(x, y) => x ; case F01(xs @ _*) => xs.head ; case F11(x, ys @ _*) => x ; case F21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:59: warning: match may not be exhaustive.
def gd3[A, B <: C[A]](x: B) = x match { case F00() => ??? ; case F10(x) => x ; case F20(x, y) => x ; case F01(xs @ _*) => xs.head ; case F11(x, ys @ _*) => x ; case F21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:60: warning: match may not be exhaustive.
def gd4[A, B <: C[A]](x: B) = x match { case F00() => ??? ; case F10(x) => x ; case F20(x, y) => x ; case F01(xs @ _*) => xs.head ; case F11(x, ys @ _*) => x ; case F21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:61: warning: match may not be exhaustive.
def gd5[A, B <: C[A]](x: B) = x match { case F00() => ??? ; case F10(x) => x ; case F20(x, y) => x ; case F01(xs @ _*) => xs.head ; case F11(x, ys @ _*) => x ; case F21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:62: warning: match may not be exhaustive.
def gd6[A, B <: C[A]](x: B) = x match { case F00() => ??? ; case F10(x) => x ; case F20(x, y) => x ; case F01(xs @ _*) => xs.head ; case F11(x, ys @ _*) => x ; case F21(x, y, zs @ _*) => x }
^
patmat-behavior.scala:68: warning: match may not be exhaustive.
It would fail on the following input: C00()
def gb1[A](x: C00[A]) = x match { case E00() => ??? ; case E10(x) => x ; case E20(x, y) => x ; case E01(xs @ _*) => xs.head ; case E11(x, ys @ _*) => x ; case E21(x, y, zs @ _*) => x }
Expand Down

0 comments on commit e6a6ac6

Please sign in to comment.