-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[Deprecated] exhaustivity & redundancy check for pattern matching #1261
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
Changes from 1 commit
5cdd42f
9a0952c
fd1b286
d681a78
da9511d
89e8ff9
b25e952
e2db834
2d6e803
f69b305
c79181d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
scala.Nil refines scala.collection.immutable.Nil, this commit follows redefinition of case objects.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -116,7 +116,7 @@ trait SpaceLogic { | |
| ss.exists(subspace(a, _)) || | ||
| (canDecompose(tp1) && subspace(Or(partitions(tp1)), b)) | ||
| case (Typ(tp1, _), Kon(tp2, ss)) => | ||
| isSubType(tp1, tp2) && subspace(Kon(tp2, signature(tp2).map(tp => Typ(tp, false))), b) | ||
| isSubType(tp1, tp2) && subspace(Kon(tp2, signature(tp2).map(Typ(_, false))), b) | ||
| case (Kon(tp1, ss), Typ(tp2, _)) => | ||
| isSubType(tp1, tp2) || | ||
| simplify(a) == Empty || | ||
|
|
@@ -188,7 +188,7 @@ trait SpaceLogic { | |
| minus(Or(partitions(tp1)), b) | ||
| else a | ||
|
||
| case (Typ(tp1, _), Kon(tp2, ss)) => | ||
| if (isSubType(tp1, tp2)) minus(Kon(tp2, signature(tp2).map(tp => Typ(tp, false))), b) | ||
| if (isSubType(tp1, tp2)) minus(Kon(tp2, signature(tp2).map(Typ(_, false))), b) | ||
|
||
| else if (isSubType(tp2, tp1) && canDecompose(tp1)) | ||
| minus(Or(partitions(tp1)), b) | ||
| else a | ||
|
|
@@ -242,15 +242,17 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { | |
| def project(pat: Tree, roundUp: Boolean = true)(implicit ctx: Context): Space = pat match { | ||
| case Literal(c) => Const(c, c.tpe) | ||
| case _: BackquotedIdent => Var(pat.symbol, pat.tpe) | ||
| case Ident(_) => | ||
| Typ(pat.tpe.stripAnnots, false) | ||
| case Select(_, _) => | ||
| if (pat.symbol.is(Module)) | ||
| Typ(pat.tpe.stripAnnots, false) | ||
| else if (pat.symbol.is(Enum)) | ||
| Const(Constant(pat.symbol), pat.tpe) | ||
| else | ||
| Var(pat.symbol, pat.tpe) | ||
| case Ident(_) | Select(_, _) => | ||
| pat.tpe.stripAnnots match { | ||
| case tp: TermRef => | ||
| if (pat.symbol.is(Enum)) | ||
| Const(Constant(pat.symbol), tp) | ||
| else if (tp.underlyingIterator.exists(_.classSymbol.is(Module))) | ||
| Typ(tp.widenTermRefExpr.stripAnnots, false) | ||
| else | ||
| Var(pat.symbol, tp) | ||
| case tp => Typ(tp, false) | ||
| } | ||
| case Alternative(trees) => Or(trees.map(project(_, roundUp))) | ||
| case Bind(_, pat) => project(pat) | ||
| case UnApply(_, _, pats) => | ||
|
|
@@ -295,6 +297,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { | |
| ktor | ||
| } | ||
|
|
||
| // refine path-dependent type in params. refer to t9672 | ||
| meth.firstParamTypes.map(_.stripTypeVar).map(refine(tp, _)) | ||
| } | ||
|
|
||
|
|
@@ -415,37 +418,42 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { | |
| if (mergeList) "_" else "List(_)" | ||
| else if (tp.classSymbol.is(CaseClass)) | ||
| // use constructor syntax for case class | ||
| showType(tp) + signature(tp).map(_ => "_").mkString("(", ",", ")") | ||
| showType(tp) + signature(tp).map(_ => "_").mkString("(", ", ", ")") | ||
| else if (signature(tp).nonEmpty) | ||
| tp.classSymbol.name + signature(tp).map(_ => "_").mkString("(", ",", ")") | ||
| tp.classSymbol.name + signature(tp).map(_ => "_").mkString("(", ", ", ")") | ||
| else if (decomposed) "_: " + showType(tp) | ||
| else "_" | ||
| case Kon(tp, params) => | ||
| if (ctx.definitions.isTupleType(tp)) | ||
| "(" + params.map(p => doShow(p)).mkString(", ") + ")" | ||
| "(" + params.map(doShow(_)).mkString(", ") + ")" | ||
| else if (tp.widen.classSymbol.showFullName == "scala.collection.immutable.::") | ||
| if (mergeList) params.map(p => doShow(p, mergeList)).mkString(", ") | ||
| else params.map(p => doShow(p, true)).mkString("List(", ", ", ")") | ||
| if (mergeList) params.map(doShow(_, mergeList)).mkString(", ") | ||
| else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")") | ||
| else | ||
| showType(tp) + params.map(p => doShow(p)).mkString("(", ", ", ")") | ||
| showType(tp) + params.map(doShow(_)).mkString("(", ", ", ")") | ||
| case Or(_) => | ||
| throw new Exception("incorrect flatten result " + s) | ||
| } | ||
|
|
||
| flatten(s).map(doShow(_, false)).distinct.mkString(", ") | ||
| } | ||
|
|
||
| def checkable(tp: Type): Boolean = tp match { | ||
| case AnnotatedType(tp, annot) => | ||
| (ctx.definitions.UncheckedAnnot != annot.symbol) && checkable(tp) | ||
| case _ => true // actually everything is checkable unless @unchecked | ||
| def checkable(tree: Match): Boolean = { | ||
| def isCheckable(tp: Type): Boolean = tp match { | ||
| case AnnotatedType(tp, annot) => | ||
| (ctx.definitions.UncheckedAnnot != annot.symbol) && isCheckable(tp) | ||
| case _ => true // actually everything is checkable unless @unchecked | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. scalac only checks sealed classes. We should consider if we indeed want to change this. |
||
| // tp.classSymbol.is(Sealed) || | ||
| // tp.isInstanceOf[OrType] || | ||
| // tp.classSymbol.is(Enum) || | ||
| // Boolean | ||
| // Int | ||
| // ... | ||
| } | ||
|
|
||
| val Match(sel, cases) = tree | ||
| isCheckable(sel.tpe.widen.elimAnonymousClass) | ||
| } | ||
|
|
||
| def checkExhaustivity(_match: Match): Unit = { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,21 @@ | ||
| object Test { | ||
| object Test1 { | ||
| val day: Day = ??? | ||
|
|
||
| day match { | ||
| case Day.MONDAY => true | ||
| case Day.TUESDAY => true | ||
| case Day.WEDNESDAY => true | ||
| } | ||
| } | ||
|
|
||
| object Test2 { | ||
| import Day._ | ||
| val day: Day = ??? | ||
|
|
||
| day match { | ||
| case MONDAY => true | ||
| case TUESDAY => true | ||
| case WEDNESDAY => true | ||
| case SUNDAY => true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| ./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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| object Test { | ||
| val Nil = scala.Nil | ||
| val X = 5 | ||
|
|
||
| object Inner { | ||
| val Y = false | ||
| } | ||
|
|
||
| def foo1a[T](l: List[T]) = l match { | ||
| case x::xs => false | ||
| } | ||
|
|
||
| def foo1b[T](l: List[T]) = l match { | ||
| case Nil => true | ||
| case x::xs => false | ||
| } | ||
|
|
||
| def foo1c[T](l: List[T]) = l match { | ||
| case Test.Nil => true | ||
| case x::xs => false | ||
| } | ||
|
|
||
| def foo2(b: Boolean) = b match { | ||
| case Inner.Y => false | ||
| } | ||
|
|
||
| def foo3(x: Int) = x match { | ||
| case X => 0 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| ./tests/patmat/t7466.scala:8: warning: match may not be exhaustive. | ||
| It would fail on the following input: (true, _), (false, _), (_, true), (_, false) | ||
| It would fail on the following input: (_, _) | ||
| (b1, b2) match { | ||
| ^ | ||
| one warning found |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| object OhNoes { | ||
|
|
||
| sealed trait F | ||
| sealed abstract class FA extends F | ||
| sealed abstract class FB extends F | ||
|
|
||
| case object FA1 extends FA | ||
| case object FB1 extends FB | ||
| case object FB2 extends FB | ||
|
|
||
| sealed trait G | ||
| case object G1 extends G | ||
| case object G2 extends G | ||
|
|
||
| sealed trait H | ||
| case class H1(a: FB, b: G) extends H | ||
| case class H2(a: F) extends H | ||
|
|
||
| val demo: H => Unit = { | ||
| case H1(FB1, G1) => | ||
| case H1(FB2, G2) => | ||
| case H2(_: FB) => | ||
| case H2(_: FA) => | ||
| case H1(FB1, G2) => | ||
| case H1(FB2, G1) => | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| object OhNoes { | ||
|
|
||
| sealed trait F | ||
| sealed abstract class FA extends F | ||
| sealed abstract class FB extends F | ||
|
|
||
| case object FA1 extends FA | ||
| case object FB1 extends FB | ||
| case object FB2 extends FB | ||
|
|
||
| sealed trait G | ||
| case object G1 extends G | ||
| case object G2 extends G | ||
|
|
||
| sealed trait H | ||
| case class H1(a: FB, b: G) extends H | ||
| case class H2(b: F) extends H | ||
|
|
||
| val demo: H => Unit = { | ||
| case H1(FB1, G1) => | ||
| case H1(FB2, G2) => | ||
| case H2(_: FB) => | ||
| case H2(_: FA) => | ||
| case H1(FB1, G2) => | ||
| case H1(FB2, G1) => | ||
| } | ||
|
|
||
| val demo2: H => Unit = { | ||
| case H2(_: FA) => | ||
| case H2(_: FB) => | ||
| case H1(FB1, G1) => | ||
| case H1(FB2, G1) => | ||
| case H1(FB1, G2) => | ||
| case H1(FB2, G2) => | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| ./tests/patmat/virtpatmat_apply.scala:2: warning: match may not be exhaustive. | ||
| It would fail on the following input: List(_) | ||
| List(1, 2, 3) match { | ||
| ^ | ||
| one warning found |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| object Test { | ||
| List(1, 2, 3) match { | ||
| case Nil => println("FAIL") | ||
| case x :: y :: xs if xs.length == 2 => println("FAIL") | ||
| case x :: y :: xs if xs.length == 1 => println("OK "+ y) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PatternMatcher now has a 🌠SpaceEngine🌠 and can do interstellar travels. Looking forward to battle space invaders.
👾👾👾👾