summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDen Shabalin <den.shabalin@gmail.com>2013-09-02 16:01:38 +0200
committerDen Shabalin <den.shabalin@gmail.com>2013-09-05 20:42:09 +0200
commit1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f (patch)
treec66d11c9b21a29a7601fedb4d8ad163dad0fa5cf
parent73a4f172c3b4f2c7d8bf9936898f031d09899cef (diff)
downloadscala-1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f.tar.gz
scala-1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f.tar.bz2
scala-1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f.zip
first-class early def splicing and extraction support
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala48
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala7
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala7
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala32
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala29
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala150
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala4
-rw-r--r--src/reflect/scala/reflect/internal/TreeGen.scala14
-rw-r--r--test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala64
-rw-r--r--test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala45
10 files changed, 334 insertions, 66 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 158fa147b6..f52ed60480 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -2704,8 +2704,7 @@ self =>
syntaxError("classes are not allowed to be virtual", skipIt = false)
}
val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart)
- if (isInterface(mods1, template.body)) mods1 |= Flags.INTERFACE
- val result = ClassDef(mods1, name, tparams, template)
+ val result = gen.mkClassDef(mods1, name, tparams, template)
// Context bounds generate implicit parameters (part of the template) with types
// from tparams: we need to ensure these don't overlap
if (!classContextBounds.isEmpty)
@@ -2796,17 +2795,7 @@ self =>
// @S: pre template body cannot stub like post body can!
val (self, body) = templateBody(isPre = true)
if (in.token == WITH && (self eq emptyValDef)) {
- val earlyDefs: List[Tree] = body flatMap {
- case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred =>
- List(copyValDef(vdef)(mods = mods | Flags.PRESUPER))
- case tdef @ TypeDef(mods, name, tparams, rhs) =>
- deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.")
- List(treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs))
- case stat if !stat.isEmpty =>
- syntaxError(stat.pos, "only concrete field definitions allowed in early object initialization section", skipIt = false)
- List()
- case _ => List()
- }
+ val earlyDefs: List[Tree] = body.map(ensureEarlyDef).filter(_.nonEmpty)
in.nextToken()
val parents = templateParents()
val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false)
@@ -2821,8 +2810,18 @@ self =>
}
}
- def isInterface(mods: Modifiers, body: List[Tree]): Boolean =
- mods.isTrait && (body forall treeInfo.isInterfaceMember)
+ def ensureEarlyDef(tree: Tree): Tree = tree match {
+ case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred =>
+ copyValDef(vdef)(mods = mods | Flags.PRESUPER)
+ case tdef @ TypeDef(mods, name, tparams, rhs) =>
+ deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.")
+ treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs)
+ case stat if !stat.isEmpty =>
+ syntaxError(stat.pos, "only concrete field definitions allowed in early object initialization section", skipIt = false)
+ EmptyTree
+ case _ =>
+ EmptyTree
+ }
/** {{{
* ClassTemplateOpt ::= `extends' ClassTemplate | [[`extends'] TemplateBody]
@@ -2831,7 +2830,7 @@ self =>
* }}}
*/
def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = {
- val (parents0, self, body) = (
+ val (parents, self, body) = (
if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) {
in.nextToken()
template()
@@ -2842,26 +2841,21 @@ self =>
(List(), self, body)
}
)
- def anyrefParents() = {
- val caseParents = if (mods.isCase) List(productConstr, serializableConstr) else Nil
- parents0 ::: caseParents match {
- case Nil => atInPos(scalaAnyRefConstr) :: Nil
- case ps => ps
- }
- }
def anyvalConstructor() = (
// Not a well-formed constructor, has to be finished later - see note
// regarding AnyVal constructor in AddInterfaces.
DefDef(NoMods, nme.CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(Nil, literalUnit))
)
- val tstart0 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart
+ val parentPos = o2p(in.offset)
+ val tstart1 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart
- atPos(tstart0) {
+ atPos(tstart1) {
// Exclude only the 9 primitives plus AnyVal.
if (inScalaRootPackage && ScalaValueClassNames.contains(name))
- Template(parents0, self, anyvalConstructor :: body)
+ Template(parents, self, anyvalConstructor :: body)
else
- gen.mkTemplate(anyrefParents(), self, constrMods, vparamss, body, o2p(tstart))
+ gen.mkTemplate(gen.mkParents(mods, parents, parentPos),
+ self, constrMods, vparamss, body, o2p(tstart))
}
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index a8ec46261f..868c74ebcd 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -116,7 +116,7 @@ trait Parsers { self: Quasiquotes =>
case AT =>
in.nextToken()
annot :: readAnnots(annot)
- case _ if isHole && lookingAhead { in.token == AT || isModifier || isDefIntro || isIdent} =>
+ case _ if isHole && lookingAhead { isAnnotation || isModifier || isDefIntro || isIdent || isStatSep || in.token == LPAREN } =>
val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString))))
in.nextToken()
ann :: readAnnots(annot)
@@ -130,6 +130,11 @@ trait Parsers { self: Quasiquotes =>
in.nextToken()
result
} else super.refineStat()
+
+ override def ensureEarlyDef(tree: Tree) = tree match {
+ case Ident(name: TermName) if isHole(name) => ValDef(NoMods | Flag.PRESUPER, name, Ident(tpnme.QUASIQUOTE_EARLY_DEF), EmptyTree)
+ case _ => super.ensureEarlyDef(tree)
+ }
}
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index f0886b5735..c136e6d785 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -140,4 +140,11 @@ trait Placeholders { self: Quasiquotes =>
case _ => None
}
}
+
+ object EarlyDefPlaceholder {
+ def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
+ case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some((tree, location, card))
+ case _ => None
+ }
+ }
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index 9789801fac..53b2e4cc96 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -7,7 +7,8 @@ import scala.reflect.internal.Flags._
trait Reifiers { self: Quasiquotes =>
import global._
- import global.build.{SyntacticClassDef, SyntacticBlock, SyntacticApplied, SyntacticTypeApplied}
+ import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef,
+ SyntacticBlock, SyntacticApplied, SyntacticTypeApplied}
import global.treeInfo._
import global.definitions._
import Cardinality._
@@ -48,13 +49,18 @@ trait Reifiers { self: Quasiquotes =>
case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe)
case CasePlaceholder(tree, location, _) => reifyCase(tree, location)
case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree)
+ case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree)
case _ => EmptyTree
}
override def reifyTreeSyntactically(tree: Tree) = tree match {
- case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, parents, selfdef, body) =>
+ case SyntacticTraitDef(mods, name, tparams, earlyDefs, parents, selfdef, body) =>
+ reifyBuildCall(nme.SyntacticTraitDef, mods, name, tparams, earlyDefs, parents, selfdef, body)
+ case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, earlyDefs, parents, selfdef, body) =>
reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, vparamss,
- parents, selfdef, body)
+ earlyDefs, parents, selfdef, body)
+ case SyntacticModuleDef(mods, name, earlyDefs, parents, selfdef, body) =>
+ reifyBuildCall(nme.SyntacticModuleDef, mods, name, earlyDefs, parents, selfdef, body)
case SyntacticApplied(fun, argss) if argss.length > 1 =>
reifyBuildCall(nme.SyntacticApplied, fun, argss)
case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) =>
@@ -110,6 +116,8 @@ trait Reifiers { self: Quasiquotes =>
def reifyRefineStat(tree: Tree) = tree
+ def reifyEarlyDef(tree: Tree) = tree
+
/** Splits list into a list of groups where subsequent elements are considered
* similar by the corresponding function.
*
@@ -163,6 +171,7 @@ trait Reifiers { self: Quasiquotes =>
case Placeholder(tree, _, DotDot) => tree
case CasePlaceholder(tree, _, DotDot) => tree
case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree)
+ case EarlyDefPlaceholder(tree, _, DotDot) => reifyEarlyDef(tree)
case List(Placeholder(tree, _, DotDotDot)) => tree
} {
reify(_)
@@ -170,8 +179,19 @@ trait Reifiers { self: Quasiquotes =>
def reifyAnnotList(annots: List[Tree]): Tree
- def ensureNoExplicitFlags(m: Modifiers, pos: Position) =
- if ((m.flags & ExplicitFlags) != 0L) c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers")
+ // These are explicit flags except those that are used
+ // to overload the same tree for two different concepts:
+ // - MUTABLE that is used to override ValDef for vars
+ // - TRAIT that is used to override ClassDef for traits
+ val nonoverloadedExplicitFlags = ExplicitFlags & ~MUTABLE & ~TRAIT
+
+ def ensureNoExplicitFlags(m: Modifiers, pos: Position) = {
+ // Traits automatically have ABSTRACT flag assigned to
+ // them so in that case it's not an explicit flag
+ val flags = if (m.isTrait) m.flags & ~ABSTRACT else m.flags
+ if ((flags & nonoverloadedExplicitFlags) != 0L)
+ c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers")
+ }
override def mirrorSelect(name: String): Tree =
Select(universe, TermName(name))
@@ -249,6 +269,8 @@ trait Reifiers { self: Quasiquotes =>
}
override def reifyRefineStat(tree: Tree) = mirrorBuildCall(nme.mkRefineStat, tree)
+
+ override def reifyEarlyDef(tree: Tree) = mirrorBuildCall(nme.mkEarlyDef, tree)
}
class UnapplyReifier extends Reifier {
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index a74d3c137d..108efee37c 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -78,6 +78,10 @@ private[reflect] trait BuildUtils { self: Universe =>
def mkRefineStat(stats: List[Tree]): List[Tree]
+ def mkEarlyDef(defn: Tree): Tree
+
+ def mkEarlyDef(defns: List[Tree]): List[Tree]
+
def RefTree(qual: Tree, sym: Symbol): Tree
val ScalaDot: ScalaDotExtractor
@@ -112,10 +116,27 @@ private[reflect] trait BuildUtils { self: Universe =>
trait SyntacticClassDefExtractor {
def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
- constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree],
- selfdef: ValDef, body: List[Tree]): Tree
- def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers,
- List[List[ValDef]], List[Tree], ValDef, List[Tree])]
+ constrMods: Modifiers, vparamss: List[List[ValDef]], earlyDefs: List[Tree],
+ parents: List[Tree], selfdef: ValDef, 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], selfdef: ValDef, body: List[Tree]): ClassDef
+ def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef],
+ List[Tree], List[Tree], ValDef, List[Tree])]
+ }
+
+ val SyntacticModuleDef: SyntacticModuleDefExtractor
+
+ trait SyntacticModuleDefExtractor {
+ def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree],
+ parents: List[Tree], selfdef: ValDef, body: List[Tree]): Tree
+ def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])]
}
val TupleN: TupleNExtractor
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index e2894c5b94..528b542361 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -70,6 +70,19 @@ trait BuildUtils { self: SymbolTable =>
case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument")
}
+ def mkVparamss(argss: List[List[ValDef]]): List[List[ValDef]] = {
+ argss.map { _.map {
+ case vd @ ValDef(mods, _, _, EmptyTree) => copyValDef(vd)(mods = mods | PARAM)
+ case vd @ ValDef(mods, _, _, _) => copyValDef(vd)(mods = mods | PARAM | DEFAULTPARAM)
+ } }
+ }
+
+ def mkTparams(tparams: List[Tree]): List[TypeDef] =
+ tparams.map {
+ case td: TypeDef => copyTypeDef(td)(mods = (td.mods | PARAM) & (~DEFERRED))
+ case other => throw new IllegalArgumentException("can't splice $other as type parameter")
+ }
+
def mkRefineStat(stat: Tree): Tree = {
stat match {
case dd: DefDef => require(dd.rhs.isEmpty, "can't use DefDef with non-empty body as refine stat")
@@ -90,6 +103,17 @@ trait BuildUtils { self: SymbolTable =>
}
}
+ def mkEarlyDef(defn: Tree): Tree = defn match {
+ case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred =>
+ copyValDef(vdef)(mods = mods | PRESUPER)
+ case tdef @ TypeDef(mods, _, _, _) =>
+ copyTypeDef(tdef)(mods = mods | PRESUPER)
+ case _ =>
+ throw new IllegalArgumentException(s"not legal early def: $defn")
+ }
+
+ def mkEarlyDef(defns: List[Tree]): List[Tree] = defns.map(mkEarlyDef)
+
def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym
object FlagsRepr extends FlagsReprExtractor {
@@ -121,36 +145,110 @@ trait BuildUtils { self: SymbolTable =>
}
}
+ private object UnCtor {
+ def unapply(tree: Tree): Option[(Modifiers, List[List[ValDef]], List[Tree])] = tree match {
+ case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, Block(lvdefs, _)) =>
+ Some(mods | Flag.TRAIT, Nil, lvdefs)
+ case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, Block(lvdefs :+ _, _)) =>
+ Some(mods, vparamss, lvdefs)
+ case _ => None
+ }
+ }
+
+ private object UnMkTemplate {
+ def unapply(templ: Template): Option[(List[Tree], ValDef, Modifiers, List[List[ValDef]], List[Tree], List[Tree])] = {
+ val Template(parents, selfdef, tbody) = templ
+ def result(ctorMods: Modifiers, vparamss: List[List[ValDef]], edefs: List[Tree], body: List[Tree]) =
+ Some((parents, selfdef, ctorMods, vparamss, edefs, body))
+ def indexOfCtor(trees: List[Tree]) =
+ trees.indexWhere { case UnCtor(_, _, _) => true ; case _ => false }
+
+ if (tbody forall treeInfo.isInterfaceMember)
+ result(NoMods | Flag.TRAIT, Nil, Nil, tbody)
+ else if (indexOfCtor(tbody) == -1)
+ None
+ else {
+ val (rawEdefs, rest) = tbody.span(treeInfo.isEarlyDef)
+ val (gvdefs, etdefs) = rawEdefs.partition(treeInfo.isEarlyValDef)
+ val (fieldDefs, UnCtor(ctorMods, ctorVparamss, lvdefs) :: body) = rest.splitAt(indexOfCtor(rest))
+ val evdefs = gvdefs.zip(lvdefs).map {
+ case (gvdef @ ValDef(_, _, tpt: TypeTree, _), ValDef(_, _, _, rhs)) =>
+ copyValDef(gvdef)(tpt = tpt.original, rhs = rhs)
+ }
+ val edefs = evdefs ::: etdefs
+ if (ctorMods.isTrait)
+ result(ctorMods, Nil, edefs, body)
+ else {
+ // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section
+ val vparamssRestoredImplicits = ctorVparamss match {
+ case Nil :: (tail @ ((head :: _) :: _)) if head.mods.isImplicit => tail
+ case other => other
+ }
+ // undo flag modifications by mergeing flag info from constructor args and fieldDefs
+ val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap
+ val vparamss = mmap(vparamssRestoredImplicits) { vd =>
+ val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM)
+ atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs))
+ }
+ result(ctorMods, vparamss, edefs, body)
+ }
+ }
+ }
+ }
+
object SyntacticClassDef extends SyntacticClassDefExtractor {
def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
- constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree],
- selfdef: ValDef, body: List[Tree]): Tree =
- ClassDef(mods, name, tparams, gen.mkTemplate(parents, selfdef, constrMods, vparamss, body, NoPosition))
-
- def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers,
- List[List[ValDef]], List[Tree], ValDef, List[Tree])] = tree match {
- case ClassDef(mods, name, tparams, Template(parents, selfdef, tbody)) =>
- // extract generated fieldDefs and constructor
- val (defs, (ctor: DefDef) :: body) = tbody.splitAt(tbody.indexWhere {
- case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => true
- case _ => false
- })
- val (earlyDefs, fieldDefs) = defs.span(treeInfo.isEarlyDef)
-
- // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section
- val vparamssRestoredImplicits = ctor.vparamss match {
- case Nil :: rest if !rest.isEmpty && !rest.head.isEmpty && rest.head.head.mods.isImplicit => rest
- case other => other
- }
+ constrMods: Modifiers, vparamss: List[List[ValDef]], earlyDefs: List[Tree],
+ parents: List[Tree], selfdef: ValDef, 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 tparams0 = mkTparams(tparams)
+ val parents0 = gen.mkParents(mods,
+ if (mods.isCase) parents.filter {
+ case ScalaDot(tpnme.Product | tpnme.Serializable | tpnme.AnyRef) => false
+ case _ => true
+ } else parents
+ )
+ val body0 = earlyDefs ::: body
+ val templ = gen.mkTemplate(parents0, selfdef, constrMods, vparamss0, body0)
+ gen.mkClassDef(mods, name, tparams0, templ)
+ }
- // undo flag modifications by mergeing flag info from constructor args and fieldDefs
- val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap
- val vparamss = mmap(vparamssRestoredImplicits) { vd =>
- val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM)
- atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs))
- }
+ def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, List[List[ValDef]],
+ List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
+ case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfdef, ctorMods, vparamss, earlyDefs, body))
+ if !ctorMods.isTrait && !ctorMods.hasFlag(JAVA) =>
+ Some((mods, name, tparams, ctorMods, vparamss, earlyDefs, parents, selfdef, body))
+ case _ =>
+ None
+ }
+ }
+
+ object SyntacticTraitDef extends SyntacticTraitDefExtractor {
+ def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], earlyDefs: List[Tree],
+ parents: List[Tree], selfdef: ValDef, body: List[Tree]): ClassDef = {
+ val mods0 = mods | TRAIT | ABSTRACT
+ val templ = gen.mkTemplate(parents, selfdef, Modifiers(TRAIT), Nil, earlyDefs ::: body)
+ gen.mkClassDef(mods0, name, mkTparams(tparams), templ)
+ }
+
+ def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef],
+ List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
+ case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfdef, ctorMods, vparamss, earlyDefs, body))
+ if mods.isTrait =>
+ Some((mods, name, tparams, earlyDefs, parents, selfdef, body))
+ case _ => None
+ }
+ }
+
+ object SyntacticModuleDef extends SyntacticModuleDefExtractor {
+ def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree],
+ parents: List[Tree], selfdef: ValDef, body: List[Tree]) =
+ ModuleDef(mods, name, gen.mkTemplate(parents, selfdef, NoMods, Nil, earlyDefs ::: body))
- Some((mods, name, tparams, ctor.mods, vparamss, parents, selfdef, earlyDefs ::: body))
+ def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
+ case ModuleDef(mods, name, UnMkTemplate(parents, selfdef, _, _, earlyDefs, body)) =>
+ Some((mods, name, earlyDefs, parents, selfdef, body))
case _ =>
None
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index f4662296e2..936445f011 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -251,6 +251,7 @@ trait StdNames {
final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
final val QUASIQUOTE_FUNCTION: NameType = "$quasiquote$function$"
final val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$"
+ final val QUASIQUOTE_EARLY_DEF: NameType = "$quasiquote$early$def$"
// Annotation simple names, used in Namer
final val BeanPropertyAnnot: NameType = "BeanProperty"
@@ -592,6 +593,8 @@ trait StdNames {
val SyntacticBlock: NameType = "SyntacticBlock"
val SyntacticClassDef: NameType = "SyntacticClassDef"
val SyntacticFunctionType: NameType= "SyntacticFunctionType"
+ val SyntacticModuleDef: NameType = "SyntacticModuleDef"
+ val SyntacticTraitDef: NameType = "SyntacticTraitDef"
val SyntacticTypeApplied: NameType = "SyntacticTypeApplied"
val This: NameType = "This"
val ThisType: NameType = "ThisType"
@@ -687,6 +690,7 @@ trait StdNames {
val moduleClass : NameType = "moduleClass"
val mkAnnotation: NameType = "mkAnnotation"
val mkRefineStat: NameType = "mkRefineStat"
+ val mkEarlyDef: NameType = "mkEarlyDef"
val ne: NameType = "ne"
val newArray: NameType = "newArray"
val newFreeTerm: NameType = "newFreeTerm"
diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala
index 648c921eb7..66345b7d0b 100644
--- a/src/reflect/scala/reflect/internal/TreeGen.scala
+++ b/src/reflect/scala/reflect/internal/TreeGen.scala
@@ -14,7 +14,8 @@ abstract class TreeGen extends macros.TreeBuilder {
def rootScalaDot(name: Name) = Select(rootId(nme.scala_) setSymbol ScalaPackage, name)
def scalaDot(name: Name) = Select(Ident(nme.scala_) setSymbol ScalaPackage, name)
def scalaAnnotationDot(name: Name) = Select(scalaDot(nme.annotation), name)
- def scalaAnyRefConstr = scalaDot(tpnme.AnyRef) setSymbol AnyRefClass // used in ide
+ def scalaAnyRefConstrRaw = scalaDot(tpnme.AnyRef)
+ def scalaAnyRefConstr = scalaAnyRefConstrRaw setSymbol AnyRefClass // used in ide
def scalaFunctionConstr(argtpes: List[Tree], restpe: Tree, abstractFun: Boolean = false): Tree = {
val cls = if (abstractFun)
@@ -380,6 +381,17 @@ abstract class TreeGen extends macros.TreeBuilder {
global.Template(parents, self, gvdefs ::: fieldDefs ::: constr ++: etdefs ::: rest)
}
+ def mkParents(ownerMods: Modifiers, parents: List[Tree], parentPos: Position = NoPosition) =
+ if (ownerMods.isCase) parents ::: List(scalaDot(tpnme.Product), scalaDot(tpnme.Serializable))
+ else if (parents.isEmpty) atPos(parentPos)(scalaAnyRefConstrRaw) :: Nil
+ else parents
+
+ def mkClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], templ: Template): ClassDef = {
+ val isInterface = mods.isTrait && (templ.body forall treeInfo.isInterfaceMember)
+ val mods1 = if (isInterface) (mods | Flags.INTERFACE) else mods
+ ClassDef(mods1, name, tparams, templ)
+ }
+
/** Create positioned tree representing an object creation <new parents { stats }
* @param npos the position of the new
* @param cpos the position of the anonymous class starting with parents
diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
index 707394fdba..96105b9581 100644
--- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
@@ -24,6 +24,21 @@ trait ClassConstruction { self: QuasiquoteProperties =>
Modifiers(), name, List(),
Template(parents, emptyValDef, emtpyConstructor :: body))
+ property("construct case class") = test {
+ val params = q"val x: Int" :: q"val y: Int" :: Nil
+ val name = TypeName("Point")
+ assertEqAst(q"$CASE class $name(..$params)", "case class Point(x: Int, y: Int)")
+ }
+
+ property("case class bare param") = test {
+ assertEqAst(q"$CASE class Point(x: Int, y: Int)", "case class Point(private[this] val x: Int, private[this] val y: Int)")
+ }
+
+ property("generate default constructors automatically") = test {
+ val parents = List.empty[Tree]
+ assertEqAst(q"class Foo extends ..$parents", "class Foo")
+ }
+
property("splice term name into class") = forAll { (name: TypeName) =>
eqAst(q"class $name", "class " + name.toString)
}
@@ -42,10 +57,59 @@ trait ClassConstruction { self: QuasiquoteProperties =>
property("splice type name into class parents") = forAll { (name: TypeName, parent: TypeName) =>
q"class $name extends $parent" ≈ classWith(name, parents = List(Ident(parent)))
}
+
+ property("param flags are consistent with raw code") = test {
+ val pubx = q"val x: Int"
+ val privx = q"private[this] val x: Int"
+ assertEqAst(q" class C(x: Int)", " class C(x: Int) ")
+ assertEqAst(q"case class C(x: Int)", "case class C(x: Int) ")
+ assertEqAst(q" class C($pubx) ", " class C(val x: Int) ")
+ assertEqAst(q"case class C($pubx) ", "case class C(x: Int) ")
+ assertEqAst(q" class C($privx)", " class C(x: Int) ")
+ assertEqAst(q"case class C($privx)", "case class C(private[this] val x: Int)")
+ }
}
trait TraitConstruction { self: QuasiquoteProperties =>
+ property("splice name into trait def") = test {
+ val Foo = TypeName("Foo")
+ assert(q"trait $Foo" ≈ q"trait Foo")
+ }
+
+ property("splice type params into trait def") = test {
+ val tparams = q"type A" :: q"type B" :: Nil
+ assert(q"trait Foo[..$tparams]" ≈ q"trait Foo[A, B]")
+ }
+ property("splice defs into trait body") = test {
+ val body = q"def foo" :: q"val bar: Baz" :: Nil
+ assert(q"trait Foo { ..$body }" ≈ q"trait Foo { def foo; val bar: Baz }")
+ }
+
+ property("splice parents into trait") = test {
+ val parents = tq"A" :: tq"B" :: Nil
+ assert(q"trait Foo extends ..$parents" ≈ q"trait Foo extends A with B")
+ }
+
+ property("splice early valdef into trait") = test {
+ val x = q"val x: Int = 1"
+ assertEqAst(q"trait T extends { $x } with Any", "trait T extends { val x: Int = 1} with Any")
+ }
+
+ property("construct trait with early valdef") = test {
+ assertEqAst(q"trait T extends { val x: Int = 1 } with Any", "trait T extends { val x: Int = 1 } with Any")
+ }
+
+ property("splice defs into early block") = test {
+ val defs = q"val x: Int = 0" :: q"type Foo = Bar" :: Nil
+ assert(q"trait T extends { ..$defs } with Bippy" ≈
+ q"trait T extends { val x: Int = 0; type Foo = Bar} with Bippy")
+ }
+
+ property("fail on splicing of non-valid early tree") = test {
+ val defn = q"def x: Int = 0"
+ assertThrows[IllegalArgumentException] { q"trait T extends { $defn } with Bar" }
+ }
}
trait TypeDefConstruction { self: QuasiquoteProperties =>
diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala
index 31d230d6fd..d6b60c4351 100644
--- a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala
@@ -15,11 +15,32 @@ object DefinitionDeconstructionProps
with ValVarDeconstruction
trait TraitDeconstruction { self: QuasiquoteProperties =>
-
+ property("exhaustive trait matcher") = test {
+ def matches(line: String) {
+ val q"""$mods trait $name[..$targs]
+ extends { ..$early } with ..$parents { $self => ..$body }""" = parse(line)
+ }
+ matches("trait Foo")
+ matches("trait Foo[T]")
+ matches("trait Foo { def bar }")
+ matches("trait Foo extends Bar with Baz")
+ matches("trait Foo { self: Bippy => val x: Int = 1}")
+ matches("trait Foo extends { val early: Int = 1 } with Bar { val late = early }")
+ matches("private[Gap] trait Foo")
+ }
}
trait ObjectDeconstruction { self: QuasiquoteProperties =>
-
+ property("exhaustive object matcher") = test {
+ def matches(line: String) = {
+ val q"""$mods object $name extends { ..$early } with ..$parents { $self => ..$body }""" = parse(line)
+ }
+ matches("object Foo")
+ matches("object Foo extends Bar[T]")
+ matches("object Foo extends { val early: T = v } with Bar")
+ matches("object Foo extends Foo { selfy => body }")
+ matches("private[Bippy] object Foo extends Bar with Baz")
+ }
}
trait ClassDeconstruction { self: QuasiquoteProperties =>
@@ -51,6 +72,26 @@ trait ClassDeconstruction { self: QuasiquoteProperties =>
property("deconstruct bare case class") = test {
val q"$mods class $name(..$args) extends ..$parents" = q"case class Foo(x: Int)"
}
+
+ property("exhaustive class matcher") = test {
+ def matches(line: String) {
+ val q"""$classMods class $name[..$targs] $ctorMods(...$argss)
+ extends { ..$early } with ..$parents { $self => ..$body }""" = parse(line)
+ }
+ matches("class Foo")
+ matches("class Foo[T]")
+ matches("class Foo[T] @annot")
+ matches("class Foo extends Bar with Baz")
+ matches("class Foo { body }")
+ matches("class Foo extends { val early = 0 } with Any")
+ matches("abstract class Foo")
+ matches("private[Baz] class Foo")
+ matches("class Foo(first: A)(second: B)")
+ matches("class Foo(first: A) extends Bar(first) with Baz")
+ matches("class Foo private (first: A) { def bar }")
+ matches("class Foo { self => bar(self) }")
+ matches("case class Foo(x: Int)")
+ }
}
trait ModsDeconstruction { self: QuasiquoteProperties =>