diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 2594d95df66b..c2d24608c72e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -859,7 +859,7 @@ object desugar { case IdPattern(named, tpt) => Function(derivedValDef(pat, named, tpt, EmptyTree, Modifiers(Param)) :: Nil, body) case _ => - makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, unchecked = false) + makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil) } /** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 4472c9f5e170..21d9a1040fc7 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -351,15 +351,17 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { /* (Nil, body) means that `body` is the default case * It's a bit hacky but it simplifies manipulations. */ - def extractSwitchCase(treeMakers: List[TreeMaker]): (List[Int], BodyTreeMaker) = treeMakers match { + def extractSwitchCase(treeMakers: List[TreeMaker]): (List[Int], BodyTreeMaker) = (treeMakers: @unchecked) match { // case 5 => case List(IntEqualityTestTreeMaker(intValue), body: BodyTreeMaker) => (List(intValue), body) // case 5 | 6 => case List(AlternativesTreeMaker(_, alts, _), body: BodyTreeMaker) => - val intValues = alts.map { - case List(IntEqualityTestTreeMaker(intValue)) => intValue + val intValues = alts.map { alt => + (alt: @unchecked) match { + case List(IntEqualityTestTreeMaker(intValue)) => intValue + } } (intValues, body) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 2f1c5f37f7f8..96c1da50b62e 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -2,16 +2,17 @@ package dotty.tools.dotc package transform package patmat -import core.Types._ -import core.Contexts._ -import core.Flags._ +import core._ +import Types._ +import Contexts._ +import Flags._ import ast.Trees._ import ast.tpd -import core.Decorators._ -import core.Symbols._ -import core.StdNames._ -import core.NameOps._ -import core.Constants._ +import Decorators._ +import Symbols._ +import StdNames._ +import NameOps._ +import Constants._ import reporting.diagnostic.messages._ import config.Printers.{ exhaustivity => debug } @@ -29,6 +30,7 @@ import config.Printers.{ exhaustivity => debug } * 3. A union of spaces `S1 | S2 | ...` is a space * 4. For a case class Kon(x1: T1, x2: T2, .., xn: Tn), if S1, S2, ..., Sn * are spaces, then `Kon(S1, S2, ..., Sn)` is a space. + * 5. `Fun(S1, S2, ..., Sn)` is an extractor space. * * For the problem of exhaustivity check, its formulation in terms of space is as follows: * @@ -63,6 +65,9 @@ case class Typ(tp: Type, decomposed: Boolean) extends Space /** Space representing a constructor pattern */ case class Kon(tp: Type, params: List[Space]) extends Space +/** Space representing an extractor pattern */ +case class Fun(tp: Type, fun: Type, params: List[Space]) extends Space + /** Union of spaces */ case class Or(spaces: List[Space]) extends Space @@ -97,20 +102,38 @@ trait SpaceLogic { /** Display space in string format */ def show(sp: Space): String - /** Simplify space using the laws, there's no nested union after simplify */ - def simplify(space: Space): Space = space match { + /** Simplify space using the laws, there's no nested union after simplify + * + * @param aggressive if true and OR space has less than 5 components, `simplify` will + * collapse `sp1 | sp2` to `sp1` if `sp2` is a subspace of `sp1`. + * + * This reduces noise in counterexamples. + */ + def simplify(space: Space, aggressive: Boolean = false): Space = space match { case Kon(tp, spaces) => - val sp = Kon(tp, spaces.map(simplify _)) + val sp = Kon(tp, spaces.map(simplify(_))) + if (sp.params.contains(Empty)) Empty + else sp + case Fun(tp, fun, spaces) => + val sp = Fun(tp, fun, spaces.map(simplify(_))) if (sp.params.contains(Empty)) Empty else sp case Or(spaces) => - val set = spaces.map(simplify _).flatMap { + val set = spaces.map(simplify(_)).flatMap { case Or(ss) => ss case s => Seq(s) } filter (_ != Empty) if (set.isEmpty) Empty else if (set.size == 1) set.toList(0) + else if (aggressive && spaces.size < 5) { + val res = set.map(sp => (sp, set.filter(_ ne sp))).find { + case (sp, sps) => + isSubspace(sp, Or(sps)) + } + if (res.isEmpty) Or(set) + else simplify(Or(res.get._2), aggressive) + } else Or(set) case Typ(tp, _) => if (canDecompose(tp) && decompose(tp).isEmpty) Empty @@ -137,26 +160,33 @@ trait SpaceLogic { def tryDecompose1(tp: Type) = canDecompose(tp) && isSubspace(Or(decompose(tp)), b) def tryDecompose2(tp: Type) = canDecompose(tp) && isSubspace(a, Or(decompose(tp))) - val res = (a, b) match { + val res = (simplify(a), b) match { case (Empty, _) => true case (_, Empty) => false - case (Or(ss), _) => ss.forall(isSubspace(_, b)) + case (Or(ss), _) => + ss.forall(isSubspace(_, b)) case (Typ(tp1, _), Typ(tp2, _)) => - isSubType(tp1, tp2) || tryDecompose1(tp1) || tryDecompose2(tp2) - case (Typ(tp1, _), Or(ss)) => + isSubType(tp1, tp2) + case (Typ(tp1, _), Or(ss)) => // optimization: don't go to subtraction too early ss.exists(isSubspace(a, _)) || tryDecompose1(tp1) + case (_, Or(_)) => + simplify(minus(a, b)) == Empty case (Typ(tp1, _), Kon(tp2, ss)) => - isSubType(tp1, tp2) && isSubspace(Kon(tp2, signature(tp2).map(Typ(_, false))), b) || - tryDecompose1(tp1) + isSubType(tp1, tp2) && isSubspace(Kon(tp2, signature(tp2).map(Typ(_, false))), b) case (Kon(tp1, ss), Typ(tp2, _)) => - isSubType(tp1, tp2) || - simplify(a) == Empty || - (isSubType(tp2, tp1) && tryDecompose1(tp1)) || - tryDecompose2(tp2) - case (Kon(_, _), Or(_)) => - simplify(minus(a, b)) == Empty + isSubType(tp1, tp2) case (Kon(tp1, ss1), Kon(tp2, ss2)) => isEqualType(tp1, tp2) && ss1.zip(ss2).forall((isSubspace _).tupled) + case (Fun(tp1, fun, ss), Typ(tp2, _)) => + isSubType(tp1, tp2) + case (Typ(tp2, _), Fun(tp1, fun, ss)) => + false // approximation: assume a type can never be fully matched by an extractor + case (Kon(_, _), Fun(_, _, _)) => + false // approximation + case (Fun(_, _, _), Kon(_, _)) => + false // approximation + case (Fun(_, fun1, ss1), Fun(_, fun2, ss2)) => + isEqualType(fun1, fun2) && ss1.zip(ss2).forall((isSubspace _).tupled) } debug.println(s"${show(a)} < ${show(b)} = $res") @@ -169,7 +199,7 @@ trait SpaceLogic { def tryDecompose1(tp: Type) = intersect(Or(decompose(tp)), b) def tryDecompose2(tp: Type) = intersect(a, Or(decompose(tp))) - val res = (a, b) match { + val res: Space = (a, b) match { case (Empty, _) | (_, Empty) => Empty case (_, Or(ss)) => Or(ss.map(intersect(a, _)).filterConserve(_ ne Empty)) case (Or(ss), _) => Or(ss.map(intersect(_, b)).filterConserve(_ ne Empty)) @@ -193,6 +223,24 @@ trait SpaceLogic { if (!isEqualType(tp1, tp2)) Empty else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty else Kon(tp1, ss1.zip(ss2).map((intersect _).tupled)) + case (Typ(tp1, _), Fun(tp2, _, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) b // prefer extractor space for better approximation + else if (canDecompose(tp1)) tryDecompose1(tp1) + else Empty + case (Fun(tp1, _, _), Typ(tp2, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) a + else if (canDecompose(tp2)) tryDecompose2(tp2) + else Empty + case (Fun(tp1, _, _), Kon(tp2, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) a + else Empty + case (Kon(tp1, _), Fun(tp2, _, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) b + else Empty + case (Fun(tp1, fun1, ss1), Fun(tp2, fun2, ss2)) => + if (!isEqualType(fun1, fun2)) Empty + else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty + else Fun(tp1, fun1, ss1.zip(ss2).map((intersect _).tupled)) } debug.println(s"${show(a)} & ${show(b)} = ${show(res)}") @@ -238,6 +286,25 @@ trait SpaceLogic { Or(ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1).map { case (ri, i) => Kon(tp1, ss1.updated(i, ri)) }) + case (Fun(tp1, _, _), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) Empty + else a + case (Typ(tp1, _), Fun(tp2, _, _)) => + a // approximation + case (Fun(_, _, _), Kon(_, _)) => + a + case (Kon(_, _), Fun(_, _, _)) => + a + case (Fun(tp1, fun1, ss1), Fun(tp2, fun2, ss2)) => + if (!isEqualType(fun1, fun2)) a + else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) a + else if (ss1.zip(ss2).forall((isSubspace _).tupled)) Empty + else + // `(_, _, _) - (Some, None, _)` becomes `(None, _, _) | (_, Some, _) | (_, _, Empty)` + Or(ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1).map { + case (ri, i) => Fun(tp1, fun1, ss1.updated(i, ri)) + }) + } debug.println(s"${show(a)} - ${show(b)} = ${show(res)}") @@ -263,6 +330,12 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { import SpaceEngine._ import tpd._ + private val scalaSomeClass = ctx.requiredClass("scala.Some") + private val scalaSeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory") + private val scalaListType = ctx.requiredClassRef("scala.collection.immutable.List") + private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil") + private val scalaConsType = ctx.requiredClassRef("scala.collection.immutable.::") + /** Checks if it's possible to create a trait/class which is a subtype of `tp`. * * - doesn't handle member collisions (will not declare a type unimplementable because of one) @@ -337,11 +410,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } /** Return the space that represents the pattern `pat` - * - * If roundUp is true, approximate extractors to its type, - * otherwise approximate extractors to Empty */ - def project(pat: Tree, roundUp: Boolean = true)(implicit ctx: Context): Space = pat match { + def project(pat: Tree): Space = pat match { case Literal(c) => if (c.value.isInstanceOf[Symbol]) Typ(c.value.asInstanceOf[Symbol].termRef, false) @@ -350,20 +420,40 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case _: BackquotedIdent => Typ(pat.tpe, false) case Ident(_) | Select(_, _) => Typ(pat.tpe.stripAnnots, false) - case Alternative(trees) => Or(trees.map(project(_, roundUp))) + case Alternative(trees) => Or(trees.map(project(_))) case Bind(_, pat) => project(pat) - case UnApply(_, _, pats) => + case UnApply(fun, _, pats) => if (pat.tpe.classSymbol.is(CaseClass)) - // FIXME: why dealias is needed here? - Kon(pat.tpe.stripAnnots.dealias, pats.map(pat => project(pat, roundUp))) - else if (roundUp) Typ(pat.tpe.stripAnnots, false) - else Empty + Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) + else if (fun.symbol.owner == scalaSeqFactoryClass && fun.symbol.name == nme.unapplySeq) + projectList(pats) + else if (fun.symbol.info.finalResultType.isRef(scalaSomeClass)) + Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) + else + Fun(pat.tpe.stripAnnots, fun.tpe, pats.map(pat => project(pat))) case Typed(pat @ UnApply(_, _, _), _) => project(pat) case Typed(expr, _) => Typ(expr.tpe.stripAnnots, true) case _ => Empty } + + /** Space of the pattern: List(a, b, c: _*) + */ + def projectList(pats: List[Tree]): Space = { + if (pats.isEmpty) return Typ(scalaNilType, false) + + val (items, zero) = if (pats.last.tpe.isRepeatedParam) + (pats.init, Typ(scalaListType.appliedTo(pats.head.tpe.widen), false)) + else + (pats, Typ(scalaNilType, false)) + + items.foldRight[Space](zero) { (pat, acc) => + Kon(scalaConsType.appliedTo(pats.head.tpe.widen), project(pat) :: acc :: Nil) + } + } + + /* Erase a type binding according to erasure semantics in pattern matching */ def erase(tp: Type): Type = { def doErase(tp: Type): Type = tp match { @@ -385,8 +475,19 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /** Is `tp1` a subtype of `tp2`? */ def isSubType(tp1: Type, tp2: Type): Boolean = { - // check SI-9657 and tests/patmat/gadt.scala - val res = erase(tp1) <:< erase(tp2) + // `erase` is a workaround to make the following code pass the check: + // + // def f(e: Either[Int, String]) = e match { + // case Left(i) => i + // case Right(s) => 0 + // } + // + // The problem is that when decompose `Either[Int, String]`, `Type.wrapIfMember` + // only refines the type member inherited from `Either` -- it's complex to refine + // the type members in `Left` and `Right`. + // + // FIXME: remove this hack + val res = tp1 <:< erase(tp2) debug.println(s"${tp1.show} <:< ${tp2.show} = $res") res } @@ -426,14 +527,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case space => List(space) } case OrType(tp1, tp2) => List(Typ(tp1, true), Typ(tp2, true)) - case _ if tp =:= ctx.definitions.BooleanType => + case tp if tp.isRef(defn.BooleanClass) => List( Typ(ConstantType(Constant(true)), true), Typ(ConstantType(Constant(false)), true) ) - case _ if tp.classSymbol.is(Enum) => + case tp if tp.classSymbol.is(Enum) => children.map(sym => Typ(sym.termRef, true)) - case _ => + case tp => val parts = children.map { sym => if (sym.is(ModuleClass)) refine(tp, sym.sourceModule.termRef) @@ -485,7 +586,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val and = dealiasedTp.asInstanceOf[AndType] canDecompose(and.tp1) || canDecompose(and.tp2) }) || - tp =:= ctx.definitions.BooleanType || + tp.isRef(defn.BooleanClass) || tp.classSymbol.is(allOf(Enum, Sealed)) // Enum value doesn't have Sealed flag debug.println(s"decomposable: ${tp.show} = $res") @@ -549,7 +650,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (ctx.definitions.isTupleType(tp)) signature(tp).map(_ => "_").mkString("(", ", ", ")") - else if (sym.showFullName == "scala.collection.immutable.::") + else if (scalaListType.isRef(sym)) + if (mergeList) "_*" else "_: List" + else if (scalaConsType.isRef(sym)) if (mergeList) "_" else "List(_)" else if (tp.classSymbol.is(CaseClass)) // use constructor syntax for case class @@ -561,11 +664,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case Kon(tp, params) => if (ctx.definitions.isTupleType(tp)) "(" + params.map(doShow(_)).mkString(", ") + ")" - else if (tp.widen.classSymbol.showFullName == "scala.collection.immutable.::") + else if (tp.isRef(scalaConsType.symbol)) if (mergeList) params.map(doShow(_, mergeList)).mkString(", ") else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")") else showType(tp) + params.map(doShow(_)).mkString("(", ", ", ")") + case Fun(tp, fun, params) => + showType(fun) + params.map(doShow(_)).mkString("(", ", ", ")") case Or(_) => throw new Exception("incorrect flatten result " + s) } @@ -586,7 +691,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val and = tp.asInstanceOf[AndType] isCheckable(and.tp1) || isCheckable(and.tp2) }) || - tp.typeSymbol == ctx.definitions.BooleanType.typeSymbol || + tp.isRef(defn.BooleanClass) || tp.typeSymbol.is(Enum) || canDecompose(tp) || (defn.isTupleType(tp) && tp.dealias.argInfos.exists(isCheckable(_))) @@ -664,13 +769,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val patternSpace = cases.map({ x => val space = project(x.pat) - debug.println(s"${x.pat.show} projects to ${show(space)}") + debug.println(s"${x.pat.show} ====> ${show(space)}") space }).reduce((a, b) => Or(List(a, b))) - val uncovered = simplify(minus(Typ(selTyp, true), patternSpace)) + val uncovered = simplify(minus(Typ(selTyp, true), patternSpace), aggressive = true) if (uncovered != Empty) - ctx.warning(PatternMatchExhaustivity(show(uncovered)), _match.pos) + ctx.warning(PatternMatchExhaustivity(show(uncovered)), sel.pos) } def checkRedundancy(_match: Match): Unit = { @@ -678,17 +783,21 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // ignore selector type for now // val selTyp = sel.tpe.widen.deAnonymize.dealias + if (cases.length == 1) return + // starts from the second, the first can't be redundant (1 until cases.length).foreach { i => - // in redundancy check, take guard as false, take extractor as match - // nothing in order to soundly approximate + // in redundancy check, take guard as false in order to soundly approximate val prevs = cases.take(i).map { x => - if (x.guard.isEmpty) project(x.pat, false) + if (x.guard.isEmpty) project(x.pat) else Empty }.reduce((a, b) => Or(List(a, b))) val curr = project(cases(i).pat) + debug.println(s"---------------reachable? ${show(curr)}") + debug.println(s"prev: ${show(prevs)}") + if (isSubspace(curr, prevs)) { ctx.warning(MatchCaseUnreachable(), cases(i).body.pos) } diff --git a/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala b/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala index 1d49a4f4f12e..c4957b66efff 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala @@ -64,7 +64,7 @@ final case class Text(text: String) extends Inline abstract class EntityLink(val title: Inline) extends Inline { def link: LinkTo } object EntityLink { def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo } - def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link)) + def unapply(el: EntityLink): Some[(Inline, LinkTo)] = Some((el.title, el.link)) } final case class HtmlTag(data: String) extends Inline { private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala index bc4a74c4fc17..8e82041f386a 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala @@ -104,6 +104,10 @@ object tags { title case ConstantReference(title) => title + + case _ => // EmptyReference + ctx.docbase.error(s"invalid reference: $ref") + null } override def render(tctx: TemplateContext, nodes: LNode*): AnyRef = nodes(0).render(tctx) match { case map: JMap[String, AnyRef] @unchecked => diff --git a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala index bb09a709012b..69929c7cdafa 100644 --- a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala +++ b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala @@ -77,6 +77,8 @@ trait MemberLookup { case (x :: xs, _) => if (xs.nonEmpty) globalLookup else lookup(entity, packages, "scala." + query) + case (Nil, _) => + throw new IllegalArgumentException("`query` cannot be empty") } } diff --git a/tests/patmat/dotty-unreachable.scala b/tests/patmat/dotty-unreachable.scala new file mode 100644 index 000000000000..f6b5aced15bb --- /dev/null +++ b/tests/patmat/dotty-unreachable.scala @@ -0,0 +1,17 @@ +object Types { + abstract case class TermRef(val prefix: String, name: String) { + type ThisType = TermRef + + def alts: List[TermRef] = ??? + } +} + +class Test { + def foo(tp: Types.TermRef): Unit = { + tp.alts.filter(_.name == "apply") match { + case Nil => + case alt :: Nil => + case alt => + } + } +} \ No newline at end of file diff --git a/tests/patmat/dotty.scala b/tests/patmat/dotty.scala new file mode 100644 index 000000000000..509c0a68b4ba --- /dev/null +++ b/tests/patmat/dotty.scala @@ -0,0 +1,30 @@ +object IntEqualityTestTreeMaker { + def unapply(xs: Int): Option[Int] = ??? +} + +class Test { + def isBelow(n: Int, s: String): Boolean = false + + def foo(xs: List[(Int, String)]): Unit = (xs filter (isBelow _).tupled) match { + case Nil => + case matches => + } + + def linkCompanions(xs: List[(Int, Int)]): Unit = { + xs.groupBy(_._1).foreach { + case (_, List(x1, x2)) => + case _ => () + } + } + + def bar(xs: List[(Int, String)]): Unit = xs match { + case (x, s) :: Nil => + case Nil => + case _ => + } + + def patmat(alts: List[List[Int]]): Unit = alts.forall { + case List(IntEqualityTestTreeMaker(_)) => false + case _ => true + } +} \ No newline at end of file diff --git a/tests/patmat/exhausting.check b/tests/patmat/exhausting.check index b8d1d8408f4d..6cb6db364892 100644 --- a/tests/patmat/exhausting.check +++ b/tests/patmat/exhausting.check @@ -1,6 +1,6 @@ 21: Pattern Match Exhaustivity: List(_), List(_, _, _) 27: Pattern Match Exhaustivity: Nil -32: Pattern Match Exhaustivity: List(_, _) +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) diff --git a/tests/patmat/for.scala b/tests/patmat/for.scala index ae9dcf65e9f1..4c2ed262fc30 100644 --- a/tests/patmat/for.scala +++ b/tests/patmat/for.scala @@ -2,4 +2,6 @@ object Test { def foo[A, B](l: List[(A, B)]): List[A] = { for ((a, b) <- l) yield a } + + def bar(xs: List[(Int, List[Int])]): Unit = for ( (_, x :: y :: xs) <- xs) yield x } \ No newline at end of file diff --git a/tests/patmat/i2363.check b/tests/patmat/i2363.check new file mode 100644 index 000000000000..482144a87e72 --- /dev/null +++ b/tests/patmat/i2363.check @@ -0,0 +1,2 @@ +15: Pattern Match Exhaustivity: List(_, _*) +21: Pattern Match Exhaustivity: _: Expr \ No newline at end of file diff --git a/tests/patmat/i2363.scala b/tests/patmat/i2363.scala new file mode 100644 index 000000000000..e910178ee47e --- /dev/null +++ b/tests/patmat/i2363.scala @@ -0,0 +1,25 @@ +sealed trait Expr +class IntExpr extends Expr +class BooleanExpr extends Expr + +object IntExpr { + def unapply(expr: Expr): Option[IntExpr] = ??? +} + +object BooleanExpr { + def unapply(expr: Expr): Option[BooleanExpr] = ??? +} + + +class Test { + def foo(x: List[Expr]): Int = x match { + case IntExpr(_) :: xs => 1 + case BooleanExpr(_) :: xs => 1 + case Nil => 2 + } + + def bar(x: Expr): Int = x match { + case IntExpr(_) => 1 + case BooleanExpr(_) => 2 + } +} diff --git a/tests/patmat/optionless.check b/tests/patmat/optionless.check new file mode 100644 index 000000000000..350b9178c50d --- /dev/null +++ b/tests/patmat/optionless.check @@ -0,0 +1,3 @@ +20: Pattern Match Exhaustivity: _: Tree +24: Pattern Match Exhaustivity: _: Tree +28: Pattern Match Exhaustivity: _: Tree diff --git a/tests/patmat/optionless.scala b/tests/patmat/optionless.scala new file mode 100644 index 000000000000..9afc43062caa --- /dev/null +++ b/tests/patmat/optionless.scala @@ -0,0 +1,32 @@ +sealed trait Tree +case class Ident(name: String) extends Tree + +object Ident1 { + def unapply(tree: Tree): Ident = ??? +} + +trait Cap +object Ident2 { + def unapply(tree: Tree)(implicit cap: Cap): Ident = ??? +} + +object Ident3 { + def unapply(tree: Tree)(implicit cap: Cap): Option[Ident] = ??? +} + + + +class Test { + def foo(t: Tree): Unit = t match { + case Ident1(t) => + } + + def bar(t: Tree)(implicit c: Cap): Unit = t match { + case Ident2(t) => + } + + def qux(t: Tree)(implicit c: Cap): Unit = t match { + case Ident3(t) => + } + +} \ No newline at end of file diff --git a/tests/patmat/patmat-adt.check b/tests/patmat/patmat-adt.check index 4adcdc49cd2f..7a938792fee8 100644 --- a/tests/patmat/patmat-adt.check +++ b/tests/patmat/patmat-adt.check @@ -1,5 +1,5 @@ 7: Pattern Match Exhaustivity: Bad(Good(_)), Good(Bad(_)) 19: Pattern Match Exhaustivity: Some(_) -24: Pattern Match Exhaustivity: (None, Some(_)), (_, Some(_)) +24: Pattern Match Exhaustivity: (_, Some(_)) 29: Pattern Match Exhaustivity: (None, None), (Some(_), Some(_)) 50: Pattern Match Exhaustivity: LetL(BooleanLit), LetL(IntLit) diff --git a/tests/patmat/patmat-extractor.check b/tests/patmat/patmat-extractor.check new file mode 100644 index 000000000000..d7ef0e3d2691 --- /dev/null +++ b/tests/patmat/patmat-extractor.check @@ -0,0 +1,2 @@ +13: Pattern Match Exhaustivity: _: Node +15: Match case Unreachable diff --git a/tests/patmat/patmatexhaust.check b/tests/patmat/patmatexhaust.check index 3de93cfdbbd4..e5e7ac397383 100644 --- a/tests/patmat/patmatexhaust.check +++ b/tests/patmat/patmatexhaust.check @@ -2,6 +2,7 @@ 11: Pattern Match Exhaustivity: Bar(_) 23: Pattern Match Exhaustivity: (Qult(), Qult()), (Kult(_), Kult(_)) 49: Pattern Match Exhaustivity: _: Gp +59: Pattern Match Exhaustivity: Nil 75: Pattern Match Exhaustivity: _: B 100: Pattern Match Exhaustivity: _: C1 114: Pattern Match Exhaustivity: D2(), D1 diff --git a/tests/patmat/t6420.check b/tests/patmat/t6420.check index c1570159461a..a96c7b64bd0e 100644 --- a/tests/patmat/t6420.check +++ b/tests/patmat/t6420.check @@ -1 +1 @@ -5: Pattern Match Exhaustivity: (Nil, _), (List(true, _), _), (List(false, _), _), (_, Nil), (_, List(true, _)), (_, List(false, _)) +5: Pattern Match Exhaustivity: (_: List, Nil), (_: List, List(true, _*)), (_: List, List(false, _*)) diff --git a/tests/patmat/t7020.check b/tests/patmat/t7020.check index a44384946184..410b921957fe 100644 --- a/tests/patmat/t7020.check +++ b/tests/patmat/t7020.check @@ -1,4 +1,4 @@ -3: Pattern Match Exhaustivity: List(_, _) -10: Pattern Match Exhaustivity: List(_, _) -17: Pattern Match Exhaustivity: List(_, _) -24: Pattern Match Exhaustivity: List(_, _) +3: Pattern Match Exhaustivity: List(_, _*) +10: Pattern Match Exhaustivity: List(_, _*) +17: Pattern Match Exhaustivity: List(_, _*) +24: Pattern Match Exhaustivity: List(_, _*) diff --git a/tests/patmat/t7466.check b/tests/patmat/t7466.check index af596399b06f..dfd3b3061f03 100644 --- a/tests/patmat/t7466.check +++ b/tests/patmat/t7466.check @@ -1 +1 @@ -8: Pattern Match Exhaustivity: (true, _), (false, _), (_, true), (_, false) +8: Pattern Match Exhaustivity: (_, true), (_, false) diff --git a/tests/patmat/t9129.check b/tests/patmat/t9129.check index 3236bb049a5b..f14f34ec9cfb 100644 --- a/tests/patmat/t9129.check +++ b/tests/patmat/t9129.check @@ -1 +1 @@ -21: Pattern Match Exhaustivity: Two(B2, A2), Two(_, A2) +21: Pattern Match Exhaustivity: Two(_, A2) diff --git a/tests/patmat/t9232.check b/tests/patmat/t9232.check index 36949147a2e7..e2372315727f 100644 --- a/tests/patmat/t9232.check +++ b/tests/patmat/t9232.check @@ -1 +1 @@ -13: Pattern Match Exhaustivity: Node2() +13: Pattern Match Exhaustivity: Node2(), Node1(Foo(_)) diff --git a/tests/patmat/t9351.check b/tests/patmat/t9351.check index bce053c947bd..6a785fa26077 100644 --- a/tests/patmat/t9351.check +++ b/tests/patmat/t9351.check @@ -1,3 +1,3 @@ 8: Pattern Match Exhaustivity: _: A -17: Pattern Match Exhaustivity: (_, _), (_, None), (_, Some(_)) +17: Pattern Match Exhaustivity: (_, None), (_, Some(_)) 28: Pattern Match Exhaustivity: (_, _)