Permalink
Browse files

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.
  • Loading branch information...
densh committed Dec 2, 2013
1 parent 2695924 commit 0ccd4bcac64efe72d9758fbfb6a6dbce44cf308e
@@ -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)
@@ -125,18 +125,18 @@ 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])]
}
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,15 +145,15 @@ 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])]
}
val SyntacticPackageObjectDef: SyntacticPackageObjectDefExtractor
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,15 +189,15 @@ 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)]
}
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
@@ -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)
@@ -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"
@@ -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")
}
@@ -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",
"""
@@ -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
}

0 comments on commit 0ccd4bc

Please sign in to comment.