diff options
author | Den Shabalin <den.shabalin@gmail.com> | 2013-09-02 16:01:38 +0200 |
---|---|---|
committer | Den Shabalin <den.shabalin@gmail.com> | 2013-09-05 20:42:09 +0200 |
commit | 1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f (patch) | |
tree | c66d11c9b21a29a7601fedb4d8ad163dad0fa5cf | |
parent | 73a4f172c3b4f2c7d8bf9936898f031d09899cef (diff) | |
download | scala-1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f.tar.gz scala-1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f.tar.bz2 scala-1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f.zip |
first-class early def splicing and extraction support
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 => |