summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/matching/PatternMatchers.scala76
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala2
-rw-r--r--src/library/scala/Iterator.scala39
-rw-r--r--test/files/neg/patmatexhaust.check20
-rw-r--r--test/files/neg/patmatexhaust.scala59
-rw-r--r--test/files/run/unapplyArray.scala31
6 files changed, 218 insertions, 9 deletions
diff --git a/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala
index d3217c8672..466f4e96c9 100644
--- a/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala
+++ b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala
@@ -1002,44 +1002,104 @@ print()
LabelDef(exit, List(result), Ident(result)))
}
+ type SymSet = collection.immutable.Set[Symbol]
+
/*protected*/ def toTree(node1: PatternNode): Tree = {
- def optimize1(selType:Type, alternatives1: PatternNode ): Boolean = {
+
+ def checkEx(tpesym:Symbol) = tpesym.hasFlag(Flags.SEALED)
+ def checkExCoverage(tpesym:Symbol): SymSet = if(!checkEx(tpesym))
+ emptySymbolSet else {
+ //Console.println("/"+tpesym)
+ //Console.println(tpesym.children)
+ tpesym.children.flatMap { x => if(x.hasFlag(Flags.CASE)) (emptySymbolSet+x) else checkExCoverage(x) }
+ }
+ def andIsUnguardedBody(p1:PatternNode) = p1.and match {
+ case p: Body => p.guard.length == 1 && p.guard(0) == EmptyTree
+ case _ => false
+ }
+ def andIsGuarded(p1:PatternNode) = p1.and match {
+ case p: Body => p.guard.length >= 1 && p.guard(0) == EmptyTree
+ case _ => false
+ }
+
+
+ // returns true if this tree is optimizable
+ // throws a warning if is not exhaustive
+ def optimize1(selType:Type, alternatives1: PatternNode ): {Boolean, collection.immutable.Set[Symbol], collection.immutable.Set[Symbol]} = {
+// if selType hasflag SEALED, enumerate selType.symbol.children
+// to be very precise, take into account the case that a non case child is sealed.
var alts = alternatives1;
if (!optimize || !isSubType(selType, defs.ScalaObjectClass.tpe))
- return false;
+ return {false, null, emptySymbolSet};
+ // contains cases that have not been covered
+ var coveredCases: SymSet = emptySymbolSet // only case _
+ var remainingCases = if(alts ne null) checkExCoverage(selType.symbol) else emptySymbolSet // only case _
var cases = 0;
while (alts ne null) {
alts match {
case ConstrPat(_) =>
- if (alts.getTpe().symbol.hasFlag(Flags.CASE))
+ coveredCases = coveredCases + alts.getTpe.symbol
+ remainingCases = remainingCases - alts.getTpe.symbol
+ if (alts.getTpe.symbol.hasFlag(Flags.CASE))
cases = cases +1;
else
- return false;
+ return {false, coveredCases, remainingCases};
case DefaultPat() =>
- ;
+ if(andIsUnguardedBody(alts) || alts.and.isInstanceOf[Header]) {
+ coveredCases = emptySymbolSet
+ remainingCases = emptySymbolSet
+ }
case _ =>
- return false;
+ return {false, coveredCases, remainingCases};
}
alts = alts.or;
}
- return cases > 2;
+ return {cases > 2, coveredCases, remainingCases};
} // def optimize
var node = node1;
var res: Tree = Literal(Constant(false)); //.setInfo(defs.BooleanClass);
+ var lastSelector: Tree = null
+ var carryCovered: SymSet = emptySymbolSet
while (node ne null)
node match {
case _h:Header =>
val selector = _h.selector;
+ if(selector != lastSelector) {
+ carryCovered = emptySymbolSet;
+ }
+ //Console.println("sel:"+selector+" last"+lastSelector+" - "+(selector == lastSelector))
val next = _h.next;
//res = And(mkNegate(res), toTree(node.or, selector));
- if (optimize1(node.getTpe(), node.or))
+ val {doOptimize, coveredCases, remainingCases} = optimize1(node.getTpe, node.or)
+ if(!remainingCases.isEmpty) {
+ carryCovered = carryCovered ++ coveredCases // ??
+ if(next != null && andIsUnguardedBody(next.or)) {
+ // ignore, default case
+ } else if(next ne null) {
+ // ignore, more headers to come
+ // Console.println(next.print("", new StringBuilder()).toString())
+ } else {
+ val realRemainingCases = remainingCases -- carryCovered
+ //Console.println("remain "+remainingCases+" carry covered "+ carryCovered + "real "+realRemainingCases)
+ if(!realRemainingCases.isEmpty) {
+ val word = if(realRemainingCases.size > 1) "cases " else "case "
+ cunit.warning(node.pos, "does not cover "+word+realRemainingCases.elements.mkString("{",",","}"))
+ //Console.println(_h.print("", new StringBuilder()).toString())
+ }
+ }
+ }
+ if (doOptimize)
res = Or(res, toOptTree(node.or, selector));
else
res = Or(res, toTree(node.or, selector));
+
+ //Console.println("!carry covered "+ carryCovered)
+
+ lastSelector = selector
node = next;
case _b:Body =>
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index d1478f629c..9d45255ad0 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -429,7 +429,7 @@ trait Definitions requires SymbolTable {
"scala.runtime.Boxed" + name
val clazz =
newClass(ScalaPackageClass, name, List(AnyValClass.typeConstructor))
- .setFlag(ABSTRACT | SEALED)
+ .setFlag(ABSTRACT /* SEALED */) // bq: SEALED is interesting for case class descendants, only
boxedClass(clazz) = getClass(boxedName)
boxedArrayClass(clazz) = getClass("scala.runtime.Boxed" + name + "Array")
refClass(clazz) = getClass("scala.runtime." + name + "Ref")
diff --git a/src/library/scala/Iterator.scala b/src/library/scala/Iterator.scala
index 4fccc09201..1ebd4c5c0d 100644
--- a/src/library/scala/Iterator.scala
+++ b/src/library/scala/Iterator.scala
@@ -14,6 +14,7 @@ package scala
import Predef._
import collection.mutable.{Buffer, ArrayBuffer}
+import compat.StringBuilder
/** The <code>Iterator</code> object provides various functions for
* creating specialized iterators.
@@ -605,4 +606,42 @@ trait Iterator[+A] {
}
res.toList
}
+
+ /** Returns a string representation of the elements in this iterator. The resulting string
+ * begins with the string <code>start</code> and is finished by the string
+ * <code>end</code>. Inside, the string representations of elements (w.r.t.
+ * the method <code>toString()</code>) are separated by the string
+ * <code>sep</code>.
+ * <p/>
+ * Ex: <br/>
+ * <code>List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"</code>
+ *
+ * @param start starting string.
+ * @param sep separator string.
+ * @param end ending string.
+ * @return a string representation of this iterable object.
+ */
+ def mkString(start: String, sep: String, end: String): String = {
+ val buf = new StringBuilder()
+ addString(buf, start, sep, end).toString
+ }
+
+ /** Returns a string representation of this iterable object. The string
+ * representations of elements (w.r.t. the method <code>toString()</code>)
+ * are separated by the string <code>sep</code>.
+ *
+ * @param sep separator string.
+ * @return a string representation of this iterable object. */
+ def mkString(sep: String): String = this.mkString("", sep, "")
+
+ /** Write all elements of this string into given string builder */
+ def addString(buf: StringBuilder, start: String, sep: String, end: String): StringBuilder = {
+ buf.append(start)
+ val elems = this
+ if (elems.hasNext) buf.append(elems.next)
+ while (elems.hasNext) {
+ buf.append(sep); buf.append(elems.next)
+ }
+ buf.append(end)
+ }
}
diff --git a/test/files/neg/patmatexhaust.check b/test/files/neg/patmatexhaust.check
new file mode 100644
index 0000000000..40054bad9f
--- /dev/null
+++ b/test/files/neg/patmatexhaust.check
@@ -0,0 +1,20 @@
+patmatexhaust.scala:8: warning: does not cover case {object Baz}
+ def ma1(x:Foo) = x match {
+ ^
+patmatexhaust.scala:12: warning: does not cover case {class Bar}
+ def ma2(x:Foo) = x match {
+ ^
+patmatexhaust.scala:24: warning: does not cover case {class Kult}
+ case {Kult(_), Qult()} => // Kult missing
+ ^
+patmatexhaust.scala:26: warning: does not cover case {class Qult}
+ case {Qult(), Kult(_)} => // Qult missing
+ ^
+patmatexhaust.scala:45: warning: does not cover case {object Gu}
+ def ma4(x:Deep) = x match { // missing cases: Gu
+ ^
+patmatexhaust.scala:57: error: unreachable code
+ case 1 =>
+ ^
+5 warnings found
+one error found
diff --git a/test/files/neg/patmatexhaust.scala b/test/files/neg/patmatexhaust.scala
new file mode 100644
index 0000000000..0416749b88
--- /dev/null
+++ b/test/files/neg/patmatexhaust.scala
@@ -0,0 +1,59 @@
+class TestSealedExhaustive { // compile only
+
+ sealed 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 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()} =>
+ }
+
+
+ sealed 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
+ case Ga =>
+ }
+
+ def zma5(x:Deep) = x match { // exhaustive
+ case Gu =>
+ case _ if 1 == 0 =>
+ case Ga =>
+ }
+
+ def redundant = 1 match { // include this otherwise script won't test this in files/neg
+ case 1 =>
+ case 1 =>
+ }
+}
diff --git a/test/files/run/unapplyArray.scala b/test/files/run/unapplyArray.scala
new file mode 100644
index 0000000000..bf6582dadf
--- /dev/null
+++ b/test/files/run/unapplyArray.scala
@@ -0,0 +1,31 @@
+object Test {
+ def main(args:Array[String]): Unit = {
+ val z = Array(1,2,3,4)
+ val zs: Seq[int] = z
+ val za: Any = z
+
+/*
+ Console.println("z is arr[int]"+z.isInstanceOf[Array[int]])
+ Console.println("zs is arr[int]"+zs.isInstanceOf[Array[int]])
+ Console.println("za is arr[int]"+ za.isInstanceOf[Array[int]])
+
+ Console.println("z is seq[int]"+z.isInstanceOf[Seq[int]])
+ Console.println("zs is seq[int]"+zs.isInstanceOf[Seq[int]])
+ Console.println("za is seq[int]"+ za.isInstanceOf[Seq[int]])
+
+ Console.println("z is anyref"+z.isInstanceOf[AnyRef])
+
+ Console.println("z useq "+ Seq.unapplySeq(z))
+ Console.println("zs useq "+ Seq.unapplySeq(zs))
+ Console.println("za useq "+ Seq.unapplySeq(za))
+
+ Console.println("z aseq "+ Seq.unapplySeq(z))
+ Console.println("zs aseq "+ Seq.unapplySeq(zs))
+ Console.println("za aseq "+ Seq.unapplySeq(za))
+*/
+ val zl = zs match {
+ case Seq(xs@_*) => xs.length
+ }
+ assert(zl == 4)
+ }
+}