summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDen Shabalin <den.shabalin@gmail.com>2013-12-02 15:12:54 +0100
committerDen Shabalin <den.shabalin@gmail.com>2013-12-10 16:02:13 +0100
commit0ccd4bcac64efe72d9758fbfb6a6dbce44cf308e (patch)
treea3531dc11a14924c9c527a086215c2b2504b296d
parent26959249070cc0a00270e192885940dab050a693 (diff)
downloadscala-0ccd4bcac64efe72d9758fbfb6a6dbce44cf308e.tar.gz
scala-0ccd4bcac64efe72d9758fbfb6a6dbce44cf308e.tar.bz2
scala-0ccd4bcac64efe72d9758fbfb6a6dbce44cf308e.zip
SI-6842 Make splicing less sensitive to precise types of trees
Previously trees that represent parameters, case clauses and type variables had strictly defined ValDef, TypeDef and CaseDef types which caused problems in compositionality. Now this checks are moved to runtime so it's possible to pass a tree that is CaseDef but has Tree type.
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala11
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala32
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala79
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala2
-rw-r--r--test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala27
-rw-r--r--test/files/scalacheck/quasiquotes/ErrorProps.scala7
-rw-r--r--test/files/scalacheck/quasiquotes/TermConstructionProps.scala6
7 files changed, 110 insertions, 54 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index aa27f6030f..d5a310668e 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -123,7 +123,7 @@ trait Reifiers { self: Quasiquotes =>
case TuplePlaceholder(args) => reifyTuple(args)
case TupleTypePlaceholder(args) => reifyTupleType(args)
case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe)
- case CasePlaceholder(tree, location, _) => reifyCase(tree, location)
+ case CasePlaceholder(tree, _, _) => tree
case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree)
case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree)
case PackageStatPlaceholder(tree, _, _) => reifyPackageStat(tree)
@@ -175,6 +175,10 @@ trait Reifiers { self: Quasiquotes =>
reifyTree(other)
case Block(stats, last) =>
reifyBuildCall(nme.SyntacticBlock, stats :+ last)
+ case Try(block, catches, finalizer) =>
+ reifyBuildCall(nme.SyntacticTry, block, catches, finalizer)
+ case Match(selector, cases) =>
+ reifyBuildCall(nme.SyntacticMatch, selector, cases)
// parser emits trees with scala package symbol to ensure
// that some names hygienically point to various scala package
// members; we need to preserve this symbol to preserve
@@ -199,11 +203,6 @@ trait Reifiers { self: Quasiquotes =>
super.reifyName(name)
}
- def reifyCase(tree: Tree, location: Location) = {
- if (holesHaveTypes && !(location.tpe <:< caseDefType)) c.abort(tree.pos, s"$caseDefType expected but ${location.tpe} found")
- tree
- }
-
def reifyTuple(args: List[Tree]) = args match {
case Nil => reify(Literal(Constant(())))
case List(hole @ Placeholder(_, _, NoDot)) => reify(hole)
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index 673cf0d0b4..d2b9cd20fd 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -125,9 +125,9 @@ private[reflect] trait BuildUtils { self: Universe =>
val SyntacticClassDef: SyntacticClassDefExtractor
trait SyntacticClassDefExtractor {
- def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
- constrMods: Modifiers, vparamss: List[List[ValDef]], earlyDefs: List[Tree],
- parents: List[Tree], selfType: ValDef, body: List[Tree]): ClassDef
+ def apply(mods: Modifiers, name: TypeName, tparams: List[Tree],
+ constrMods: Modifiers, vparamss: List[List[Tree]], earlyDefs: List[Tree],
+ parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef
def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, List[List[ValDef]],
List[Tree], List[Tree], ValDef, List[Tree])]
}
@@ -135,8 +135,8 @@ private[reflect] trait BuildUtils { self: Universe =>
val SyntacticTraitDef: SyntacticTraitDefExtractor
trait SyntacticTraitDefExtractor {
- def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
- earlyDefs: List[Tree], parents: List[Tree], selfType: ValDef, body: List[Tree]): ClassDef
+ def apply(mods: Modifiers, name: TypeName, tparams: List[Tree],
+ earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef
def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef],
List[Tree], List[Tree], ValDef, List[Tree])]
}
@@ -145,7 +145,7 @@ private[reflect] trait BuildUtils { self: Universe =>
trait SyntacticObjectDefExtractor {
def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree],
- parents: List[Tree], selfType: ValDef, body: List[Tree]): Tree
+ parents: List[Tree], selfType: Tree, body: List[Tree]): Tree
def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])]
}
@@ -153,7 +153,7 @@ private[reflect] trait BuildUtils { self: Universe =>
trait SyntacticPackageObjectDefExtractor {
def apply(name: TermName, earlyDefs: List[Tree],
- parents: List[Tree], selfType: ValDef, body: List[Tree]): Tree
+ parents: List[Tree], selfType: Tree, body: List[Tree]): Tree
def unapply(tree: Tree): Option[(TermName, List[Tree], List[Tree], ValDef, List[Tree])]
}
@@ -175,7 +175,7 @@ private[reflect] trait BuildUtils { self: Universe =>
val SyntacticNew: SyntacticNewExtractor
trait SyntacticNewExtractor {
- def apply(earlyDefs: List[Tree], parents: List[Tree], selfType: ValDef, body: List[Tree]): Tree
+ def apply(earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): Tree
def unapply(tree: Tree): Option[(List[Tree], List[Tree], ValDef, List[Tree])]
}
@@ -189,7 +189,7 @@ private[reflect] trait BuildUtils { self: Universe =>
val SyntacticFunction: SyntacticFunctionExtractor
trait SyntacticFunctionExtractor {
- def apply(params: List[ValDef], body: Tree): Tree
+ def apply(params: List[Tree], body: Tree): Tree
def unapply(tree: Tree): Option[(List[ValDef], Tree)]
}
@@ -197,7 +197,7 @@ private[reflect] trait BuildUtils { self: Universe =>
val SyntacticDefDef: SyntacticDefDefExtractor
trait SyntacticDefDefExtractor {
- def apply(mods: Modifiers, name: TermName, tparams: List[Tree], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef
+ def apply(mods: Modifiers, name: TermName, tparams: List[Tree], vparamss: List[List[Tree]], tpt: Tree, rhs: Tree): DefDef
def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[List[ValDef]], Tree, Tree)]
}
@@ -246,6 +246,18 @@ private[reflect] trait BuildUtils { self: Universe =>
def unapply(tree: Tree): Option[(List[Tree], Tree)]
}
+ val SyntacticMatch: SyntacticMatchExtractor
+ trait SyntacticMatchExtractor {
+ def apply(selector: Tree, cases: List[Tree]): Match
+ def unapply(tree: Match): Option[(Tree, List[CaseDef])]
+ }
+
+ val SyntacticTry: SyntacticTryExtractor
+ trait SyntacticTryExtractor {
+ def apply(block: Tree, catches: List[Tree], finalizer: Tree): Try
+ def unapply(tree: Try): Option[(Tree, List[CaseDef], Tree)]
+ }
+
val SyntacticIdent: SyntacticIdentExtractor
trait SyntacticIdentExtractor {
def apply(name: Name, isBackquoted: Boolean = false): Ident
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index 22c10520b5..22e2bf4cad 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -71,12 +71,16 @@ trait BuildUtils { self: SymbolTable =>
def mkAnnotation(trees: List[Tree]): List[Tree] = trees.map(mkAnnotation)
- def mkVparamss(argss: List[List[ValDef]]): List[List[ValDef]] = argss.map(_.map(mkParam))
+ def mkVparamss(argss: List[List[Tree]]): List[List[ValDef]] = argss.map(_.map(mkParam))
- def mkParam(vd: ValDef): ValDef = {
- var newmods = (vd.mods | PARAM) & (~DEFERRED)
- if (vd.rhs.nonEmpty) newmods |= DEFAULTPARAM
- copyValDef(vd)(mods = newmods)
+ def mkParam(tree: Tree): ValDef = tree match {
+ case vd: ValDef =>
+ var newmods = (vd.mods | PARAM) & (~DEFERRED)
+ if (vd.rhs.nonEmpty) newmods |= DEFAULTPARAM
+ copyValDef(vd)(mods = newmods)
+ case _ =>
+ throw new IllegalArgumentException(s"$tree is not valid represenation of function parameter, " +
+ """consider reformatting it into q"val $name: $T = $default" shape""")
}
def mkTparams(tparams: List[Tree]): List[TypeDef] =
@@ -218,12 +222,25 @@ trait BuildUtils { self: SymbolTable =>
}
}
+ protected def mkSelfType(tree: Tree) = tree match {
+ case vd: ValDef =>
+ require(vd.rhs.isEmpty, "self types must have empty right hand side")
+ copyValDef(vd)(mods = (vd.mods | PRIVATE) & (~DEFERRED))
+ case _ =>
+ throw new IllegalArgumentException(s"$tree is not a valid representation of self type, " +
+ """consider reformatting into q"val $self: $T" shape""")
+ }
+
object SyntacticClassDef extends SyntacticClassDefExtractor {
- def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
- constrMods: Modifiers, vparamss: List[List[ValDef]], earlyDefs: List[Tree],
- parents: List[Tree], selfType: ValDef, body: List[Tree]): ClassDef = {
+ def apply(mods: Modifiers, name: TypeName, tparams: List[Tree],
+ constrMods: Modifiers, vparamss: List[List[Tree]], earlyDefs: List[Tree],
+ parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = {
val extraFlags = PARAMACCESSOR | (if (mods.isCase) CASEACCESSOR else 0L)
- val vparamss0 = vparamss.map { _.map { vd => copyValDef(vd)(mods = (vd.mods | extraFlags) & (~DEFERRED)) } }
+ val vparamss0 = vparamss.map { _.map {
+ case vd: ValDef => copyValDef(vd)(mods = (vd.mods | extraFlags) & (~DEFERRED))
+ case tree => throw new IllegalArgumentException(s"$tree is not valid representation of class parameter, " +
+ """consider reformatting it into q"val $name: $T = $default" shape""")
+ } }
val tparams0 = mkTparams(tparams)
val parents0 = gen.mkParents(mods,
if (mods.isCase) parents.filter {
@@ -232,7 +249,8 @@ trait BuildUtils { self: SymbolTable =>
} else parents
)
val body0 = earlyDefs ::: body
- val templ = gen.mkTemplate(parents0, selfType, constrMods, vparamss0, body0)
+ val selfType0 = mkSelfType(selfType)
+ val templ = gen.mkTemplate(parents0, selfType0, constrMods, vparamss0, body0)
gen.mkClassDef(mods, name, tparams0, templ)
}
@@ -247,10 +265,10 @@ trait BuildUtils { self: SymbolTable =>
}
object SyntacticTraitDef extends SyntacticTraitDefExtractor {
- def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], earlyDefs: List[Tree],
- parents: List[Tree], selfType: ValDef, body: List[Tree]): ClassDef = {
+ def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], earlyDefs: List[Tree],
+ parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = {
val mods0 = mods | TRAIT | ABSTRACT
- val templ = gen.mkTemplate(parents, selfType, Modifiers(TRAIT), Nil, earlyDefs ::: body)
+ val templ = gen.mkTemplate(parents, mkSelfType(selfType), Modifiers(TRAIT), Nil, earlyDefs ::: body)
gen.mkClassDef(mods0, name, mkTparams(tparams), templ)
}
@@ -265,8 +283,8 @@ trait BuildUtils { self: SymbolTable =>
object SyntacticObjectDef extends SyntacticObjectDefExtractor {
def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree],
- parents: List[Tree], selfType: ValDef, body: List[Tree]) =
- ModuleDef(mods, name, gen.mkTemplate(parents, selfType, NoMods, Nil, earlyDefs ::: body))
+ parents: List[Tree], selfType: Tree, body: List[Tree]) =
+ ModuleDef(mods, name, gen.mkTemplate(parents, mkSelfType(selfType), NoMods, Nil, earlyDefs ::: body))
def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
case ModuleDef(mods, name, UnMkTemplate(parents, selfType, _, _, earlyDefs, body)) =>
@@ -278,7 +296,7 @@ trait BuildUtils { self: SymbolTable =>
object SyntacticPackageObjectDef extends SyntacticPackageObjectDefExtractor {
def apply(name: TermName, earlyDefs: List[Tree],
- parents: List[Tree], selfType: ValDef, body: List[Tree]): Tree =
+ parents: List[Tree], selfType: Tree, body: List[Tree]): Tree =
gen.mkPackageObject(SyntacticObjectDef(NoMods, name, earlyDefs, parents, selfType, body))
def unapply(tree: Tree): Option[(TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
@@ -368,11 +386,9 @@ trait BuildUtils { self: SymbolTable =>
}
object SyntacticFunction extends SyntacticFunctionExtractor {
- def apply(params: List[ValDef], body: Tree): Tree = {
- val params0 = params.map { arg =>
- require(arg.rhs.isEmpty, "anonymous functions don't support default values")
- mkParam(arg)
- }
+ def apply(params: List[Tree], body: Tree): Tree = {
+ val params0 :: Nil = mkVparamss(params :: Nil)
+ require(params0.forall { _.rhs.isEmpty }, "anonymous functions don't support default values")
Function(params0, body)
}
@@ -383,8 +399,8 @@ trait BuildUtils { self: SymbolTable =>
}
object SyntacticNew extends SyntacticNewExtractor {
- def apply(earlyDefs: List[Tree], parents: List[Tree], selfType: ValDef, body: List[Tree]): Tree =
- gen.mkNew(parents, selfType, earlyDefs ::: body, NoPosition, NoPosition)
+ def apply(earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): Tree =
+ gen.mkNew(parents, mkSelfType(selfType), earlyDefs ::: body, NoPosition, NoPosition)
def unapply(tree: Tree): Option[(List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
case SyntacticApplied(Select(New(SyntacticTypeApplied(ident, targs)), nme.CONSTRUCTOR), argss) =>
@@ -398,7 +414,7 @@ trait BuildUtils { self: SymbolTable =>
}
object SyntacticDefDef extends SyntacticDefDefExtractor {
- def apply(mods: Modifiers, name: TermName, tparams: List[Tree], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef =
+ def apply(mods: Modifiers, name: TermName, tparams: List[Tree], vparamss: List[List[Tree]], tpt: Tree, rhs: Tree): DefDef =
DefDef(mods, name, mkTparams(tparams), mkVparamss(vparamss), tpt, rhs)
def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[List[ValDef]], Tree, Tree)] = tree match {
@@ -664,6 +680,21 @@ trait BuildUtils { self: SymbolTable =>
}
}
+ protected def mkCases(cases: List[Tree]): List[CaseDef] = cases.map {
+ case c: CaseDef => c
+ case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case")
+ }
+
+ object SyntacticMatch extends SyntacticMatchExtractor {
+ def apply(selector: Tree, cases: List[Tree]) = Match(selector, mkCases(cases))
+ def unapply(tree: Match): Option[(Tree, List[CaseDef])] = Match.unapply(tree)
+ }
+
+ object SyntacticTry extends SyntacticTryExtractor {
+ def apply(block: Tree, catches: List[Tree], finalizer: Tree) = Try(block, mkCases(catches), finalizer)
+ def unapply(tree: Try): Option[(Tree, List[CaseDef], Tree)] = Try.unapply(tree)
+ }
+
object SyntacticIdent extends SyntacticIdentExtractor {
def apply(name: Name, isBackquoted: Boolean) = {
val id = self.Ident(name)
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index f6c612f4eb..6cc0199458 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -598,10 +598,12 @@ trait StdNames {
val SyntacticFunction: NameType = "SyntacticFunction"
val SyntacticFunctionType: NameType = "SyntacticFunctionType"
val SyntacticIdent: NameType = "SyntacticIdent"
+ val SyntacticMatch: NameType = "SyntacticMatch"
val SyntacticNew: NameType = "SyntacticNew"
val SyntacticObjectDef: NameType = "SyntacticObjectDef"
val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef"
val SyntacticTraitDef: NameType = "SyntacticTraitDef"
+ val SyntacticTry: NameType = "SyntacticTry"
val SyntacticTuple: NameType = "SyntacticTuple"
val SyntacticTupleType: NameType = "SyntacticTupleType"
val SyntacticTypeApplied: NameType = "SyntacticTypeApplied"
diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
index 2ec679e78b..2af656c7c9 100644
--- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
@@ -2,12 +2,25 @@ import org.scalacheck._, Prop._, Gen._, Arbitrary._
import scala.reflect.runtime.universe._, Flag._, build.ScalaDot
object DefinitionConstructionProps
- extends QuasiquoteProperties("definition construction")
- with ClassConstruction
- with TraitConstruction
- with TypeDefConstruction
- with ValDefConstruction
- with PackageConstruction
+ extends QuasiquoteProperties("definition construction")
+ with ClassConstruction
+ with TraitConstruction
+ with TypeDefConstruction
+ with ValDefConstruction
+ with PackageConstruction {
+ property("SI-6842") = test {
+ val x: Tree = q"val x: Int"
+ assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0")
+ assertEqAst(q"class C($x)", "class C(val x: Int)")
+ assertEqAst(q"class C { $x => }", "class C { x: Int => }")
+ assertEqAst(q"trait B { $x => }", "trait B { x: Int => }")
+ assertEqAst(q"object A { $x => }", "object A { x: Int => }")
+ val t: Tree = q"type T"
+ assertEqAst(q"def f[$t] = 0", "def f[T] = 0")
+ assertEqAst(q"class C[$t]", "class C[T]")
+ assertEqAst(q"trait B[$t]", "trait B[T]")
+ }
+}
trait ClassConstruction { self: QuasiquoteProperties =>
val anyRef = ScalaDot(TypeName("AnyRef"))
@@ -283,7 +296,7 @@ trait MethodConstruction { self: QuasiquoteProperties =>
assertEqAst(q"@$a def foo", "@Foo[A,B] def foo")
}
- property("splice annotation with multiple argument lists") = test{
+ property("splice annotation with multiple argument lists") = test {
val a = q"new Foo(a)(b)"
assertEqAst(q"@$a def foo", "@Foo(a)(b) def foo")
}
diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala
index cb46a60dbe..adad48fcdf 100644
--- a/test/files/scalacheck/quasiquotes/ErrorProps.scala
+++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala
@@ -105,13 +105,6 @@ object ErrorProps extends QuasiquoteProperties("errors") {
q"f($sb)"
""")
- property("casedef expected") = fails(
- "reflect.runtime.universe.CaseDef expected but reflect.runtime.universe.Tree found",
- """
- val t = EmptyTree
- q"_ { case $t }"
- """)
-
property("can't splice with ... card here") = fails(
"Can't splice with ... here",
"""
diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
index ded8fd9c16..338c379fc3 100644
--- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
@@ -210,6 +210,12 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
assertEqAst(q"{..$xs}", "{1; 2}")
}
+ property("SI-6842") = test {
+ val cases: List[Tree] = cq"a => b" :: cq"_ => c" :: Nil
+ assertEqAst(q"1 match { case ..$cases }", "1 match { case a => b case _ => c }")
+ assertEqAst(q"try 1 catch { case ..$cases }", "try 1 catch { case a => b case _ => c }")
+ }
+
property("SI-8009") = test {
q"`foo`".asInstanceOf[reflect.internal.SymbolTable#Ident].isBackquoted
}