/* NSC -- new Scala compiler * Copyright 2005-2011 LAMP/EPFL * @author Paul Phillips */ package scala.tools.nsc package typechecker import symtab.Flags._ import scala.collection.{ mutable, immutable } object listutil { def mexists[T](xss: List[List[T]])(p: T => Boolean) = xss exists (_ exists p) def mmap[T, U](xss: List[List[T]])(f: T => U) = xss map (_ map f) def mforeach[T](xss: List[List[T]])(f: T => Unit) = xss foreach (_ foreach f) def mfind[T](xss: List[List[T]])(p: T => Boolean): Option[T] = { for (xs <- xss; x <- xs) if (p(x)) return Some(x) None } def mfilter[T](xss: List[List[T]])(p: T => Boolean) = for (xs <- xss; x <- xs; if p(x)) yield x } /** Logic related to method synthesis which involves cooperation between * Namer and Typer. */ trait MethodSynthesis { self: Analyzer => import global._ import definitions._ /** There are two key methods in here. * * 1) enterGetterSetter is called from Namer with a ValDef which * may need accessors. Some setup is performed. In general this * creates symbols and enters them into the scope of the owner. * * 2) finishGetterSetter is called from Typer when a Template is typed. * It completes the job, returning a list of trees with their symbols * set to those created in enterGetterSetter. Those trees then become * part of the typed template. */ trait MethodSynth { self: Namer => def enterGetterSetter(tree: ValDef) { val ValDef(mods, name, _, _) = tree if (nme.isSetterName(name)) context.error(tree.pos, "Names of vals or vars may not end in `_='") val getter = Getter(tree).createAndEnterSymbol() tree.symbol = ( if (mods.isLazy) enterLazyVal(tree, getter) else { if (mods.isPrivateLocal) context.error(tree.pos, "private[this] not allowed for case class parameters") // Create the setter if necessary. if (mods.isMutable) Setter(tree).createAndEnterSymbol() // If abstract, the tree gets the getter's symbol. Otherwise, create a field. if (mods.isDeferred) getter setPos tree.pos else enterStrictVal(tree) } ) enterBeans(tree) } def finishGetterSetter(typer: Typer, stat: Tree): List[Tree] = stat match { case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) => // If we don't save the annotations, they seem to wander off. val annotations = stat.symbol.initialize.annotations val trees = ( allValDefDerived(vd) map (acc => atPos(vd.pos.focus)(acc derive annotations)) filterNot (_ eq EmptyTree) ) log(trees.mkString("Accessor trees:\n ", "\n ", "\n")) if (vd.symbol.isLazy) List(stat) else trees case _ => List(stat) } def standardAccessors(vd: ValDef): List[DerivedFromValDef] = ( if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd)) else List(Getter(vd)) ) def beanAccessors(vd: ValDef): List[DerivedFromValDef] = { if (forMSIL) Nil else if (vd.symbol hasAnnotation BeanPropertyAttr) { if (vd.mods.isMutable) List(BeanGetter(vd), BeanSetter(vd)) else List(BeanGetter(vd)) } else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr) List(BooleanBeanGetter(vd)) else Nil } def allValDefDerived(vd: ValDef) = { val field = if (vd.mods.isDeferred) Nil else List(Field(vd)) field ::: standardAccessors(vd) ::: beanAccessors(vd) } trait Derived { def name: TermName def flagsMask: Long def flagsExtra: Long def completer(sym: Symbol): Type } trait DerivedFromValDef extends Derived { /** The declaration from which we are deriving. */ def tree: ValDef /** Which meta-annotation is associated with this kind of entity. * Presently one of: field, getter, setter, beanGetter, beanSetter, param. */ def category: Symbol // Final methods to make the rest easier to reason about. final def mods = tree.mods final def basisSym = tree.symbol final def enclClass = basisSym.enclClass final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter) final def fieldSelection = Select(This(enclClass), basisSym) final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil) def derivedSym: Symbol = tree.symbol def derivedTree: Tree = EmptyTree def isSetter = false def isDeferred = mods.isDeferred def keepClean = false // whether annotations whose definitions are not meta-annotated should be kept. def validate() { } def createAndEnterSymbol(): Symbol = { val sym = ( owner.newMethod(tree.pos.focus, name) setFlag tree.mods.flags & flagsMask setFlag flagsExtra ) setPrivateWithin(tree, sym) enterInScope(sym) sym setInfo completer(sym) } /** The annotations amongst those found on the original symbol which * should be propagated to this kind of accessor. */ private def deriveAnnotations(initial: List[AnnotationInfo]): List[AnnotationInfo] = { initial filter { ann => // There are no meta-annotation arguments attached to `ann` if (ann.metaAnnotations.isEmpty) { // A meta-annotation matching `annotKind` exists on `ann`'s definition. (ann.defaultTargets contains category) || // `ann`'s definition has no meta-annotations, and `keepClean` is true. (ann.defaultTargets.isEmpty && keepClean) } // There are meta-annotation arguments, and one of them matches `annotKind` else ann.metaAnnotations exists (_ matches category) } } private def logDerived(result: Tree): Tree = { val id = List(mods.defaultFlagString, basisSym.accurateKindString, basisSym.getterName) filterNot (_ == "") mkString " " log("[+derived] " + id + " (" + derivedSym + ")\n " + result) result } final def derive(initial: List[AnnotationInfo]): Tree = { validate() derivedSym setAnnotations deriveAnnotations(initial) logDerived(derivedTree) } } trait DerivedGetter extends DerivedFromValDef { // TODO } trait DerivedSetter extends DerivedFromValDef { override def isSetter = true private def setterParam = derivedSym.paramss match { case (p :: Nil) :: _ => p case _ => NoSymbol } private def setterRhs = ( if (mods.isDeferred || derivedSym.isOverloaded) EmptyTree else Assign(fieldSelection, Ident(setterParam)) ) private def setterDef = DefDef(derivedSym, setterRhs) override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef } case class Getter(tree: ValDef) extends DerivedGetter { def name = tree.name def category = GetterTargetClass def flagsMask = GetterFlags def flagsExtra = ACCESSOR | ( if (tree.mods.isMutable) 0 else STABLE ) override def derivedSym = ( if (mods.isDeferred) basisSym else basisSym.getter(enclClass) ) override def validate() { assert(derivedSym != NoSymbol, tree) if (derivedSym.isOverloaded) context.error(derivedSym.pos, derivedSym+" is defined twice") super.validate() } // keep type tree of original abstract field private def fixTypeTree(dd: DefDef): DefDef = { dd.tpt match { case tt: TypeTree if dd.rhs == EmptyTree => tt setOriginal tree.tpt case tpt => tpt setPos tree.tpt.pos.focus } dd } override def derivedTree: DefDef = { fixTypeTree { DefDef(derivedSym, if (mods.isDeferred) EmptyTree else gen.mkCheckInit(fieldSelection) ) } } } case class Setter(tree: ValDef) extends DerivedSetter { def name = nme.getterToSetter(tree.name) def category = SetterTargetClass def flagsMask = SetterFlags def flagsExtra = ACCESSOR override def derivedSym = basisSym.setter(enclClass) } case class Field(tree: ValDef) extends DerivedFromValDef { def name = nme.getterToLocal(tree.name) def category = FieldTargetClass def flagsMask = FieldFlags def flagsExtra = PrivateLocal // By default annotations go to the field, except if the field is // generated for a class parameter (PARAMACCESSOR). override def keepClean = !mods.isParamAccessor override def derivedTree = ( if (mods.isDeferred) EmptyTree else treeCopy.ValDef(tree, mods | flagsExtra, name, tree.tpt, tree.rhs) ) } case class Param(tree: ValDef) extends DerivedFromValDef { def name = tree.name def category = ParamTargetClass def flagsMask = -1L def flagsExtra = 0L override def keepClean = true override def derivedTree = EmptyTree } def validateParam(tree: ValDef) { Param(tree).derive(tree.symbol.annotations) } sealed abstract class BeanAccessor(bean: String) extends DerivedFromValDef { def name = bean + tree.name.toString.capitalize def flagsMask = BeanPropertyFlags def flagsExtra = 0 override def derivedSym = enclClass.info decl name } trait AnyBeanGetter extends BeanAccessor with DerivedGetter { def category = BeanGetterTargetClass override def validate() { if (derivedSym == NoSymbol) { // the namer decides whether to generate these symbols or not. at that point, we don't // have symbolic information yet, so we only look for annotations named "BeanProperty". context.error(tree.pos, "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import") } super.validate() } } trait NoSymbolBeanGetter extends AnyBeanGetter { // Derives a tree without attempting to use the original tree's symbol. override def derivedTree = { atPos(tree.pos.focus) { DefDef(derivedMods, name, Nil, List(Nil), tree.tpt.duplicate, if (isDeferred) EmptyTree else Select(This(owner), tree.name) ) } } override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree) } case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { } case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { } case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter { def category = BeanSetterTargetClass } // No Symbols available. private def beanAccessorsFromNames(tree: ValDef) = { val ValDef(mods, name, tpt, _) = tree val hasBP = mods hasAnnotationNamed tpnme.BeanPropertyAnnot val hasBoolBP = mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot if (hasBP || hasBoolBP) { val getter = ( if (hasBP) new BeanGetter(tree) with NoSymbolBeanGetter else new BooleanBeanGetter(tree) with NoSymbolBeanGetter ) getter :: { if (mods.isMutable) List(BeanSetter(tree)) else Nil } } else Nil } protected def enterBeans(tree: ValDef) { if (forMSIL) return val ValDef(mods, name, _, _) = tree val beans = beanAccessorsFromNames(tree) if (beans.nonEmpty) { if (!name(0).isLetter) context.error(tree.pos, "`BeanProperty' annotation can be applied only to fields that start with a letter") else if (mods.isPrivate) // avoids name clashes with private fields in traits context.error(tree.pos, "`BeanProperty' annotation can be applied only to non-private fields") // Create and enter the symbols here, add the trees in finishGetterSetter. beans foreach (_.createAndEnterSymbol()) } } } }