summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-12-15 18:28:03 -0800
committerPaul Phillips <paulp@improving.org>2013-12-15 18:28:03 -0800
commitdef46a9d448c4ee84eea48694a65af438370f940 (patch)
tree43ff702f2988f0d88780c92394762991c4bb8764 /src
parent11bfa25e37d32f4017d5c04b4899b1bdfbd95e06 (diff)
downloadscala-def46a9d448c4ee84eea48694a65af438370f940.tar.gz
scala-def46a9d448c4ee84eea48694a65af438370f940.tar.bz2
scala-def46a9d448c4ee84eea48694a65af438370f940.zip
SI-7850 CCE in patmat with invalid isEmpty.
Name-based pattern matcher needed some hardening against unapply methods with the right name but wrong types. Only isEmpty methods which return Boolean are acceptable. Catching it directly rather than indirectly also allowed for better error messages.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala24
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala2
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
4 files changed, 21 insertions, 9 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 719d04a7f9..12d9f8886d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -625,8 +625,7 @@ trait ContextErrors {
setError(tree)
}
- def CaseClassConstructorError(tree: Tree) = {
- val baseMessage = tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method"
+ def CaseClassConstructorError(tree: Tree, baseMessage: String) = {
val addendum = directUnapplyMember(tree.symbol.info) match {
case sym if hasMultipleNonImplicitParamLists(sym) => s"\nNote: ${sym.defString} exists in ${tree.symbol}, but it cannot be used as an extractor due to its second non-implicit parameter list"
case _ => ""
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
index 1fef6bd66b..41c656f8ce 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
@@ -78,18 +78,32 @@ trait PatternTypers {
// Do some ad-hoc overloading resolution and update the tree's symbol and type
// do not update the symbol if the tree's symbol's type does not define an unapply member
// (e.g. since it's some method that returns an object with an unapply member)
- val fun = inPlaceAdHocOverloadingResolution(fun0)(hasUnapplyMember)
- def caseClass = fun.tpe.typeSymbol.linkedClassOfClass
+ val fun = inPlaceAdHocOverloadingResolution(fun0)(hasUnapplyMember)
+ val caseClass = fun.tpe.typeSymbol.linkedClassOfClass
+ val member = unapplyMember(fun.tpe)
+ def resultType = (fun.tpe memberType member).finalResultType
+ def isEmptyType = resultOfMatchingMethod(resultType, nme.isEmpty)()
+ def isOkay = (
+ resultType.isErroneous
+ || (resultType <:< BooleanTpe)
+ || (isEmptyType <:< BooleanTpe)
+ || member.isMacro
+ || member.isOverloaded // the whole overloading situation is over the rails
+ )
// Dueling test cases: pos/overloaded-unapply.scala, run/case-class-23.scala, pos/t5022.scala
// A case class with 23+ params has no unapply method.
// A case class constructor may be overloaded with unapply methods in the companion.
- if (caseClass.isCase && !unapplyMember(fun.tpe).isOverloaded)
+ if (caseClass.isCase && !member.isOverloaded)
logResult(s"convertToCaseConstructor($fun, $caseClass, pt=$pt)")(convertToCaseConstructor(fun, caseClass, pt))
- else if (hasUnapplyMember(fun))
+ else if (!reallyExists(member))
+ CaseClassConstructorError(fun, s"${fun.symbol} is not a case class, nor does it have an unapply/unapplySeq member")
+ else if (isOkay)
fun
+ else if (isEmptyType == NoType)
+ CaseClassConstructorError(fun, s"an unapply result must have a member `def isEmpty: Boolean")
else
- CaseClassConstructorError(fun)
+ CaseClassConstructorError(fun, s"an unapply result must have a member `def isEmpty: Boolean (found: def isEmpty: $isEmptyType)")
}
def typedArgsForFormals(args: List[Tree], formals: List[Type], mode: Mode): List[Tree] = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
index 6557a9803a..ed96f66ab8 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
@@ -39,8 +39,6 @@ trait Unapplies extends ast.TreeDSL {
*/
def unapplyMember(tp: Type): Symbol = directUnapplyMember(tp) filter (sym => !hasMultipleNonImplicitParamLists(sym))
- def unapplyMemberType(tree: Tree): Type = tree.tpe memberType unapplyMember(tree.tpe)
-
object HasUnapply {
def unapply(tp: Type): Option[Symbol] = unapplyMember(tp).toOption
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 5ad2b15e8c..f2bf4172ec 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -691,6 +691,7 @@ trait StdNames {
val inlinedEquals: NameType = "inlinedEquals"
val isArray: NameType = "isArray"
val isDefinedAt: NameType = "isDefinedAt"
+ val isEmpty: NameType = "isEmpty"
val isInstanceOf_ : NameType = "isInstanceOf"
val isInstanceOf_Ob : NameType = "$isInstanceOf"
val java: NameType = "java"