diff options
author | Paul Phillips <paulp@improving.org> | 2013-12-15 18:28:03 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-12-15 18:28:03 -0800 |
commit | def46a9d448c4ee84eea48694a65af438370f940 (patch) | |
tree | 43ff702f2988f0d88780c92394762991c4bb8764 /src | |
parent | 11bfa25e37d32f4017d5c04b4899b1bdfbd95e06 (diff) | |
download | scala-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')
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" |