diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index ce9280d827b8..6442d2feec22 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -8,12 +8,8 @@ import Symbols._ import Types._ import Scopes._ import typer.{FrontEnd, Typer, ImportInfo, RefChecks} -import reporting.{Reporter, ConsoleReporter} import Phases.Phase import transform._ -import transform.TreeTransforms.{TreeTransform, TreeTransformer} -import core.DenotTransformers.DenotTransformer -import core.Denotations.SingleDenotation import dotty.tools.backend.jvm.{LabelDefs, GenBCode} import dotty.tools.backend.sjs.GenSJSIR diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index f603f68178a3..12ca8c14cea4 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -616,16 +616,20 @@ object desugar { * * { cases } * ==> - * x$1 => x$1 match { cases } + * x$1 => (x$1 @unchecked) match { cases } * * If `nparams` != 1, expand instead to * - * (x$1, ..., x$n) => (x$0, ..., x${n-1}) match { cases } + * (x$1, ..., x$n) => (x$0, ..., x${n-1} @unchecked) match { cases } */ - def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1)(implicit ctx: Context) = { + def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1, unchecked: Boolean = true)(implicit ctx: Context) = { val params = (1 to nparams).toList.map(makeSyntheticParameter(_)) val selector = makeTuple(params.map(p => Ident(p.name))) - Function(params, Match(selector, cases)) + + if (unchecked) + Function(params, Match(Annotated(New(ref(defn.UncheckedAnnotType)), selector), cases)) + else + Function(params, Match(selector, cases)) } /** Map n-ary function `(p1, ..., pn) => body` where n != 1 to unary function as follows: @@ -753,7 +757,7 @@ object desugar { case VarPattern(named, tpt) => Function(derivedValDef(named, tpt, EmptyTree, Modifiers(Param)) :: Nil, body) case _ => - makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil) + makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, unchecked = false) } /** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap @@ -799,7 +803,7 @@ object desugar { val cases = List( CaseDef(pat, EmptyTree, Literal(Constant(true))), CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))) - Apply(Select(rhs, nme.withFilter), Match(EmptyTree, cases)) + Apply(Select(rhs, nme.withFilter), makeCaseLambda(cases)) } /** Is pattern `pat` irrefutable when matched against `rhs`? diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 4b2861452d0c..fc7623f94ebc 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -1,17 +1,17 @@ package dotty.tools.dotc package core -import Periods._ import Contexts._ -import dotty.tools.backend.jvm.{LabelDefs, GenBCode} +import dotty.tools.backend.jvm.{GenBCode, LabelDefs} import dotty.tools.dotc.core.Symbols.ClassSymbol import util.DotClass import DenotTransformers._ import Denotations._ import Decorators._ import config.Printers._ -import scala.collection.mutable.{ListBuffer, ArrayBuffer} -import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform} + +import scala.collection.mutable.ListBuffer +import dotty.tools.dotc.transform.TreeTransforms.{MiniPhase, TreeTransformer} import dotty.tools.dotc.transform._ import Periods._ import typer.{FrontEnd, RefChecks} diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index f514a329e1b9..3c051b952c45 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -810,6 +810,13 @@ object Types { case _ => this } + /** Eliminate anonymous classes */ + final def deAnonymize(implicit ctx: Context): Type = this match { + case tp:TypeRef if tp.symbol.isAnonymousClass => + tp.symbol.asClass.typeRef.asSeenFrom(tp.prefix, tp.symbol.owner) + case tp => tp + } + /** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type * is no longer alias type, LazyRef, or instantiated type variable. */ diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index f7a69aa53f28..e159162ccdcc 100644 --- a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -90,6 +90,7 @@ class ClassfileParser( val jflags = in.nextChar val isAnnotation = hasAnnotation(jflags) val sflags = classTranslation.flags(jflags) + val isEnum = (jflags & JAVA_ACC_ENUM) != 0 val nameIdx = in.nextChar currentClassName = pool.getClassName(nameIdx) @@ -145,6 +146,15 @@ class ClassfileParser( setClassInfo(classRoot, classInfo) setClassInfo(moduleRoot, staticInfo) } + + // eager load java enum definitions for exhaustivity check of pattern match + if (isEnum) { + instanceScope.toList.map(_.ensureCompleted()) + staticScope.toList.map(_.ensureCompleted()) + classRoot.setFlag(Flags.Enum) + moduleRoot.setFlag(Flags.Enum) + } + result } diff --git a/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/src/dotty/tools/dotc/transform/ExpandSAMs.scala index d9445d04673e..04c6864b1cd2 100644 --- a/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -74,7 +74,8 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer => Bind(defaultSym, Underscore(selector.tpe.widen)), EmptyTree, Literal(Constant(false))) - cpy.Match(applyRhs)(paramRef, cases.map(translateCase) :+ defaultCase) + val annotated = Annotated(New(ref(defn.UncheckedAnnotType)), paramRef) + cpy.Match(applyRhs)(annotated, cases.map(translateCase) :+ defaultCase) case _ => tru } diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 974053769233..accdc762f66d 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -24,6 +24,7 @@ import Applications._ import TypeApplications._ import SymUtils._, core.NameOps._ import core.Mode +import patmat._ import dotty.tools.dotc.util.Positions.Position import dotty.tools.dotc.core.Decorators._ @@ -52,6 +53,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans override def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = { val translated = new Translator()(ctx).translator.translateMatch(tree) + // check exhaustivity and unreachability + val engine = new SpaceEngine + if (engine.checkable(tree)) { + engine.checkExhaustivity(tree) + engine.checkRedundancy(tree) + } + translated.ensureConforms(tree.tpe) } @@ -1246,13 +1254,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans case _ => false } - def elimAnonymousClass(t: Type) = t match { - case t:TypeRef if t.symbol.isAnonymousClass => - t.symbol.asClass.typeRef.asSeenFrom(t.prefix, t.symbol.owner) - case _ => - t - } - /** Implement a pattern match by turning its cases (including the implicit failure case) * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. * @@ -1266,7 +1267,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans def translateMatch(match_ : Match): Tree = { val Match(sel, cases) = match_ - val selectorTp = elimAnonymousClass(sel.tpe.widen/*withoutAnnotations*/) + val selectorTp = sel.tpe.widen.deAnonymize/*withoutAnnotations*/ val selectorSym = freshSym(sel.pos, selectorTp, "selector") @@ -1275,6 +1276,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans case _ => (cases, None) } + // checkMatchVariablePatterns(nonSyntheticCases) // only used for warnings // we don't transform after uncurry diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index fcde59b24a03..4bfb79726aa6 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -38,6 +38,8 @@ import Symbols._, TypeUtils._ * * (9) Adds SourceFile annotations to all top-level classes and objects * + * (10) Adds Child annotations to all sealed classes + * * The reason for making this a macro transform is that some functions (in particular * super and protected accessors and instantiation checks) are naturally top-down and * don't lend themselves to the bottom-up approach of a mini phase. The other two functions @@ -231,6 +233,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran ctx.compilationUnit.source.exists && sym != defn.SourceFileAnnot) sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path)) + + if (!sym.isAnonymousClass) // ignore anonymous class + for (parent <- sym.asClass.classInfo.classParents) { + val pclazz = parent.classSymbol + if (pclazz.is(Sealed)) pclazz.addAnnotation(Annotation.makeChild(sym)) + } + tree } else { diff --git a/src/dotty/tools/dotc/transform/patmat/Space.scala b/src/dotty/tools/dotc/transform/patmat/Space.scala new file mode 100644 index 000000000000..0829e6c34161 --- /dev/null +++ b/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -0,0 +1,626 @@ +package dotty.tools.dotc +package transform +package patmat + +import core.Types._ +import core.Contexts._ +import core.Flags._ +import ast.Trees._ +import ast.tpd +import core.Decorators._ +import core.Symbols._ +import core.StdNames._ +import core.NameOps._ +import core.Constants._ + +/** Space logic for checking exhaustivity and unreachability of pattern matching + * + * Space can be thought of as a set of possible values. A type or a pattern + * both refer to spaces. The space of a type is the values that inhabit the + * type. The space of a pattern is the values that can be covered by the + * pattern. + * + * Space is recursively defined as follows: + * + * 1. `Empty` is a space + * 2. For a type T, `Typ(T)` is a space + * 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. A constant `Const(value, T)` is a point in space + * 6. A stable identifier `Var(sym, T)` is a space + * + * For the problem of exhaustivity check, its formulation in terms of space is as follows: + * + * Is the space Typ(T) a subspace of the union of space covered by all the patterns? + * + * The problem of unreachable patterns can be formulated as follows: + * + * Is the space covered by a pattern a subspace of the space covered by previous patterns? + */ + + +/** space definition */ +sealed trait Space + +/** Empty space */ +case object Empty extends Space + +/** Space representing the set of all values of a type + * + * @param tp: the type this space represents + * @param decomposed: does the space result from decomposition? Used for pretty print + * + * Note: When transform patterns like `_: List[T]` into space, the type parameter is + * erased. E.g., `_: List[Int]` will be erased to `Typ(List, _)`. + * + * However, the scrutinee of pattern matching is not erased in order to make + * pattern matching for GADT more precise. + */ +case class Typ(tp: Type, decomposed: Boolean) extends Space + +/** Space representing a constructor pattern */ +case class Kon(tp: Type, params: List[Space]) extends Space + +/** Union of spaces */ +case class Or(spaces: List[Space]) extends Space + +/** Point in space */ +sealed trait Point extends Space + +/** Point representing variables(stable identifier) in patterns */ +case class Var(sym: Symbol, tp: Type) extends Point + +/** Point representing literal constants in patterns */ +case class Const(value: Constant, tp: Type) extends Point + +/** abstract space logic */ +trait SpaceLogic { + /** Is `tp1` a subtype of `tp2`? */ + def isSubType(tp1: Type, tp2: Type): Boolean + + /** Is `tp1` the same type as `tp2`? */ + def isEqualType(tp1: Type, tp2: Type): Boolean + + /** Is `tp` a case class? + * + * Assumption: + * (1) One case class cannot be inherited directly or indirectly by another + * case class. + * (2) Inheritance of a case class cannot be well handled by the algorithm. + */ + def isCaseClass(tp: Type): Boolean + + /** Is the type `tp` decomposable? i.e. all values of the type can be covered + * by its decomposed types. + * + * Abstract sealed class, OrType, Boolean and Java enums can be decomposed. + */ + def canDecompose(tp: Type): Boolean + + /** Return term parameter types of the case class `tp` */ + def signature(tp: Type): List[Type] + + /** Get components of decomposable types */ + def decompose(tp: Type): List[Space] + + /** Simplify space using the laws, there's no nested union after simplify */ + def simplify(space: Space): Space = space match { + case Kon(tp, spaces) => + val sp = Kon(tp, spaces.map(simplify _)) + if (sp.params.contains(Empty)) Empty + else sp + case Or(spaces) => + 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 Or(set) + case Typ(tp, _) => + if (canDecompose(tp) && decompose(tp).isEmpty) Empty + else space + case _ => space + } + + /** Flatten space to get rid of `Or` for pretty print */ + def flatten(space: Space): List[Space] = space match { + case Kon(tp, spaces) => + val flats = spaces.map(flatten _) + + flats.foldLeft(List[Kon]()) { (acc, flat) => + if (acc.isEmpty) flat.map(s => Kon(tp, Nil :+ s)) + else for (Kon(tp, ss) <- acc; s <- flat) yield Kon(tp, ss :+ s) + } + case Or(spaces) => + spaces.flatMap(flatten _) + case _ => List(space) + } + + /** Is `a` a subspace of `b`? Equivalent to `a - b == Empty`, but faster */ + def isSubspace(a: Space, b: Space): Boolean = { + def tryDecompose1(tp: Type) = canDecompose(tp) && isSubspace(Or(decompose(tp)), b) + def tryDecompose2(tp: Type) = canDecompose(tp) && isSubspace(a, Or(decompose(tp))) + + (a, b) match { + case (Empty, _) => true + case (_, Empty) => false + case (Or(ss), _) => ss.forall(isSubspace(_, b)) + case (Typ(tp1, _), Typ(tp2, _)) => + isSubType(tp1, tp2) || tryDecompose1(tp1) || tryDecompose2(tp2) + case (Typ(tp1, _), Or(ss)) => + ss.exists(isSubspace(a, _)) || tryDecompose1(tp1) + case (Typ(tp1, _), Kon(tp2, ss)) => + isSubType(tp1, tp2) && isSubspace(Kon(tp2, signature(tp2).map(Typ(_, false))), b) || + tryDecompose1(tp1) + 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 + case (Kon(tp1, ss1), Kon(tp2, ss2)) => + isEqualType(tp1, tp2) && ss1.zip(ss2).forall((isSubspace _).tupled) + case (Const(v1, _), Const(v2, _)) => v1 == v2 + case (Const(_, tp1), Typ(tp2, _)) => isSubType(tp1, tp2) || tryDecompose2(tp2) + case (Const(_, _), Or(ss)) => ss.exists(isSubspace(a, _)) + case (Const(_, _), _) => false + case (_, Const(_, _)) => false + case (Var(x, _), Var(y, _)) => x == y + case (Var(_, tp1), Typ(tp2, _)) => isSubType(tp1, tp2) || tryDecompose2(tp2) + case (Var(_, _), Or(ss)) => ss.exists(isSubspace(a, _)) + case (Var(_, _), _) => false + case (_, Var(_, _)) => false + } + } + + /** Intersection of two spaces */ + def intersect(a: Space, b: Space): Space = { + def tryDecompose1(tp: Type) = intersect(Or(decompose(tp)), b) + def tryDecompose2(tp: Type) = intersect(a, Or(decompose(tp))) + + (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)) + case (Typ(tp1, _), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) a + else if (isSubType(tp2, tp1)) b + else if (canDecompose(tp1)) tryDecompose1(tp1) + else if (canDecompose(tp2)) tryDecompose2(tp2) + else Empty + case (Typ(tp1, _), Kon(tp2, ss)) => + if (isSubType(tp2, tp1)) b + else if (isSubType(tp1, tp2)) a // problematic corner case: inheriting a case class + else if (canDecompose(tp1)) tryDecompose1(tp1) + else Empty + case (Kon(tp1, ss), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) a + else if (isSubType(tp2, tp1)) a // problematic corner case: inheriting a case class + else if (canDecompose(tp2)) tryDecompose2(tp2) + else Empty + case (Kon(tp1, ss1), Kon(tp2, ss2)) => + 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 (Const(v1, _), Const(v2, _)) => + if (v1 == v2) a else Empty + case (Const(_, tp1), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) a + else if (canDecompose(tp2)) tryDecompose2(tp2) + else Empty + case (Const(_, _), _) => Empty + case (Typ(tp1, _), Const(_, tp2)) => + if (isSubType(tp2, tp1)) b + else if (canDecompose(tp1)) tryDecompose1(tp1) + else Empty + case (_, Const(_, _)) => Empty + case (Var(x, _), Var(y, _)) => + if (x == y) a else Empty + case (Var(_, tp1), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) a + else if (canDecompose(tp2)) tryDecompose2(tp2) + else Empty + case (Var(_, _), _) => Empty + case (Typ(tp1, _), Var(_, tp2)) => + if (isSubType(tp2, tp1)) b + else if (canDecompose(tp1)) tryDecompose1(tp1) + else Empty + case (_, Var(_, _)) => Empty + } + } + + /** The space of a not covered by b */ + def minus(a: Space, b: Space): Space = { + def tryDecompose1(tp: Type) = minus(Or(decompose(tp)), b) + def tryDecompose2(tp: Type) = minus(a, Or(decompose(tp))) + + (a, b) match { + case (Empty, _) => Empty + case (_, Empty) => a + case (Typ(tp1, _), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) Empty + else if (canDecompose(tp1)) tryDecompose1(tp1) + else if (canDecompose(tp2)) tryDecompose2(tp2) + else a + case (Typ(tp1, _), Kon(tp2, ss)) => + // corner case: inheriting a case class + // rationale: every instance of `tp1` is covered by `tp2(_)` + if (isSubType(tp1, tp2)) minus(Kon(tp2, signature(tp2).map(Typ(_, false))), b) + else if (canDecompose(tp1)) tryDecompose1(tp1) + else a + case (_, Or(ss)) => + ss.foldLeft(a)(minus) + case (Or(ss), _) => + Or(ss.map(minus(_, b))) + case (Kon(tp1, ss), Typ(tp2, _)) => + // uncovered corner case: tp2 :< tp1 + if (isSubType(tp1, tp2)) Empty + else if (simplify(a) == Empty) Empty + else if (canDecompose(tp2)) tryDecompose2(tp2) + else a + case (Kon(tp1, ss1), Kon(tp2, ss2)) => + if (!isEqualType(tp1, tp2)) 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) => Kon(tp1, ss1.updated(i, ri)) + }) + case (Const(v1, _), Const(v2, _)) => + if (v1 == v2) Empty else a + case (Const(_, tp1), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) Empty + else if (canDecompose(tp2)) tryDecompose2(tp2) + else a + case (Const(_, _), _) => a + case (Typ(tp1, _), Const(_, tp2)) => // Boolean & Java enum + if (canDecompose(tp1)) tryDecompose1(tp1) + else a + case (_, Const(_, _)) => a + case (Var(x, _), Var(y, _)) => + if (x == y) Empty else a + case (Var(_, tp1), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) Empty + else if (canDecompose(tp2)) tryDecompose2(tp2) + else a + case (Var(_, _), _) => a + case (_, Var(_, _)) => a + } + } +} + +/** Scala implementation of space logic */ +class SpaceEngine(implicit ctx: Context) extends SpaceLogic { + import tpd._ + + /** 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 { + case Literal(c) => Const(c, c.tpe) + case _: BackquotedIdent => 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(erase(tp), false) + } + case Alternative(trees) => Or(trees.map(project(_, roundUp))) + case Bind(_, pat) => project(pat) + case UnApply(_, _, pats) => + if (pat.tpe.classSymbol.is(CaseClass)) + Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat, roundUp))) + else if (roundUp) Typ(pat.tpe.stripAnnots, false) + else Empty + case Typed(pat @ UnApply(_, _, _), _) => project(pat) + case Typed(expr, _) => Typ(erase(expr.tpe.stripAnnots), true) + case _ => + Empty + } + + /* Erase a type binding according to erasure semantics in pattern matching */ + def erase(tp: Type): Type = { + def doErase(tp: Type): Type = tp match { + case tp: RefinedType => erase(tp.parent) + case _ => tp + } + + tp match { + case OrType(tp1, tp2) => + OrType(erase(tp1), erase(tp2)) + case AndType(tp1, tp2) => + AndType(erase(tp1), erase(tp2)) + case _ => + val origin = doErase(tp) + if (origin =:= defn.ArrayType) tp else origin + } + } + + /** Is `tp1` a subtype of `tp2`? */ + def isSubType(tp1: Type, tp2: Type): Boolean = { + // expose is important due to type bounds + // check SI-9657 and tests/patmat/gadt.scala + tp1 <:< expose(tp2) + } + + def isEqualType(tp1: Type, tp2: Type): Boolean = tp1 =:= tp2 + + /** Parameter types of the case class type `tp` */ + def signature(tp: Type): List[Type] = { + val ktor = tp.classSymbol.primaryConstructor.info + + val meth = ktor match { + case ktor: PolyType => + ktor.instantiate(tp.classSymbol.typeParams.map(_.typeRef)).asSeenFrom(tp, tp.classSymbol) + case _ => ktor + } + + // refine path-dependent type in params. refer to t9672 + meth.firstParamTypes.map(_.asSeenFrom(tp, tp.classSymbol)) + } + + /** Decompose a type into subspaces -- assume the type can be decomposed */ + def decompose(tp: Type): List[Space] = { + val children = tp.classSymbol.annotations.filter(_.symbol == ctx.definitions.ChildAnnot).map { annot => + // refer to definition of Annotation.makeChild + annot.tree match { + case Apply(TypeApply(_, List(tpTree)), _) => tpTree.symbol + } + } + + tp match { + case OrType(tp1, tp2) => List(Typ(tp1, true), Typ(tp2, true)) + case _ if tp =:= ctx.definitions.BooleanType => + List( + Const(Constant(true), ctx.definitions.BooleanType), + Const(Constant(false), ctx.definitions.BooleanType) + ) + case _ if tp.classSymbol.is(Enum) => + children.map(sym => Const(Constant(sym), tp)) + case _ => + val parts = children.map { sym => + if (sym.is(ModuleClass)) + sym.asClass.classInfo.selfType + else if (sym.info.typeParams.length > 0 || tp.isInstanceOf[TypeRef]) + refine(tp, sym.typeRef) + else + sym.typeRef + } filter { tpe => + // Child class may not always be subtype of parent: + // GADT & path-dependent types + isSubType(tpe, tp) + } + + parts.map(Typ(_, true)) + } + } + + /** Refine tp2 based on tp1 + * + * E.g. if `tp1` is `Option[Int]`, `tp2` is `Some`, then return + * `Some[Int]`. + * + * If `tp1` is `path1.A`, `tp2` is `path2.B`, and `path1` is subtype of + * `path2`, then return `path1.B`. + */ + def refine(tp1: Type, tp2: Type): Type = (tp1, tp2) match { + case (tp1: RefinedType, _) => tp1.wrapIfMember(refine(tp1.parent, tp2)) + case (TypeRef(ref1: TypeProxy, _), tp2 @ TypeRef(ref2: TypeProxy, name)) => + if (ref1.underlying <:< ref2.underlying) TypeRef(ref1, name) else tp2 + case _ => tp2 + } + + /** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */ + def canDecompose(tp: Type): Boolean = + tp.typeSymbol.is(allOf(Abstract, Sealed)) || + tp.typeSymbol.is(allOf(Trait, Sealed)) || + tp.isInstanceOf[OrType] || + tp =:= ctx.definitions.BooleanType || + tp.typeSymbol.is(Enum) + + def isCaseClass(tp: Type): Boolean = tp.classSymbol.isClass && tp.classSymbol.is(CaseClass) + + /** Show friendly type name with current scope in mind + * + * E.g. C.this.B --> B if current owner is C + * C.this.x.T --> x.T if current owner is C + * X[T] --> X + * C --> C if current owner is C !!! + * + */ + def showType(tp: Type): String = { + val enclosingCls = ctx.owner.enclosingClass.asClass.classInfo.symbolicTypeRef + + def isOmittable(sym: Symbol) = + sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName || + ctx.definitions.UnqualifiedOwnerTypes.exists(_.symbol == sym) || + sym.showFullName.startsWith("scala.") || + sym == enclosingCls.typeSymbol + + def refinePrefix(tp: Type): String = tp match { + case NoPrefix => "" + case tp: NamedType if isOmittable(tp.symbol) => "" + case tp: ThisType => refinePrefix(tp.tref) + case tp: RefinedType => refinePrefix(tp.parent) + case tp: NamedType => tp.name.show.stripSuffix("$") + } + + def refine(tp: Type): String = tp match { + case tp: RefinedType => refine(tp.parent) + case tp: ThisType => refine(tp.tref) + case tp: NamedType => + val pre = refinePrefix(tp.prefix) + if (tp.name == tpnme.hkApply) pre + else if (pre.isEmpty) tp.name.show.stripSuffix("$") + else pre + "." + tp.name.show.stripSuffix("$") + case _ => tp.show.stripSuffix("$") + } + + val text = tp.stripAnnots match { + case tp: OrType => showType(tp.tp1) + " | " + showType(tp.tp2) + case tp => refine(tp) + } + + if (text.isEmpty) enclosingCls.show.stripSuffix("$") + else text + } + + /** Display spaces */ + def show(s: Space): String = { + def doShow(s: Space, mergeList: Boolean = false): String = s match { + case Empty => "" + case Const(v, _) => v.show + case Var(x, _) => x.show + case Typ(tp, decomposed) => + val sym = tp.widen.classSymbol + + if (sym.is(ModuleClass)) + showType(tp) + else if (ctx.definitions.isTupleType(tp)) + signature(tp).map(_ => "_").mkString("(", ", ", ")") + else if (sym.showFullName == "scala.collection.immutable.::") + if (mergeList) "_" else "List(_)" + else if (tp.classSymbol.is(CaseClass)) + // use constructor syntax for case class + showType(tp) + signature(tp).map(_ => "_").mkString("(", ", ", ")") + else if (signature(tp).nonEmpty) + tp.classSymbol.name + signature(tp).map(_ => "_").mkString("(", ", ", ")") + else if (decomposed) "_: " + showType(tp) + else "_" + case Kon(tp, params) => + if (ctx.definitions.isTupleType(tp)) + "(" + params.map(doShow(_)).mkString(", ") + ")" + else if (tp.widen.classSymbol.showFullName == "scala.collection.immutable.::") + if (mergeList) params.map(doShow(_, mergeList)).mkString(", ") + else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")") + else + showType(tp) + params.map(doShow(_)).mkString("(", ", ", ")") + case Or(_) => + throw new Exception("incorrect flatten result " + s) + } + + flatten(s).map(doShow(_, false)).distinct.mkString(", ") + } + + 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 + + // tp.classSymbol.is(Sealed) || + // tp.isInstanceOf[OrType] || + // tp.classSymbol.is(Enum) || + // Boolean + // Int + // ... + } + + val Match(sel, cases) = tree + isCheckable(sel.tpe.widen.deAnonymize) + } + + + /** Expose refined type to eliminate reference to type variables + * + * A = B M { type T = A } ~~> M { type T = B } + * + * A <: X :> Y M { type T = A } ~~> M { type T <: X :> Y } + * + * A <: X :> Y B <: U :> V M { type T <: A :> B } ~~> M { type T <: X :> V } + * + * A = X B = Y M { type T <: A :> B } ~~> M { type T <: X :> Y } + */ + def expose(tp: Type): Type = { + def follow(tp: Type, up: Boolean): Type = tp match { + case tp: TypeProxy => + tp.underlying match { + case TypeBounds(lo, hi) => + follow(if (up) hi else lo, up) + case _ => + tp + } + case OrType(tp1, tp2) => + OrType(follow(tp1, up), follow(tp2, up)) + case AndType(tp1, tp2) => + AndType(follow(tp1, up), follow(tp2, up)) + } + + tp match { + case tp: RefinedType => + tp.refinedInfo match { + case tpa : TypeAlias => + val hi = follow(tpa.alias, true) + val lo = follow(tpa.alias, false) + val refined = if (hi =:= lo) + tpa.derivedTypeAlias(hi) + else + tpa.derivedTypeBounds(lo, hi) + + tp.derivedRefinedType( + expose(tp.parent), + tp.refinedName, + refined + ) + case tpb @ TypeBounds(lo, hi) => + tp.derivedRefinedType( + expose(tp.parent), + tp.refinedName, + tpb.derivedTypeBounds(follow(lo, false), follow(hi, true)) + ) + } + case _ => tp + } + } + + def checkExhaustivity(_match: Match): Unit = { + val Match(sel, cases) = _match + val selTyp = sel.tpe.widen.deAnonymize.dealias + + + 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 + ) + } + } + + def checkRedundancy(_match: Match): Unit = { + val Match(sel, cases) = _match + // ignore selector type for now + // val selTyp = sel.tpe.widen.deAnonymize.dealias + + // 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 + val prevs = cases.take(i).map { x => + if (x.guard.isEmpty) project(x.pat, false) + else Empty + }.reduce((a, b) => Or(List(a, b))) + + val curr = project(cases(i).pat) + + if (isSubspace(curr, prevs)) { + ctx.warning("unreachable code", cases(i).body.pos) + } + } + } +} diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index a8f3b89183cf..2b0e360a0c2e 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -408,6 +408,16 @@ class Namer { typer: Typer => case mdef: DefTree => val sym = enterSymbol(createSymbol(mdef)) setDocstring(sym, stat) + + // add java enum constants + mdef match { + case vdef: ValDef if (isEnumConstant(vdef)) => + val enumClass = sym.owner.linkedClass + if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed) + enumClass.addAnnotation(Annotation.makeChild(sym)) + case _ => + } + ctx case stats: Thicket => for (tree <- stats.toList) { @@ -419,6 +429,24 @@ class Namer { typer: Typer => ctx } + /** Determines whether this field holds an enum constant. + * To qualify, the following conditions must be met: + * - The field's class has the ENUM flag set + * - The field's class extends java.lang.Enum + * - The field has the ENUM flag set + * - The field is static + * - The field is stable + */ + def isEnumConstant(vd: ValDef)(implicit ctx: Context) = { + // val ownerHasEnumFlag = + // Necessary to check because scalac puts Java's static members into the companion object + // while Scala's enum constants live directly in the class. + // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal + // cyclic reference error. See the commit message for details. + // if (ctx.compilationUnit.isJava) ctx.owner.companionClass.is(Enum) else ctx.owner.is(Enum) + vd.mods.is(allOf(Enum, Stable, JavaStatic, JavaDefined)) // && ownerHasEnumFlag + } + def setDocstring(sym: Symbol, tree: Tree)(implicit ctx: Context) = tree match { case t: MemberDef => ctx.base.addDocstring(sym, t.rawComment) case _ => () @@ -996,4 +1024,6 @@ class Namer { typer: Typer => case _ => mapOver(tp) } } + + } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 995fa43ca7b1..b2abe3b36f49 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -470,7 +470,7 @@ trait TypeAssigner { tree.withType(sym.nonMemberTermRef) def assignType(tree: untpd.Annotated, annot: Tree, arg: Tree)(implicit ctx: Context) = - tree.withType(AnnotatedType(arg.tpe, Annotation(annot))) + tree.withType(AnnotatedType(arg.tpe.widen, Annotation(annot))) def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = tree.withType(pid.symbol.valRef) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 268020ec51d3..3834a57c2599 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -719,7 +719,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tree.selector match { case EmptyTree => val (protoFormals, _) = decomposeProtoFunction(pt, 1) - typed(desugar.makeCaseLambda(tree.cases, protoFormals.length) withPos tree.pos, pt) + val unchecked = pt <:< defn.PartialFunctionType + typed(desugar.makeCaseLambda(tree.cases, protoFormals.length, unchecked) withPos tree.pos, pt) case _ => val sel1 = typedExpr(tree.selector) val selType = widenForMatchSelector( diff --git a/test/test/transform/PatmatExhaustivityTest.scala b/test/test/transform/PatmatExhaustivityTest.scala new file mode 100644 index 000000000000..935085480f5e --- /dev/null +++ b/test/test/transform/PatmatExhaustivityTest.scala @@ -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.ConsoleReporter + +class PatmatExhaustivityTest { + val testsDir = "./tests/patmat" + // stop-after: patmatexhaust-huge.scala crash compiler + val options = List("-Ystop-after:splitter") + + private def compileFile(file: File) = { + val stringBuffer = new StringWriter() + val reporter = new ConsoleReporter(writer = 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 ConsoleReporter(writer = 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) + } +} diff --git a/tests/patmat/NonAbstractSealed.check b/tests/patmat/NonAbstractSealed.check new file mode 100644 index 000000000000..9224ee370ce0 --- /dev/null +++ b/tests/patmat/NonAbstractSealed.check @@ -0,0 +1,5 @@ +./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 \ No newline at end of file diff --git a/tests/patmat/NonAbstractSealed.scala b/tests/patmat/NonAbstractSealed.scala new file mode 100644 index 000000000000..ff2e90aeeb05 --- /dev/null +++ b/tests/patmat/NonAbstractSealed.scala @@ -0,0 +1,10 @@ +sealed class A +class B extends A +class C extends A + +object Test { + (null: A) match { + case t: B => + case t: C => + } +} diff --git a/tests/patmat/TwoTrait.scala b/tests/patmat/TwoTrait.scala new file mode 100644 index 000000000000..b8e3402c5e8c --- /dev/null +++ b/tests/patmat/TwoTrait.scala @@ -0,0 +1,12 @@ +object Test { + sealed trait A + sealed trait B + + abstract sealed class Parent + class Foo extends Parent with A with B + class Bar extends Parent with B with A + + (null: A) match { + case _: B => + } +} diff --git a/tests/patmat/enum/Day.java b/tests/patmat/enum/Day.java new file mode 100644 index 000000000000..eedb9a72b541 --- /dev/null +++ b/tests/patmat/enum/Day.java @@ -0,0 +1,4 @@ +public enum Day { + SUNDAY, MONDAY, TUESDAY, WEDNESDAY, + THURSDAY, FRIDAY, SATURDAY +} \ No newline at end of file diff --git a/tests/patmat/enum/expected.check b/tests/patmat/enum/expected.check new file mode 100644 index 000000000000..b3dafa8bdd02 --- /dev/null +++ b/tests/patmat/enum/expected.check @@ -0,0 +1,9 @@ +./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 \ No newline at end of file diff --git a/tests/patmat/enum/patmat-enum.scala b/tests/patmat/enum/patmat-enum.scala new file mode 100644 index 000000000000..ec5c90255f7a --- /dev/null +++ b/tests/patmat/enum/patmat-enum.scala @@ -0,0 +1,21 @@ +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 + } +} \ No newline at end of file diff --git a/tests/patmat/for.scala b/tests/patmat/for.scala new file mode 100644 index 000000000000..ae9dcf65e9f1 --- /dev/null +++ b/tests/patmat/for.scala @@ -0,0 +1,5 @@ +object Test { + def foo[A, B](l: List[(A, B)]): List[A] = { + for ((a, b) <- l) yield a + } +} \ No newline at end of file diff --git a/tests/patmat/gadt.check b/tests/patmat/gadt.check new file mode 100644 index 000000000000..79b3494e08cf --- /dev/null +++ b/tests/patmat/gadt.check @@ -0,0 +1,17 @@ +./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 \ No newline at end of file diff --git a/tests/patmat/gadt.scala b/tests/patmat/gadt.scala new file mode 100644 index 000000000000..9f3e7696f75d --- /dev/null +++ b/tests/patmat/gadt.scala @@ -0,0 +1,58 @@ +object Test { + sealed trait Expr[T] + case class IntLit(i: Int) extends Expr[Int] + case class BooleanLit(b: Boolean) extends Expr[Boolean] + case class Sum(l: Expr[Int], r: Expr[Int]) extends Expr[Int] + case class Or(l: Expr[Boolean], r: Expr[Boolean]) extends Expr[Boolean] + + def foo1a(x: Expr[Int]) = x match { + case _: IntLit => true + case _: Sum => true + } + + def foo1b(x: Expr[Int]) = x match { + case _: Sum => true + } + + def foo2a(x: Expr[Boolean]) = x match { + case _: BooleanLit => true + case _: Or => true + } + + def foo2b(x: Expr[Boolean]) = x match { + case _: BooleanLit => true + } + + def foo3a(x: Expr[Boolean]) = x match { + case _: BooleanLit => true + case _: Or => true + // case _: Sum => true + } + + def foo3b(x: Expr[Int]) = x match { + case _: IntLit => true + case _: Sum => true + // case _: Or => true + } + + def foo4a(x: Expr) = x match { + case _: IntLit => true + case _: Sum => true + case _: BooleanLit => true + case _: Or => true + } + + def foo4b(x: Expr) = x match { + case _: Sum => true + case _: Or => true + } + + def foo5a[T <: Int](x: Expr[T]) = x match { + case _: IntLit => true + case _: Sum => true + } + + def foo5b[T <: Int](x: Expr[T]) = x match { + case _: IntLit => true + } +} diff --git a/tests/patmat/i947.check b/tests/patmat/i947.check new file mode 100644 index 000000000000..5cce559c4834 --- /dev/null +++ b/tests/patmat/i947.check @@ -0,0 +1,4 @@ +./tests/patmat/i947.scala:10: warning: unreachable code + case ys: List[d18383] => false + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/i947.scala b/tests/patmat/i947.scala new file mode 100644 index 000000000000..0f2d9e77583a --- /dev/null +++ b/tests/patmat/i947.scala @@ -0,0 +1,16 @@ +object Test { + + class c { + + private var x: Int = 0 + + override def equals(other: Any) = other match { + case o: c => x == o.x + case xs: List[c] => false + case ys: List[d18383] => false + case _ => false + } + + + } +} diff --git a/tests/patmat/partial-function.scala b/tests/patmat/partial-function.scala new file mode 100644 index 000000000000..f168489dad74 --- /dev/null +++ b/tests/patmat/partial-function.scala @@ -0,0 +1,12 @@ +sealed abstract class TA +sealed abstract class TB extends TA +case object B extends TB +case object B2 extends TB + +case class CC(i: Int, tb: TB) + +object Test { + def foo: PartialFunction[CC, Unit] = { + case CC(_, B) => () + } +} \ No newline at end of file diff --git a/tests/patmat/patmat-adt.check b/tests/patmat/patmat-adt.check new file mode 100644 index 000000000000..f4e1ce369e50 --- /dev/null +++ b/tests/patmat/patmat-adt.check @@ -0,0 +1,21 @@ +./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 \ No newline at end of file diff --git a/tests/patmat/patmat-adt.scala b/tests/patmat/patmat-adt.scala new file mode 100644 index 000000000000..e7eac4e4a662 --- /dev/null +++ b/tests/patmat/patmat-adt.scala @@ -0,0 +1,58 @@ +object PatmatADT { + abstract sealed class Odd(x: Odd) + + case class Good(x: Odd) extends Odd(x) + case class Bad(x: Odd) extends Odd(x) + + def foo1a(x: Odd) = x match { // warning: Good(_: Bad), Bad(_: Good) + case Good(_: Good) => false + case Bad(_: Bad) => false + } + + def foo1b(x: Odd) = x match { + case Good(_: Good) => false + case Bad(_: Bad) => false + case Good(_: Bad) => false + case Bad(_: Good) => false + } + + def foo2(x: Option[Int]) = x match { // warning: Some(_: Int) + case Some(_: Double) => true + case None => true + } + + def foo3a[T](x: Option[T]) = (x, x) match { // warning: (Some(_), Some(_)), (None, Some(_)) + case (Some(_), None) => true + case (None, None) => true + } + + def foo3b[T](x: Option[T]) = (x, x) match { // warning: (Some(_), Some(_)), (None, None) + case (Some(_), None) => true + case (None, Some(_)) => true + } + + sealed trait Base + case class Foo() extends Base + + def foo4(x: Base) = x match { + case Foo() => + } + + sealed abstract class CL3Literal + case object IntLit extends CL3Literal + case object CharLit extends CL3Literal + case object BooleanLit extends CL3Literal + + + sealed abstract class Tree + case class LetL(value: CL3Literal) extends Tree + + def foo5(tree: Tree) : Any = tree match { + case LetL(CharLit) => + } + + def foo6[T](l: List[T]): Boolean = l match { + case x::xs => true + case Nil => false + } +} \ No newline at end of file diff --git a/tests/patmat/patmat-extractor.scala b/tests/patmat/patmat-extractor.scala new file mode 100644 index 000000000000..02fde96dcb42 --- /dev/null +++ b/tests/patmat/patmat-extractor.scala @@ -0,0 +1,17 @@ +sealed trait Node +case class NodeA(i: Int) extends Node +case class NodeB(b: Boolean) extends Node +case class NodeC(s: String) extends Node + +object Node { + def unapply(node: Node): Option[(Node, Node)] = ??? +} + +// currently scalac can't do anything with following +// it's possible to do better in our case +object Test { + def foo(x: Node): Boolean = x match { // unexhaustive + case Node(NodeA(_), NodeB(_)) => true + case Node(NodeA(4), NodeB(false)) => true // unreachable code + } +} \ No newline at end of file diff --git a/tests/patmat/patmat-indent.check b/tests/patmat/patmat-indent.check new file mode 100644 index 000000000000..3a76e0a9581b --- /dev/null +++ b/tests/patmat/patmat-indent.check @@ -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 \ No newline at end of file diff --git a/tests/patmat/patmat-indent.scala b/tests/patmat/patmat-indent.scala new file mode 100644 index 000000000000..ef25bb2c7ccf --- /dev/null +++ b/tests/patmat/patmat-indent.scala @@ -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 + } +} \ No newline at end of file diff --git a/tests/patmat/patmat-ortype.check b/tests/patmat/patmat-ortype.check new file mode 100644 index 000000000000..2291da251994 --- /dev/null +++ b/tests/patmat/patmat-ortype.check @@ -0,0 +1,13 @@ +./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 \ No newline at end of file diff --git a/tests/patmat/patmat-ortype.scala b/tests/patmat/patmat-ortype.scala new file mode 100644 index 000000000000..c7419acd3b0f --- /dev/null +++ b/tests/patmat/patmat-ortype.scala @@ -0,0 +1,40 @@ +object PatmatOrType { + + def foo1(x: Int | Double) = x match { + case _: Int => true + case _: Double => true + } + + def foo2a(x: Int | Double | String) = x match { // _: String not matched + case _: Int => true + case _: Double => true + } + + def foo2b(x: Int | Double | String) = x match { + case _: Int => true + case _: (Double | String) => true + } + + def foo3(x: Option[Int | Double | String]) = x match { // warning: None, Some(_: String) not matched + case Some(_: Int) => true + case Some(_: Double) => true + } + + def foo4(x: Option[Int | Double | String]) = x match { + case Some(_: Int) => true + case Some(_: Double) => true + case Some(_: String) => true + case None => false + } + + def foo5a(x: Option[Int | Double | String]) = x match { + case Some(_: (Int | Double)) => true + case Some(_: String) => true + case None => false + } + + def foo5b(x: Option[Int | Double | String]) = x match { // warning: Some(_: String) not matched + case Some(_: (Int | Double)) => true + case None => false + } +} \ No newline at end of file diff --git a/tests/patmat/patmatexhaust-huge.check b/tests/patmat/patmatexhaust-huge.check new file mode 100644 index 000000000000..06cac90bd5e7 --- /dev/null +++ b/tests/patmat/patmatexhaust-huge.check @@ -0,0 +1,5 @@ +./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 \ No newline at end of file diff --git a/tests/patmat/patmatexhaust-huge.scala b/tests/patmat/patmatexhaust-huge.scala new file mode 100644 index 000000000000..c4008b99542a --- /dev/null +++ b/tests/patmat/patmatexhaust-huge.scala @@ -0,0 +1,806 @@ +abstract sealed trait C +case object C1 extends C +case object C2 extends C +case object C3 extends C +case object C4 extends C +case object C5 extends C +case object C6 extends C +case object C7 extends C +case object C8 extends C +case object C9 extends C +case object C10 extends C +case object C11 extends C +case object C12 extends C +case object C13 extends C +case object C14 extends C +case object C15 extends C +case object C16 extends C +case object C17 extends C +case object C18 extends C +case object C19 extends C +case object C20 extends C +case object C21 extends C +case object C22 extends C +case object C23 extends C +case object C24 extends C +case object C25 extends C +case object C26 extends C +case object C27 extends C +case object C28 extends C +case object C29 extends C +case object C30 extends C +case object C31 extends C +case object C32 extends C +case object C33 extends C +case object C34 extends C +case object C35 extends C +case object C36 extends C +case object C37 extends C +case object C38 extends C +case object C39 extends C +case object C40 extends C +case object C41 extends C +case object C42 extends C +case object C43 extends C +case object C44 extends C +case object C45 extends C +case object C46 extends C +case object C47 extends C +case object C48 extends C +case object C49 extends C +case object C50 extends C +case object C51 extends C +case object C52 extends C +case object C53 extends C +case object C54 extends C +case object C55 extends C +case object C56 extends C +case object C57 extends C +case object C58 extends C +case object C59 extends C +case object C60 extends C +case object C61 extends C +case object C62 extends C +case object C63 extends C +case object C64 extends C +case object C65 extends C +case object C66 extends C +case object C67 extends C +case object C68 extends C +case object C69 extends C +case object C70 extends C +case object C71 extends C +case object C72 extends C +case object C73 extends C +case object C74 extends C +case object C75 extends C +case object C76 extends C +case object C77 extends C +case object C78 extends C +case object C79 extends C +case object C80 extends C +case object C81 extends C +case object C82 extends C +case object C83 extends C +case object C84 extends C +case object C85 extends C +case object C86 extends C +case object C87 extends C +case object C88 extends C +case object C89 extends C +case object C90 extends C +case object C91 extends C +case object C92 extends C +case object C93 extends C +case object C94 extends C +case object C95 extends C +case object C96 extends C +case object C97 extends C +case object C98 extends C +case object C99 extends C +case object C100 extends C +case object C101 extends C +case object C102 extends C +case object C103 extends C +case object C104 extends C +case object C105 extends C +case object C106 extends C +case object C107 extends C +case object C108 extends C +case object C109 extends C +case object C110 extends C +case object C111 extends C +case object C112 extends C +case object C113 extends C +case object C114 extends C +case object C115 extends C +case object C116 extends C +case object C117 extends C +case object C118 extends C +case object C119 extends C +case object C120 extends C +case object C121 extends C +case object C122 extends C +case object C123 extends C +case object C124 extends C +case object C125 extends C +case object C126 extends C +case object C127 extends C +case object C128 extends C +case object C129 extends C +case object C130 extends C +case object C131 extends C +case object C132 extends C +case object C133 extends C +case object C134 extends C +case object C135 extends C +case object C136 extends C +case object C137 extends C +case object C138 extends C +case object C139 extends C +case object C140 extends C +case object C141 extends C +case object C142 extends C +case object C143 extends C +case object C144 extends C +case object C145 extends C +case object C146 extends C +case object C147 extends C +case object C148 extends C +case object C149 extends C +case object C150 extends C +case object C151 extends C +case object C152 extends C +case object C153 extends C +case object C154 extends C +case object C155 extends C +case object C156 extends C +case object C157 extends C +case object C158 extends C +case object C159 extends C +case object C160 extends C +case object C161 extends C +case object C162 extends C +case object C163 extends C +case object C164 extends C +case object C165 extends C +case object C166 extends C +case object C167 extends C +case object C168 extends C +case object C169 extends C +case object C170 extends C +case object C171 extends C +case object C172 extends C +case object C173 extends C +case object C174 extends C +case object C175 extends C +case object C176 extends C +case object C177 extends C +case object C178 extends C +case object C179 extends C +case object C180 extends C +case object C181 extends C +case object C182 extends C +case object C183 extends C +case object C184 extends C +case object C185 extends C +case object C186 extends C +case object C187 extends C +case object C188 extends C +case object C189 extends C +case object C190 extends C +case object C191 extends C +case object C192 extends C +case object C193 extends C +case object C194 extends C +case object C195 extends C +case object C196 extends C +case object C197 extends C +case object C198 extends C +case object C199 extends C +case object C200 extends C +case object C201 extends C +case object C202 extends C +case object C203 extends C +case object C204 extends C +case object C205 extends C +case object C206 extends C +case object C207 extends C +case object C208 extends C +case object C209 extends C +case object C210 extends C +case object C211 extends C +case object C212 extends C +case object C213 extends C +case object C214 extends C +case object C215 extends C +case object C216 extends C +case object C217 extends C +case object C218 extends C +case object C219 extends C +case object C220 extends C +case object C221 extends C +case object C222 extends C +case object C223 extends C +case object C224 extends C +case object C225 extends C +case object C226 extends C +case object C227 extends C +case object C228 extends C +case object C229 extends C +case object C230 extends C +case object C231 extends C +case object C232 extends C +case object C233 extends C +case object C234 extends C +case object C235 extends C +case object C236 extends C +case object C237 extends C +case object C238 extends C +case object C239 extends C +case object C240 extends C +case object C241 extends C +case object C242 extends C +case object C243 extends C +case object C244 extends C +case object C245 extends C +case object C246 extends C +case object C247 extends C +case object C248 extends C +case object C249 extends C +case object C250 extends C +case object C251 extends C +case object C252 extends C +case object C253 extends C +case object C254 extends C +case object C255 extends C +case object C256 extends C +case object C257 extends C +case object C258 extends C +case object C259 extends C +case object C260 extends C +case object C261 extends C +case object C262 extends C +case object C263 extends C +case object C264 extends C +case object C265 extends C +case object C266 extends C +case object C267 extends C +case object C268 extends C +case object C269 extends C +case object C270 extends C +case object C271 extends C +case object C272 extends C +case object C273 extends C +case object C274 extends C +case object C275 extends C +case object C276 extends C +case object C277 extends C +case object C278 extends C +case object C279 extends C +case object C280 extends C +case object C281 extends C +case object C282 extends C +case object C283 extends C +case object C284 extends C +case object C285 extends C +case object C286 extends C +case object C287 extends C +case object C288 extends C +case object C289 extends C +case object C290 extends C +case object C291 extends C +case object C292 extends C +case object C293 extends C +case object C294 extends C +case object C295 extends C +case object C296 extends C +case object C297 extends C +case object C298 extends C +case object C299 extends C +case object C300 extends C +case object C301 extends C +case object C302 extends C +case object C303 extends C +case object C304 extends C +case object C305 extends C +case object C306 extends C +case object C307 extends C +case object C308 extends C +case object C309 extends C +case object C310 extends C +case object C311 extends C +case object C312 extends C +case object C313 extends C +case object C314 extends C +case object C315 extends C +case object C316 extends C +case object C317 extends C +case object C318 extends C +case object C319 extends C +case object C320 extends C +case object C321 extends C +case object C322 extends C +case object C323 extends C +case object C324 extends C +case object C325 extends C +case object C326 extends C +case object C327 extends C +case object C328 extends C +case object C329 extends C +case object C330 extends C +case object C331 extends C +case object C332 extends C +case object C333 extends C +case object C334 extends C +case object C335 extends C +case object C336 extends C +case object C337 extends C +case object C338 extends C +case object C339 extends C +case object C340 extends C +case object C341 extends C +case object C342 extends C +case object C343 extends C +case object C344 extends C +case object C345 extends C +case object C346 extends C +case object C347 extends C +case object C348 extends C +case object C349 extends C +case object C350 extends C +case object C351 extends C +case object C352 extends C +case object C353 extends C +case object C354 extends C +case object C355 extends C +case object C356 extends C +case object C357 extends C +case object C358 extends C +case object C359 extends C +case object C360 extends C +case object C361 extends C +case object C362 extends C +case object C363 extends C +case object C364 extends C +case object C365 extends C +case object C366 extends C +case object C367 extends C +case object C368 extends C +case object C369 extends C +case object C370 extends C +case object C371 extends C +case object C372 extends C +case object C373 extends C +case object C374 extends C +case object C375 extends C +case object C376 extends C +case object C377 extends C +case object C378 extends C +case object C379 extends C +case object C380 extends C +case object C381 extends C +case object C382 extends C +case object C383 extends C +case object C384 extends C +case object C385 extends C +case object C386 extends C +case object C387 extends C +case object C388 extends C +case object C389 extends C +case object C390 extends C +case object C391 extends C +case object C392 extends C +case object C393 extends C +case object C394 extends C +case object C395 extends C +case object C396 extends C +case object C397 extends C +case object C398 extends C +case object C399 extends C +case object C400 extends C + +object M { + def f(c: C): Int = c match { + case C1 => 1 + case C2 => 2 + case C3 => 3 + case C4 => 4 + case C5 => 5 + case C6 => 6 + case C7 => 7 + case C8 => 8 + case C9 => 9 + case C10 => 10 + case C11 => 11 + case C12 => 12 + case C13 => 13 + case C14 => 14 + case C15 => 15 + case C16 => 16 + case C17 => 17 + case C18 => 18 + case C19 => 19 + case C20 => 20 + case C21 => 21 + case C22 => 22 + case C23 => 23 + case C24 => 24 + case C25 => 25 + case C26 => 26 + case C27 => 27 + case C28 => 28 + case C29 => 29 + case C30 => 30 + case C31 => 31 + case C32 => 32 + case C33 => 33 + case C34 => 34 + case C35 => 35 + case C36 => 36 + case C37 => 37 + case C38 => 38 + case C39 => 39 + case C40 => 40 + case C41 => 41 + case C42 => 42 + case C43 => 43 + case C44 => 44 + case C45 => 45 + case C46 => 46 + case C47 => 47 + case C48 => 48 + case C49 => 49 + case C50 => 50 + case C51 => 51 + case C52 => 52 + case C53 => 53 + case C54 => 54 + case C55 => 55 + case C56 => 56 + case C57 => 57 + case C58 => 58 + case C59 => 59 + case C60 => 60 + case C61 => 61 + case C62 => 62 + case C63 => 63 + case C64 => 64 + case C65 => 65 + case C66 => 66 + case C67 => 67 + case C68 => 68 + case C69 => 69 + case C70 => 70 + case C71 => 71 + case C72 => 72 + case C73 => 73 + case C74 => 74 + case C75 => 75 + case C76 => 76 + case C77 => 77 + case C78 => 78 + case C79 => 79 + case C80 => 80 + case C81 => 81 + case C82 => 82 + case C83 => 83 + case C84 => 84 + case C85 => 85 + case C86 => 86 + case C87 => 87 + case C88 => 88 + case C89 => 89 + case C90 => 90 + case C91 => 91 + case C92 => 92 + case C93 => 93 + case C94 => 94 + case C95 => 95 + case C96 => 96 + case C97 => 97 + case C98 => 98 + case C99 => 99 + case C100 => 100 + case C101 => 101 + case C102 => 102 + case C103 => 103 + case C104 => 104 + case C105 => 105 + case C106 => 106 + case C107 => 107 + case C108 => 108 + case C109 => 109 + case C110 => 110 + case C111 => 111 + case C112 => 112 + case C113 => 113 + case C114 => 114 + case C115 => 115 + case C116 => 116 + case C117 => 117 + case C118 => 118 + case C119 => 119 + case C120 => 120 + case C121 => 121 + case C122 => 122 + case C123 => 123 + case C124 => 124 + case C125 => 125 + case C126 => 126 + case C127 => 127 + case C128 => 128 + case C129 => 129 + case C130 => 130 + case C131 => 131 + case C132 => 132 + case C133 => 133 + case C134 => 134 + case C135 => 135 + case C136 => 136 + case C137 => 137 + case C138 => 138 + case C139 => 139 + case C140 => 140 + case C141 => 141 + case C142 => 142 + case C143 => 143 + case C144 => 144 + case C145 => 145 + case C146 => 146 + case C147 => 147 + case C148 => 148 + case C149 => 149 + case C150 => 150 + case C151 => 151 + case C152 => 152 + case C153 => 153 + case C154 => 154 + case C155 => 155 + case C156 => 156 + case C157 => 157 + case C158 => 158 + case C159 => 159 + case C160 => 160 + case C161 => 161 + case C162 => 162 + case C163 => 163 + case C164 => 164 + case C165 => 165 + case C166 => 166 + case C167 => 167 + case C168 => 168 + case C169 => 169 + case C170 => 170 + case C171 => 171 + case C172 => 172 + case C173 => 173 + case C174 => 174 + case C175 => 175 + case C176 => 176 + case C177 => 177 + case C178 => 178 + case C179 => 179 + case C180 => 180 + case C181 => 181 + case C182 => 182 + case C183 => 183 + case C184 => 184 + case C185 => 185 + case C186 => 186 + case C187 => 187 + case C188 => 188 + case C189 => 189 + case C190 => 190 + case C191 => 191 + case C192 => 192 + case C193 => 193 + case C194 => 194 + case C195 => 195 + case C196 => 196 + case C197 => 197 + case C198 => 198 + case C199 => 199 + case C200 => 200 + case C201 => 201 + case C202 => 202 + case C203 => 203 + case C204 => 204 + case C205 => 205 + case C206 => 206 + case C207 => 207 + case C208 => 208 + case C209 => 209 + case C210 => 210 + case C211 => 211 + case C212 => 212 + case C213 => 213 + case C214 => 214 + case C215 => 215 + case C216 => 216 + case C217 => 217 + case C218 => 218 + case C219 => 219 + case C220 => 220 + case C221 => 221 + case C222 => 222 + case C223 => 223 + case C224 => 224 + case C225 => 225 + case C226 => 226 + case C227 => 227 + case C228 => 228 + case C229 => 229 + case C230 => 230 + case C231 => 231 + case C232 => 232 + case C233 => 233 + case C234 => 234 + case C235 => 235 + case C236 => 236 + case C237 => 237 + case C238 => 238 + case C239 => 239 + case C240 => 240 + case C241 => 241 + case C242 => 242 + case C243 => 243 + case C244 => 244 + case C245 => 245 + case C246 => 246 + case C247 => 247 + case C248 => 248 + case C249 => 249 + case C250 => 250 + case C251 => 251 + case C252 => 252 + case C253 => 253 + case C254 => 254 + case C255 => 255 + case C256 => 256 + case C257 => 257 + case C258 => 258 + case C259 => 259 + case C260 => 260 + case C261 => 261 + case C262 => 262 + case C263 => 263 + case C264 => 264 + case C265 => 265 + case C266 => 266 + case C267 => 267 + case C268 => 268 + case C269 => 269 + case C270 => 270 + case C271 => 271 + case C272 => 272 + case C273 => 273 + case C274 => 274 + case C275 => 275 + case C276 => 276 + case C277 => 277 + case C278 => 278 + case C279 => 279 + case C280 => 280 + case C281 => 281 + case C282 => 282 + case C283 => 283 + case C284 => 284 + case C285 => 285 + case C286 => 286 + case C287 => 287 + case C288 => 288 + case C289 => 289 + case C290 => 290 + case C291 => 291 + case C292 => 292 + case C293 => 293 + case C294 => 294 + case C295 => 295 + case C296 => 296 + case C297 => 297 + case C298 => 298 + case C299 => 299 + case C300 => 300 + case C301 => 301 + case C302 => 302 + case C303 => 303 + case C304 => 304 + case C305 => 305 + case C306 => 306 + case C307 => 307 + case C308 => 308 + case C309 => 309 + case C310 => 310 + case C311 => 311 + case C312 => 312 + case C313 => 313 + case C314 => 314 + case C315 => 315 + case C316 => 316 + case C317 => 317 + case C318 => 318 + case C319 => 319 + case C320 => 320 + case C321 => 321 + case C322 => 322 + case C323 => 323 + case C324 => 324 + case C325 => 325 + case C326 => 326 + case C327 => 327 + case C328 => 328 + case C329 => 329 + case C330 => 330 + case C331 => 331 + case C332 => 332 + case C333 => 333 + case C334 => 334 + case C335 => 335 + case C336 => 336 + case C337 => 337 + case C338 => 338 + case C339 => 339 + case C340 => 340 + case C341 => 341 + case C342 => 342 + case C343 => 343 + case C344 => 344 + case C345 => 345 + case C346 => 346 + case C347 => 347 + case C348 => 348 + case C349 => 349 + case C350 => 350 + case C351 => 351 + case C352 => 352 + case C353 => 353 + case C354 => 354 + case C355 => 355 + case C356 => 356 + case C357 => 357 + case C358 => 358 + case C359 => 359 + case C360 => 360 + case C361 => 361 + case C362 => 362 + case C363 => 363 + case C364 => 364 + case C365 => 365 + case C366 => 366 + case C367 => 367 + case C368 => 368 + case C369 => 369 + case C370 => 370 + case C371 => 371 + case C372 => 372 + case C373 => 373 + case C374 => 374 + case C375 => 375 + case C376 => 376 + case C377 => 377 + case C378 => 378 + case C379 => 379 + case C380 => 380 + case C381 => 381 + case C382 => 382 + case C383 => 383 + case C384 => 384 + case C385 => 385 + case C386 => 386 + case C387 => 387 + case C388 => 388 + case C389 => 389 + case C390 => 390 + case C391 => 391 +// case C392 => 392 + case C393 => 393 + case C394 => 394 + case C395 => 395 + case C396 => 396 +// case C397 => 397 + case C398 => 398 + case C399 => 399 + case C400 => 400 + } +} diff --git a/tests/patmat/patmatexhaust.check b/tests/patmat/patmatexhaust.check new file mode 100644 index 000000000000..ef2b578d6e3b --- /dev/null +++ b/tests/patmat/patmatexhaust.check @@ -0,0 +1,33 @@ +./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 \ No newline at end of file diff --git a/tests/patmat/patmatexhaust.scala b/tests/patmat/patmatexhaust.scala new file mode 100644 index 000000000000..26f0c12a919c --- /dev/null +++ b/tests/patmat/patmatexhaust.scala @@ -0,0 +1,131 @@ +class TestSealedExhaustive { // compile only + sealed abstract class Foo + + case class Bar(x:Int) extends Foo + case object Baz extends Foo + + def ma1(x:Foo) = x match { + case Bar(_) => // not exhaustive + } + + def ma2(x:Foo) = x match { + case Baz => // not exhaustive + } + + sealed abstract class Mult + case class Kult(s:Mult) extends Mult + case class Qult() extends Mult + + def ma33(x:Kult) = x match { // exhaustive + case Kult(_) => // exhaustive + } + + def ma3(x:Mult) = (x,x) match { // not exhaustive + case (Kult(_), Qult()) => // Kult missing + //case (Kult(_), Kult(_)) => + case (Qult(), Kult(_)) => // Qult missing + //case (Qult(), Qult()) => + } + + def ma3u(x:Mult) = ((x,x) : @unchecked) match { // not exhaustive, but not checked! + case (Kult(_), Qult()) => + case (Qult(), Kult(_)) => + } + + sealed abstract class Deep + + case object Ga extends Deep + sealed class Gp extends Deep + case object Gu extends Gp + + def zma3(x:Deep) = x match { // exhaustive! + case _ => + } + def zma4(x:Deep) = x match { // exhaustive! + case Ga => + case _ => + } + + def ma4(x:Deep) = x match { // missing cases: Gu, Gp which is not abstract so must be included + case Ga => + } + + def ma5(x:Deep) = x match { + case Gu => + case _ if 1 == 0 => + case Ga => + } + + def ma6() = List(1,2) match { // give up + case List(1,2) => + case x :: xs => + } + + def ma7() = List(1,2) match { //exhaustive + case 1::2::Nil => + case _ => + } + + sealed class B + case class B1() extends B + case object B2 extends B + def ma8(x: B) = x match { + case _: B => true + } + def ma9(x: B) = x match { + case B1() => true // missing B, which is not abstract so must be included + case B2 => true + } + + object ob1 { + sealed abstract class C + sealed abstract class C1 extends C + object C2 extends C + case class C3() extends C + case object C4 extends C + + def ma10(x: C) = x match { // exhaustive: abstract sealed C1 is dead end. + case C3() => true + case C2 | C4 => true + } + } + + object ob2 { + sealed abstract class C + abstract class C1 extends C + object C2 extends C + case class C3() extends C + case object C4 extends C + + def ma10(x: C) = x match { // not exhaustive: C1 is not sealed. + case C3() => true + case C2 | C4 => true + } + } + object ob3 { + sealed abstract class C + sealed abstract class C1 extends C + object D1 extends C1 + case class D2() extends C1 + object C2 extends C + case class C3() extends C + case object C4 extends C + + def ma10(x: C) = x match { // not exhaustive: C1 has subclasses. + case C3() => true + case C2 | C4 => true + } + } + object ob4 { + sealed abstract class C + sealed class C1 extends C + object C2 extends C + case class C3() extends C + case object C4 extends C + + def ma10(x: C) = x match { // not exhaustive: C1 is not abstract. + case C3() => true + case C2 | C4 => true + } + } +} diff --git a/tests/patmat/t1056.scala b/tests/patmat/t1056.scala new file mode 100644 index 000000000000..68f1ff27310c --- /dev/null +++ b/tests/patmat/t1056.scala @@ -0,0 +1,5 @@ +object Test { + type T = PartialFunction[String,String] + def g(h: T) = () + g({case s: String => s}) +} diff --git a/tests/patmat/t2425.scala b/tests/patmat/t2425.scala new file mode 100644 index 000000000000..477d5467aab3 --- /dev/null +++ b/tests/patmat/t2425.scala @@ -0,0 +1,15 @@ +trait B +class D extends B +object Test extends App { + def foo[T](bar: T) = { + bar match { + case _: Array[Array[_]] => println("array 2d") + case _: Array[_] => println("array 1d") + case _ => println("something else") + } + } + foo(Array.fill(10)(2)) + foo(Array.fill(10, 10)(2)) + foo(Array.fill(10, 10, 10)(2)) + foo(List(1, 2, 3)) +} diff --git a/tests/patmat/t3097.scala b/tests/patmat/t3097.scala new file mode 100644 index 000000000000..3ff61b3c7b87 --- /dev/null +++ b/tests/patmat/t3097.scala @@ -0,0 +1,35 @@ +sealed trait ISimpleValue + +sealed trait IListValue extends ISimpleValue { + def items: List[IAtomicValue[_]] +} + +sealed trait IAtomicValue[O] extends ISimpleValue { + def data: O +} + +sealed trait IAbstractDoubleValue[O] extends IAtomicValue[O] { +} + +sealed trait IDoubleValue extends IAbstractDoubleValue[Double] + +case class ListValue(val items: List[IAtomicValue[_]]) extends IListValue + +class DoubleValue(val data: Double) extends IDoubleValue { + def asDouble = data +} + +object Test { + + /** + * @param args the command line arguments + */ + def main(args: Array[String]): Unit = { + val v: ISimpleValue = new DoubleValue(1) + v match { + case m: IListValue => println("list") + case a: IAtomicValue[_] => println("atomic") + } + + } +} \ No newline at end of file diff --git a/tests/patmat/t3111.check b/tests/patmat/t3111.check new file mode 100644 index 000000000000..46ff0a6a9376 --- /dev/null +++ b/tests/patmat/t3111.check @@ -0,0 +1,8 @@ +./tests/patmat/t3111.scala:4: warning: match may not be exhaustive. +It would fail on the following input: false + bool match { + ^ +./tests/patmat/t3111.scala:11: warning: unreachable code + case _ => "cats and dogs living together... mass hysteria!" + ^ +two warnings found \ No newline at end of file diff --git a/tests/patmat/t3111.scala b/tests/patmat/t3111.scala new file mode 100644 index 000000000000..8f2bc5a273a9 --- /dev/null +++ b/tests/patmat/t3111.scala @@ -0,0 +1,13 @@ +object Test { + val bool: Boolean = false + + bool match { + case true => "true!" + } + + bool match { + case true => "true!" + case false => "false!" + case _ => "cats and dogs living together... mass hysteria!" + } +} \ No newline at end of file diff --git a/tests/patmat/t3163.check b/tests/patmat/t3163.check new file mode 100644 index 000000000000..3da94e2c2a40 --- /dev/null +++ b/tests/patmat/t3163.check @@ -0,0 +1,5 @@ +./tests/patmat/t3163.scala:2: warning: match may not be exhaustive. +It would fail on the following input: _: AnyVal + def foo(x : AnyVal) = x match {case b : Boolean => "It's a bool"} + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t3163.scala b/tests/patmat/t3163.scala new file mode 100644 index 000000000000..2e0f2c1d9482 --- /dev/null +++ b/tests/patmat/t3163.scala @@ -0,0 +1,3 @@ +object Test { + def foo(x : AnyVal) = x match {case b : Boolean => "It's a bool"} +} \ No newline at end of file diff --git a/tests/patmat/t3683.scala b/tests/patmat/t3683.scala new file mode 100644 index 000000000000..44be9d6c63fe --- /dev/null +++ b/tests/patmat/t3683.scala @@ -0,0 +1,19 @@ +sealed trait Foo +sealed trait Bar extends Foo +sealed trait W[T >: Bar <: Foo] +sealed case class X() extends W[Foo] +sealed case class Y() extends W[Bar] +sealed case class Z[T >: Bar <: Foo]( + z1: W[T] +) extends W[T] + +object Main { + def func(w: W[Bar]): Int = { + w match { + // Error if I include it, warning if I do not! + // case X() => 2 + case Y() => 1 + case Z(z) => func(z) + } + } +} diff --git a/tests/patmat/t4020.scala b/tests/patmat/t4020.scala new file mode 100644 index 000000000000..f9764601910a --- /dev/null +++ b/tests/patmat/t4020.scala @@ -0,0 +1,25 @@ +class A { + sealed trait Foo +} + +object a1 extends A { + case class Foo1(i: Int) extends Foo +} + +object a2 extends A { + case class Foo2(i: Int) extends Foo +} + +class B { + def mthd(foo: a2.Foo) = { + foo match { + case a2.Foo2(i) => i + + // Note: This case is impossible. In fact, scalac + // will (correctly) report an error if it is uncommented, + // but a warning if it is commented. + + // case a1.Foo1(i) => i + } + } +} \ No newline at end of file diff --git a/tests/patmat/t4333.scala.ignore b/tests/patmat/t4333.scala.ignore new file mode 100644 index 000000000000..07d105c74e47 --- /dev/null +++ b/tests/patmat/t4333.scala.ignore @@ -0,0 +1,7 @@ +object Enum extends Enumeration { val A, B, C = Value } + +object Test { + def foo(v : Enum.Value) = v match { + case Enum.B => println("B") + } +} diff --git a/tests/patmat/t4408.check b/tests/patmat/t4408.check new file mode 100644 index 000000000000..53bfe1c2c564 --- /dev/null +++ b/tests/patmat/t4408.check @@ -0,0 +1,5 @@ +./tests/patmat/t4408.scala:2: warning: match may not be exhaustive. +It would fail on the following input: List(_, _, _) + def printList(in: List[String]): Unit = in match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t4408.scala b/tests/patmat/t4408.scala new file mode 100644 index 000000000000..419b6636971e --- /dev/null +++ b/tests/patmat/t4408.scala @@ -0,0 +1,16 @@ +object Test { + def printList(in: List[String]): Unit = in match { + case Nil => Unit + + case (s: String) :: Nil => + println(s) + + case head :: (s: String) :: Nil => + printList(head :: Nil) + for(i <- head){ + print(i) + } + println + println(s) + } +} diff --git a/tests/patmat/t4526.check b/tests/patmat/t4526.check new file mode 100644 index 000000000000..b577cbc0c121 --- /dev/null +++ b/tests/patmat/t4526.check @@ -0,0 +1,13 @@ +./tests/patmat/t4526.scala:2: warning: match may not be exhaustive. +It would fail on the following input: _: Int + def foo(a: Int) = a match { + ^ +./tests/patmat/t4526.scala:7: warning: match may not be exhaustive. +It would fail on the following input: (_, _) + def bar(a: (Int, Int)) = a match { + ^ +./tests/patmat/t4526.scala:12: warning: match may not be exhaustive. +It would fail on the following input: (false, false), (true, true) + def baz(a: (Boolean, Boolean)) = a match { + ^ +three warnings found \ No newline at end of file diff --git a/tests/patmat/t4526.scala b/tests/patmat/t4526.scala new file mode 100644 index 000000000000..d531c6b34304 --- /dev/null +++ b/tests/patmat/t4526.scala @@ -0,0 +1,16 @@ +object Test{ + def foo(a: Int) = a match { + case 5 => "Five!" + case 42 => "The answer." + } + + def bar(a: (Int, Int)) = a match { + case (5, 5) => "Two fives!" + case (42, 21) => "The answer and a half." + } + + def baz(a: (Boolean, Boolean)) = a match { + case (true, false) => "tf" + case (false, true) => "ft" + } +} \ No newline at end of file diff --git a/tests/patmat/t4691.check b/tests/patmat/t4691.check new file mode 100644 index 000000000000..4d2c24506810 --- /dev/null +++ b/tests/patmat/t4691.check @@ -0,0 +1,5 @@ +./tests/patmat/t4691.scala:15: warning: match may not be exhaustive. +It would fail on the following input: NodeType2(_) + def test (x: Node) = x match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t4691.scala b/tests/patmat/t4691.scala new file mode 100644 index 000000000000..bfaa61670057 --- /dev/null +++ b/tests/patmat/t4691.scala @@ -0,0 +1,18 @@ +sealed trait Node + +class NodeType1 (val a:Int) extends Node +class NodeType2 (val b:Int) extends Node + +object NodeType1 { + def unapply (x : NodeType1) : Some[Int] = Some(x.a) +} + +object NodeType2 { + def unapply (x : NodeType2) : Some[Int] = Some(x.b) +} + +object Test { + def test (x: Node) = x match { + case NodeType1(a) => "got node type 1 " + a + } +} \ No newline at end of file diff --git a/tests/patmat/t5440.check b/tests/patmat/t5440.check new file mode 100644 index 000000000000..0780d652945e --- /dev/null +++ b/tests/patmat/t5440.check @@ -0,0 +1,5 @@ +./tests/patmat/t5440.scala:2: warning: match may not be exhaustive. +It would fail on the following input: (Nil, List(_)), (List(_), Nil) + def merge(list1: List[Long], list2: List[Long]): Boolean = (list1, list2) match { + ^ +one warning found diff --git a/tests/patmat/t5440.scala b/tests/patmat/t5440.scala new file mode 100644 index 000000000000..6721b0562466 --- /dev/null +++ b/tests/patmat/t5440.scala @@ -0,0 +1,6 @@ +object Test { + def merge(list1: List[Long], list2: List[Long]): Boolean = (list1, list2) match { + case (hd1::_, hd2::_) => true + case (Nil, Nil) => true + } +} \ No newline at end of file diff --git a/tests/patmat/t5968.scala b/tests/patmat/t5968.scala new file mode 100644 index 000000000000..14cc903c8033 --- /dev/null +++ b/tests/patmat/t5968.scala @@ -0,0 +1,7 @@ +object Test { + object X + def f(e: Either[Int, X.type]) = e match { + case Left(i) => i + case Right(X) => 0 + } +} \ No newline at end of file diff --git a/tests/patmat/t6008.scala b/tests/patmat/t6008.scala new file mode 100644 index 000000000000..c42e9c5a5991 --- /dev/null +++ b/tests/patmat/t6008.scala @@ -0,0 +1,5 @@ +object Test { + def x(in: (Int, Boolean)) = in match { + case (i: Int, b: Boolean) => 3 + } +} \ No newline at end of file diff --git a/tests/patmat/t6420.check b/tests/patmat/t6420.check new file mode 100644 index 000000000000..c62b33d181a5 --- /dev/null +++ b/tests/patmat/t6420.check @@ -0,0 +1,5 @@ +./tests/patmat/t6420.scala:5: warning: match may not be exhaustive. +It would fail on the following input: (Nil, _), (List(_, _), _), (Nil, Nil), (Nil, List(_, _)), (List(_, _), Nil), (List(_, _), List(_, _)), (_, Nil), (_, List(_, _)) + def foo(x: List[Boolean], y: List[Boolean]) = (x,y) match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t6420.scala b/tests/patmat/t6420.scala new file mode 100644 index 000000000000..80c0f90f694b --- /dev/null +++ b/tests/patmat/t6420.scala @@ -0,0 +1,11 @@ +object Test { + val c0 = false + val c1 = true + + def foo(x: List[Boolean], y: List[Boolean]) = (x,y) match { + case (`c0`::x, `c0`::y) => x + case (`c0`::x, `c1`::y) => y + case (`c1`::x, `c0`::y) => y + case (`c1`::x, `c1`::y) => x + } +} diff --git a/tests/patmat/t6450.scala b/tests/patmat/t6450.scala new file mode 100644 index 000000000000..157f1ce8124e --- /dev/null +++ b/tests/patmat/t6450.scala @@ -0,0 +1,9 @@ +sealed abstract class FoundNode[T] +// case class A[T](x: T) extends FoundNode[T] + +object Foo { + val v: (Some[_], FoundNode[_]) = (???, ???) + v match { + case (x: Some[t], _) => + } +} diff --git a/tests/patmat/t6818.scala b/tests/patmat/t6818.scala new file mode 100644 index 000000000000..2334095c46d8 --- /dev/null +++ b/tests/patmat/t6818.scala @@ -0,0 +1,11 @@ +object Test { + type Id[X] = X + + def foo(x:Id[Option[Int]]) = x match { + case Some(n) => "foo" + case None => "bar" + } + + foo(Some(3)) // "foo" + foo(None) // "bar" +} \ No newline at end of file diff --git a/tests/patmat/t7206.scala.ignore b/tests/patmat/t7206.scala.ignore new file mode 100644 index 000000000000..0133f1808e7a --- /dev/null +++ b/tests/patmat/t7206.scala.ignore @@ -0,0 +1,19 @@ +object E extends Enumeration { + val V = Value +} + +sealed case class C(e: E.Value) + +class Test { + def foo(c: C) = { + c match { + case C(E.V) => {} + } + } + + def foo2(e: E.Value) = { + e match { + case E.V => {} + } + } +} diff --git a/tests/patmat/t7285.check b/tests/patmat/t7285.check new file mode 100644 index 000000000000..703706cdc532 --- /dev/null +++ b/tests/patmat/t7285.check @@ -0,0 +1,13 @@ +./tests/patmat/t7285.scala:15: warning: match may not be exhaustive. +It would fail on the following input: (Up, Down) + (d1, d2) match { + ^ +./tests/patmat/t7285.scala:33: warning: match may not be exhaustive. +It would fail on the following input: Down + (d1) match { + ^ +./tests/patmat/t7285.scala:51: warning: match may not be exhaustive. +It would fail on the following input: (Base.Up, Base.Down) + (d1, d2) match { + ^ +three warnings found \ No newline at end of file diff --git a/tests/patmat/t7285.scala b/tests/patmat/t7285.scala new file mode 100644 index 000000000000..d40df7fe8919 --- /dev/null +++ b/tests/patmat/t7285.scala @@ -0,0 +1,55 @@ +sealed abstract class Base + + +object Test1 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + def foo(d1: Base, d2: Base) = + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Up) => true + } + } +} + +object Test2 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + def foo(d1: Base, d2: Base) = + (d1) match { + case Test2.Base.Up => false + } + } +} + + +object Test4 { + sealed abstract class Base + + object Base { + case object Down extends Base + + case object Up extends Base + } + + import Test4.Base._ + def foo(d1: Base, d2: Base) = + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Test4.Base.Up) => true + } +} diff --git a/tests/patmat/t7285a.scala b/tests/patmat/t7285a.scala new file mode 100644 index 000000000000..49f6b663b28c --- /dev/null +++ b/tests/patmat/t7285a.scala @@ -0,0 +1,83 @@ +sealed abstract class Base + +object Test { + case object Up extends Base + + def foo(d1: Base) = + d1 match { + case Up => + } + + // Sealed subtype: ModuleTypeRef .this.Test.Up.type + // Pattern: UniqueThisType Test.this.type +} + + +object Test1 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + def foo(d1: Base, d2: Base) = + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Up) => true + case (Up, Down) => false + } + } +} + +object Test2 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + def foo(d1: Base, d2: Base) = + (d1) match { + case Up | Down => false + } + } +} + +object Test3 { + sealed abstract class Base + + object Base { + case object Down extends Base + + def foo(d1: Base, d2: Base) = + (d1, d2) match { + case (Down, Down) => false + } + } +} + +object Test4 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + } + import Test4.Base._ + def foo(d1: Base, d2: Base) = + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Test4.Base.Up) => true + case (Up, Down) => false + } +} diff --git a/tests/patmat/t7298.scala b/tests/patmat/t7298.scala new file mode 100644 index 000000000000..6fba5e120ca3 --- /dev/null +++ b/tests/patmat/t7298.scala @@ -0,0 +1,11 @@ +sealed trait Bool + +object Bool { + case object FALSE extends Bool + case object TRUE extends Bool + + def show(b: Bool) = b match { + case FALSE => "1" + case TRUE => "2" + } +} diff --git a/tests/patmat/t7353.scala b/tests/patmat/t7353.scala new file mode 100644 index 000000000000..7a8fea115fc4 --- /dev/null +++ b/tests/patmat/t7353.scala @@ -0,0 +1,11 @@ +sealed trait EthernetType + +object EthernetType { + final case object Gigabit extends EthernetType + final case object FastEthernet extends EthernetType + + final def toInt(t: EthernetType) = t match { + case Gigabit => 1 + case FastEthernet => 2 + } +} \ No newline at end of file diff --git a/tests/patmat/t7437.scala b/tests/patmat/t7437.scala new file mode 100644 index 000000000000..b0c5dff7cbbc --- /dev/null +++ b/tests/patmat/t7437.scala @@ -0,0 +1,17 @@ +sealed trait IntegralNumber +sealed trait FiniteNumber extends IntegralNumber + +object IntegralNumber { + + sealed abstract class BaseNumber extends IntegralNumber + sealed abstract class NonFinite extends BaseNumber + object NaN extends NonFinite + sealed abstract class FiniteNumberImpl[N](val value: N) extends BaseNumber with FiniteNumber + sealed class IntNumber(value: Int) extends FiniteNumberImpl[Int](value) + + def test(t: IntNumber, o: IntegralNumber) = o match { + case NaN => -1 + case o: IntNumber => t.value.compare(o.value) + } + +} \ No newline at end of file diff --git a/tests/patmat/t7466.check b/tests/patmat/t7466.check new file mode 100644 index 000000000000..8e575f6a213f --- /dev/null +++ b/tests/patmat/t7466.check @@ -0,0 +1,5 @@ +./tests/patmat/t7466.scala:8: warning: match may not be exhaustive. +It would fail on the following input: (_, _) + (b1, b2) match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t7466.scala b/tests/patmat/t7466.scala new file mode 100644 index 000000000000..a74bf4ee210f --- /dev/null +++ b/tests/patmat/t7466.scala @@ -0,0 +1,17 @@ +object Test extends App { + val Yes1 = true + val Yes2 = true + val No1 = false + val No2 = false + + def test(b1: Boolean, b2: Boolean) = { + (b1, b2) match { + case (No1, No2) => println("1") + case (No1, Yes2) => println("2") + case (Yes1, No2) => println("3") + case (Yes1, Yes2) => println("4") + } + } + + test(No1, Yes2) +} \ No newline at end of file diff --git a/tests/patmat/t7631.check b/tests/patmat/t7631.check new file mode 100644 index 000000000000..ede3703e2a60 --- /dev/null +++ b/tests/patmat/t7631.check @@ -0,0 +1,5 @@ +./tests/patmat/t7631.scala:8: warning: match may not be exhaustive. +It would fail on the following input: TestB() + val x = input match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t7631.scala b/tests/patmat/t7631.scala new file mode 100644 index 000000000000..13e74183f084 --- /dev/null +++ b/tests/patmat/t7631.scala @@ -0,0 +1,11 @@ +sealed trait Test +case class TestA() extends Test +case class TestB() extends Test + +object Tester { + val input : Test = TestA() + val num = 3 + val x = input match { + case TestA() if num == 3 => 2 + } +} \ No newline at end of file diff --git a/tests/patmat/t7669.check b/tests/patmat/t7669.check new file mode 100644 index 000000000000..2804dbf5c3ac --- /dev/null +++ b/tests/patmat/t7669.check @@ -0,0 +1,5 @@ +./tests/patmat/t7669.scala:10: warning: match may not be exhaustive. +It would fail on the following input: NotHandled(_) + def exhausto(expr: Expr): Unit = expr match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t7669.scala b/tests/patmat/t7669.scala new file mode 100644 index 000000000000..3aa74129ed9b --- /dev/null +++ b/tests/patmat/t7669.scala @@ -0,0 +1,14 @@ +object Test { + + sealed abstract class Expr + // Change type of `arg` to `Any` and the exhaustiveness warning + // is issued below + case class Op(arg: Expr) extends Expr + case class NotHandled(num: Double) extends Expr + + + def exhausto(expr: Expr): Unit = expr match { + case Op(Op(_)) => + case Op(_) => + } +} diff --git a/tests/patmat/t7746.check b/tests/patmat/t7746.check new file mode 100644 index 000000000000..be4c53570c68 --- /dev/null +++ b/tests/patmat/t7746.check @@ -0,0 +1,5 @@ +./tests/patmat/t7746.scala:2: warning: match may not be exhaustive. +It would fail on the following input: Some(_), None + def f[T](x: Option[T]) = x match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t7746.scala b/tests/patmat/t7746.scala new file mode 100644 index 000000000000..91f3823a47e9 --- /dev/null +++ b/tests/patmat/t7746.scala @@ -0,0 +1,5 @@ +object Test { + def f[T](x: Option[T]) = x match { + case Some(Some(5)) => true + } +} \ No newline at end of file diff --git a/tests/patmat/t8068.scala b/tests/patmat/t8068.scala new file mode 100644 index 000000000000..9837b7381413 --- /dev/null +++ b/tests/patmat/t8068.scala @@ -0,0 +1,14 @@ +trait K[A] { + sealed trait T + case class C(x: Int) extends T + case object O extends T +} + +object Hello { + def f[A](k: K[A])(t: k.T) = { + t match { + case k.C(x) => ??? + case k.O => ??? + } + } +} diff --git a/tests/patmat/t8178.check b/tests/patmat/t8178.check new file mode 100644 index 000000000000..963845f5310c --- /dev/null +++ b/tests/patmat/t8178.check @@ -0,0 +1,13 @@ +./tests/patmat/t8178.scala:6: warning: match may not be exhaustive. +It would fail on the following input: FailsChild2(_) + f match { + ^ +./tests/patmat/t8178.scala:14: warning: match may not be exhaustive. +It would fail on the following input: VarArgs1(_) + f match { + ^ +./tests/patmat/t8178.scala:27: warning: match may not be exhaustive. +It would fail on the following input: SeqArgs2(_) + f match { + ^ +three warnings found \ No newline at end of file diff --git a/tests/patmat/t8178.scala b/tests/patmat/t8178.scala new file mode 100644 index 000000000000..4fb39955bc0e --- /dev/null +++ b/tests/patmat/t8178.scala @@ -0,0 +1,33 @@ +sealed trait Fails +case class VarArgs1(a: String*) extends Fails +case class FailsChild2(a: Seq[String]) extends Fails +object FailsTest { + def matchOnVarArgsFirstFails(f: Fails) = { + f match { + case VarArgs1(_) => ??? + // BUG: Without this line we should get a non-exhaustive match compiler error. + //case FailsChild2(_) => ??? + } + } + + def matchOnSeqArgsFirstWorks(f: Fails) = { + f match { + case FailsChild2(_) => ??? + // Without this line, the compiler reports a "match may not be exhaustive" error as expected. + // case VarArgs1(_) => ??? + } + } +} + +sealed trait Works +case class SeqArgs1(a: Seq[String]) extends Works +case class SeqArgs2(a: Seq[String]) extends Works +object WorksTest { + def matcher(f: Works) = { + f match { + case SeqArgs1(_) => ??? + // Without this line, the compiler reports a "match may not be exhaustive" error as expected. + // case SeqArgs2(_) => ??? + } + } +} \ No newline at end of file diff --git a/tests/patmat/t8412.check b/tests/patmat/t8412.check new file mode 100644 index 000000000000..b82b33999613 --- /dev/null +++ b/tests/patmat/t8412.check @@ -0,0 +1,5 @@ +./tests/patmat/t8412.scala:7: warning: match may not be exhaustive. +It would fail on the following input: Lit(_) + tree match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t8412.scala b/tests/patmat/t8412.scala new file mode 100644 index 000000000000..f4b2b60907ff --- /dev/null +++ b/tests/patmat/t8412.scala @@ -0,0 +1,14 @@ +sealed trait Tree +case class Let(sth: List[Any]) extends Tree +case class Lit(sth: Any) extends Tree + +object Test { + def wroong(tree: Tree) = + tree match { + case Let(_ :: rest) => + ??? + case Let(Nil) => + ??? + // no warning for missing Lit(_) in 2.10 + } +} diff --git a/tests/patmat/t8430.check b/tests/patmat/t8430.check new file mode 100644 index 000000000000..4493062bfceb --- /dev/null +++ b/tests/patmat/t8430.check @@ -0,0 +1,5 @@ +./tests/patmat/t8430.scala:15: warning: match may not be exhaustive. +It would fail on the following input: LetF, LetC, LetP, LetL(UnitLit), LetL(BooleanLit), LetL(IntLit) + def transform(tree: Tree) : Any = tree match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t8430.scala b/tests/patmat/t8430.scala new file mode 100644 index 000000000000..ccd4585d94b0 --- /dev/null +++ b/tests/patmat/t8430.scala @@ -0,0 +1,19 @@ +sealed trait CL3Literal +case object IntLit extends CL3Literal +case object CharLit extends CL3Literal +case object BooleanLit extends CL3Literal +case object UnitLit extends CL3Literal + + +sealed trait Tree +case class LetL(value: CL3Literal) extends Tree +case object LetP extends Tree +case object LetC extends Tree +case object LetF extends Tree + +object Test { + def transform(tree: Tree) : Any = tree match { + case LetL(CharLit) => + ??? + } +} diff --git a/tests/patmat/t8511.check b/tests/patmat/t8511.check new file mode 100644 index 000000000000..df07d019aa33 --- /dev/null +++ b/tests/patmat/t8511.check @@ -0,0 +1,5 @@ +./tests/patmat/t8511.scala:18: warning: match may not be exhaustive. +It would fail on the following input: Baz(), Bar(_) + private def logic(head: Expr): String = head match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t8511.scala b/tests/patmat/t8511.scala new file mode 100644 index 000000000000..bc7f64713441 --- /dev/null +++ b/tests/patmat/t8511.scala @@ -0,0 +1,25 @@ +sealed trait Expr +final case class Foo(other: Option[String]) extends Expr +final case class Bar(someConstant: String) extends Expr +final case class Baz() extends Expr +final case class EatsExhaustiveWarning(other: Reference) extends Expr + +sealed trait Reference { + val value: String +} + +object Reference { + def unapply(reference: Reference): Option[(String)] = { + Some(reference.value) + } +} + +object EntryPoint { + private def logic(head: Expr): String = head match { + case Foo(_) => + ??? + // Commenting this line only causes the exhaustive search warning to be emitted + case EatsExhaustiveWarning(Reference(text)) => + ??? + } +} \ No newline at end of file diff --git a/tests/patmat/t8546.scala b/tests/patmat/t8546.scala new file mode 100644 index 000000000000..c39d749b4c45 --- /dev/null +++ b/tests/patmat/t8546.scala @@ -0,0 +1,49 @@ +package test + +class F1() { + private sealed abstract class T + private case class A(m: Int) extends T + private case class B() extends T + private case object C extends T + + // No warnings here + private def foo(t: T) = t match { + case A(m) => println("A:" + m) + case B() => println("B") + case C => println("C") + } + + def test(m: Int): Unit = { + foo(A(m)) + foo(B()) + foo(C) + } +} + +class F2[M]() { + private sealed abstract class T + private case class A(m: M) extends T + private case class B() extends T + private case object C extends T + + // match may not be exhaustive. It would fail on the following input: C + private def foo(t: T) = t match { + case A(m) => println("A:" + m) + case B() => println("B") + case C => println("C") + } + + def test(m: M): Unit = { + foo(A(m)) + foo(B()) + foo(C) + } + +} + +object Test { + def main(args: Array[String]): Unit = { + new F1().test(1) + new F2[Int]().test(1) + } +} \ No newline at end of file diff --git a/tests/patmat/t8606.scala b/tests/patmat/t8606.scala new file mode 100644 index 000000000000..9388c9f02bd0 --- /dev/null +++ b/tests/patmat/t8606.scala @@ -0,0 +1,18 @@ +class Cl[T] { + + sealed trait A { + def foo = this match { + case AObj => 0 + case BObj => 0 + case ACls(x) => 0 + case BCls(x) => 0 + } + } + + case object AObj extends A + case class ACls(x: Int) extends A + + sealed trait B extends A + case object BObj extends B + case class BCls(x: Int) extends B +} diff --git a/tests/patmat/t9129.check b/tests/patmat/t9129.check new file mode 100644 index 000000000000..aa722a61acad --- /dev/null +++ b/tests/patmat/t9129.check @@ -0,0 +1,5 @@ +./tests/patmat/t9129.scala:21: warning: match may not be exhaustive. +It would fail on the following input: Two(B2, A2), Two(_, A2) + def foo(c: C): Unit = c match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t9129.scala b/tests/patmat/t9129.scala new file mode 100644 index 000000000000..89f08f0ac2db --- /dev/null +++ b/tests/patmat/t9129.scala @@ -0,0 +1,29 @@ +object Test { + + sealed abstract class A + + case object A1 extends A + + case object A2 extends A + + sealed abstract class B + + case object B1 extends B + + case object B2 extends B + + sealed abstract class C + + final case class One(a: A, b: B) extends C + + final case class Two(b: B, a: A) extends C + + def foo(c: C): Unit = c match { + case One(A1, B1) => + case One(A2, B1) => + case One(A1, B2) => + case One(A2, B2) => + case Two(B1, A1) => + case Two(B2, A1) => + } +} \ No newline at end of file diff --git a/tests/patmat/t9232.check b/tests/patmat/t9232.check new file mode 100644 index 000000000000..c3957c0ffa1d --- /dev/null +++ b/tests/patmat/t9232.check @@ -0,0 +1,5 @@ +./tests/patmat/t9232.scala:13: warning: match may not be exhaustive. +It would fail on the following input: Node2() + def transformTree(tree: Tree): Any = tree match { + ^ +one warning found diff --git a/tests/patmat/t9232.scala b/tests/patmat/t9232.scala new file mode 100644 index 000000000000..975ec58db823 --- /dev/null +++ b/tests/patmat/t9232.scala @@ -0,0 +1,16 @@ +final class Foo(val value: Int) + +object Foo { + def unapplySeq(foo: Foo): Some[Seq[Int]] = Some(List(foo.value)) + // def unapply(foo: Foo): Some[Int] = Some(foo.value) +} + +sealed trait Tree +case class Node1(foo: Foo) extends Tree +case class Node2() extends Tree + +object Test { + def transformTree(tree: Tree): Any = tree match { + case Node1(Foo(1)) => ??? + } +} diff --git a/tests/patmat/t9289.check b/tests/patmat/t9289.check new file mode 100644 index 000000000000..5240988e2606 --- /dev/null +++ b/tests/patmat/t9289.check @@ -0,0 +1,9 @@ +./tests/patmat/t9289.scala:9: warning: match may not be exhaustive. +It would fail on the following input: module.LetR() + def patmat(tree: module.Tree) = tree match { + ^ +./tests/patmat/t9289.scala:20: warning: match may not be exhaustive. +It would fail on the following input: module.LetR() + def patmat(tree: module.Tree) = tree match { + ^ +two warnings found \ No newline at end of file diff --git a/tests/patmat/t9289.scala b/tests/patmat/t9289.scala new file mode 100644 index 000000000000..714a4a0e3928 --- /dev/null +++ b/tests/patmat/t9289.scala @@ -0,0 +1,28 @@ +trait Module { + sealed trait Tree + + case class LetL() extends Tree + case class LetR() extends Tree +} + +class Patmat[T <: Module](val module: T) { + def patmat(tree: module.Tree) = tree match { + case module.LetL() => ??? + } + + def exhaust(tree: module.Tree) = tree match { + case module.LetL() => ??? + case module.LetR() => ??? + } +} + +class Patmat2(val module: Module) { + def patmat(tree: module.Tree) = tree match { + case module.LetL() => ??? + } + + def exhaust(tree: module.Tree) = tree match { + case module.LetL() => ??? + case module.LetR() => ??? + } +} diff --git a/tests/patmat/t9351.check b/tests/patmat/t9351.check new file mode 100644 index 000000000000..03b94c2c001b --- /dev/null +++ b/tests/patmat/t9351.check @@ -0,0 +1,13 @@ +./tests/patmat/t9351.scala:8: warning: match may not be exhaustive. +It would fail on the following input: _: A + a match { + ^ +./tests/patmat/t9351.scala:17: warning: match may not be exhaustive. +It would fail on the following input: (_, _), (_, None), (_, Some(_)) + (a, o) match { + ^ +./tests/patmat/t9351.scala:28: warning: match may not be exhaustive. +It would fail on the following input: (_, _) + (a, b) match { + ^ +three warnings found \ No newline at end of file diff --git a/tests/patmat/t9351.scala b/tests/patmat/t9351.scala new file mode 100644 index 000000000000..9b9bd4312ae7 --- /dev/null +++ b/tests/patmat/t9351.scala @@ -0,0 +1,35 @@ +trait A {} +case object B extends A {} +case object C extends A {} + +class X { + def good = { + val a: A = B + a match { + case B => + case C => + } + } + + def bad = { + val a: A = B + val o: Option[Int] = None + (a, o) match { + case (B, None) => + case (B, Some(_)) => + case (C, None) => + case (C, Some(_)) => + } + } + + def alsoGood = { + val a: A = B + val b: A = C + (a, b) match { + case (B, B) => + case (B, C) => + case (C, B) => + case (C, C) => + } + } +} diff --git a/tests/patmat/t9398.check b/tests/patmat/t9398.check new file mode 100644 index 000000000000..0efbf231d6c5 --- /dev/null +++ b/tests/patmat/t9398.check @@ -0,0 +1,5 @@ +./tests/patmat/t9398.scala:11: warning: match may not be exhaustive. +It would fail on the following input: CC(_, B2) + case CC(_, B) => () + ^ +one warning found diff --git a/tests/patmat/t9398.scala b/tests/patmat/t9398.scala new file mode 100644 index 000000000000..6d4d6bd3b111 --- /dev/null +++ b/tests/patmat/t9398.scala @@ -0,0 +1,13 @@ +sealed abstract class TA +sealed abstract class TB extends TA +case object B extends TB +case object B2 extends TB + +case class CC(i: Int, tb: TB) + +object Test { + // Should warn that CC(_, B2) isn't matched + def foo: CC => Unit = { + case CC(_, B) => () + } +} \ No newline at end of file diff --git a/tests/patmat/t9399.scala b/tests/patmat/t9399.scala new file mode 100644 index 000000000000..89dbedd9672f --- /dev/null +++ b/tests/patmat/t9399.scala @@ -0,0 +1,16 @@ +sealed abstract class TA +sealed abstract class TB extends TA +case object A extends TA +case object B extends TB + +sealed trait C +case class CTA(id: Int, da: TA) extends C +case class CTB(id: Int, da: TB) extends C + +object Test { + val test: C => Unit = { + case CTA(_, A) => + case CTA(_, B) => + case CTB(_, B) => + } +} diff --git a/tests/patmat/t9411a.scala b/tests/patmat/t9411a.scala new file mode 100644 index 000000000000..d5264663ece9 --- /dev/null +++ b/tests/patmat/t9411a.scala @@ -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) => + } +} diff --git a/tests/patmat/t9411b.scala b/tests/patmat/t9411b.scala new file mode 100644 index 000000000000..6888ba9382c8 --- /dev/null +++ b/tests/patmat/t9411b.scala @@ -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) => + } +} diff --git a/tests/patmat/t9573.check b/tests/patmat/t9573.check new file mode 100644 index 000000000000..4ec379161422 --- /dev/null +++ b/tests/patmat/t9573.check @@ -0,0 +1,5 @@ +./tests/patmat/t9573.scala:9: warning: match may not be exhaustive. +It would fail on the following input: Horse(_) + x match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t9573.scala b/tests/patmat/t9573.scala new file mode 100644 index 000000000000..2a32c2599f23 --- /dev/null +++ b/tests/patmat/t9573.scala @@ -0,0 +1,13 @@ +class Foo { + + def foo = { + abstract sealed class Animal + case class Goat(age: Int) extends Animal + case class Horse(age: Int) extends Animal + + val x: Animal = Goat(1) + x match { + case Goat(_) => println("a goat") + } + } +} \ No newline at end of file diff --git a/tests/patmat/t9630.scala b/tests/patmat/t9630.scala new file mode 100644 index 000000000000..c846faa99350 --- /dev/null +++ b/tests/patmat/t9630.scala @@ -0,0 +1,21 @@ +sealed trait OpError +sealed trait RequestErrorType +sealed trait ProcessingErrorType + +final case class InvalidEndpoint(reason: String) extends RequestErrorType +final case class InvalidParameters(reason: String) extends RequestErrorType + +final case class InvalidFormat(response: String) extends ProcessingErrorType +final case class EntityNotFound(id: Long) extends ProcessingErrorType + +final case class RequestError(errorType: RequestErrorType) extends OpError +final case class ProcessingError(errorType: ProcessingErrorType) extends OpError + +object Test{ + def printMatches(error: OpError): Unit = error match { + case RequestError(InvalidEndpoint(reason)) => //print something + case RequestError(InvalidParameters(reason)) => //print something + case ProcessingError(InvalidFormat(format)) => //print something + case ProcessingError(EntityNotFound(entityId)) => //print something + } +} \ No newline at end of file diff --git a/tests/patmat/t9657.check b/tests/patmat/t9657.check new file mode 100644 index 000000000000..d3e2ec73f494 --- /dev/null +++ b/tests/patmat/t9657.check @@ -0,0 +1,17 @@ +./tests/patmat/t9657.scala:29: warning: match may not be exhaustive. +It would fail on the following input: Bus(_) + def refuel2[P <: Petrol.type](vehicle: Vehicle {type A = P} ): Vehicle = vehicle match { + ^ +./tests/patmat/t9657.scala:38: warning: match may not be exhaustive. +It would fail on the following input: Bus(_) + def foo2(vehicle: Vehicle {type A <: Petrol.type} ): Vehicle = vehicle match { + ^ +./tests/patmat/t9657.scala:49: warning: match may not be exhaustive. +It would fail on the following input: Bus(_) + def bar2(vehicle: Vehicle {type A <: P} ): Vehicle = vehicle match { + ^ +./tests/patmat/t9657.scala:58: warning: match may not be exhaustive. +It would fail on the following input: Bus(_) + def qux2[P <: Petrol.type](vehicle: Vehicle {type A <: P} ): Vehicle = vehicle match { + ^ +four warnings found \ No newline at end of file diff --git a/tests/patmat/t9657.scala b/tests/patmat/t9657.scala new file mode 100644 index 000000000000..f9769574ed35 --- /dev/null +++ b/tests/patmat/t9657.scala @@ -0,0 +1,62 @@ +sealed trait PowerSource + +case object Petrol extends PowerSource + +case object Pedal extends PowerSource + +sealed abstract class Vehicle { + type A <: PowerSource +} + +case object Bicycle extends Vehicle { + type A = Pedal.type +} + +case class Bus(fuel: Int) extends Vehicle { + type A = Petrol.type +} + +case class Car(fuel: Int) extends Vehicle { + type A = Petrol.type +} + +class Test { + def refuel[P <: Petrol.type](vehicle: Vehicle {type A = P} ): Vehicle = vehicle match { + case Car(_) => Car(100) + case Bus(_) => Bus(100) + } + + def refuel2[P <: Petrol.type](vehicle: Vehicle {type A = P} ): Vehicle = vehicle match { + case Car(_) => Car(100) + } + + def foo1(vehicle: Vehicle {type A <: Petrol.type} ): Vehicle = vehicle match { + case Car(_) => Car(100) + case Bus(_) => Bus(100) + } + + def foo2(vehicle: Vehicle {type A <: Petrol.type} ): Vehicle = vehicle match { + case Car(_) => Car(100) + } + + type P = Petrol.type + + def bar1(vehicle: Vehicle {type A <: P} ): Vehicle = vehicle match { + case Car(_) => Car(100) + case Bus(_) => Bus(100) + } + + def bar2(vehicle: Vehicle {type A <: P} ): Vehicle = vehicle match { + case Car(_) => Car(100) + } + + def qux1[P <: Petrol.type](vehicle: Vehicle {type A <: P} ): Vehicle = vehicle match { + case Car(_) => Car(100) + case Bus(_) => Bus(100) + } + + def qux2[P <: Petrol.type](vehicle: Vehicle {type A <: P} ): Vehicle = vehicle match { + case Car(_) => Car(100) + } + +} diff --git a/tests/patmat/t9672.check b/tests/patmat/t9672.check new file mode 100644 index 000000000000..3284d1df1e8d --- /dev/null +++ b/tests/patmat/t9672.check @@ -0,0 +1,5 @@ +./tests/patmat/t9672.scala:22: warning: match may not be exhaustive. +It would fail on the following input: SimpleExpr.IntExpr(_) + def func(expr: Expr) = expr match { + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t9672.scala b/tests/patmat/t9672.scala new file mode 100644 index 000000000000..fe068f3d5e78 --- /dev/null +++ b/tests/patmat/t9672.scala @@ -0,0 +1,28 @@ +trait Hierarchy { + sealed trait Expr +} +trait If { + this: Hierarchy => + case class If(cond: Expr, yes: Expr, no: Expr) extends Expr +} +trait Word { + this: Hierarchy => + case class Word(name: String) extends Expr +} +trait IntExpr { + this: Hierarchy => + case class IntExpr(value : Int) extends Expr +} + +object SimpleExpr extends Hierarchy with If with Word with IntExpr +//object OtherExpr extends Hierarchy with If with IntExpr + +object Demo extends App { + import SimpleExpr._ + def func(expr: Expr) = expr match { + case If(cond, yes, no) => cond + case Word(name) => name + // compiler should emit warning "missing case statement" + // emits the wrong warning "unreachable code" + } +} \ No newline at end of file diff --git a/tests/patmat/t9677.check b/tests/patmat/t9677.check new file mode 100644 index 000000000000..f1e1817cbbaa --- /dev/null +++ b/tests/patmat/t9677.check @@ -0,0 +1,4 @@ +./tests/patmat/t9677.scala:20: warning: unreachable code + case path: A => println("Not root") + ^ +one warning found \ No newline at end of file diff --git a/tests/patmat/t9677.scala b/tests/patmat/t9677.scala new file mode 100644 index 000000000000..1e9b1df5e891 --- /dev/null +++ b/tests/patmat/t9677.scala @@ -0,0 +1,23 @@ +sealed abstract class Base + +sealed trait A extends Base + +object A { + + case object Root extends Base + + def apply(param: String): A = { + new A {} + } +} + +object ExhaustiveMatchWarning { + + def test: Unit = { + val b: Base = A("blabla") + b match { + case A.Root => println("Root") + case path: A => println("Not root") + } + } +} \ No newline at end of file diff --git a/tests/patmat/try.scala b/tests/patmat/try.scala new file mode 100644 index 000000000000..d7df24ee0184 --- /dev/null +++ b/tests/patmat/try.scala @@ -0,0 +1,5 @@ +object Test { + try 2/0 catch { + case e: Exception => + } +} \ No newline at end of file diff --git a/tests/patmat/tuple.scala b/tests/patmat/tuple.scala new file mode 100644 index 000000000000..f33a5cfec38f --- /dev/null +++ b/tests/patmat/tuple.scala @@ -0,0 +1,5 @@ +object Test { + (4, (4, 6)) match { + case (x, (y, z)) => true + } +} \ No newline at end of file diff --git a/tests/patmat/virtpatmat_apply.check b/tests/patmat/virtpatmat_apply.check new file mode 100644 index 000000000000..d10d82165a3e --- /dev/null +++ b/tests/patmat/virtpatmat_apply.check @@ -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 \ No newline at end of file diff --git a/tests/patmat/virtpatmat_apply.scala b/tests/patmat/virtpatmat_apply.scala new file mode 100644 index 000000000000..646d15f902ac --- /dev/null +++ b/tests/patmat/virtpatmat_apply.scala @@ -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) + } +}