diff options
author | Lukas Rytz <lukas.rytz@epfl.ch> | 2009-12-04 08:23:11 +0000 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@epfl.ch> | 2009-12-04 08:23:11 +0000 |
commit | 0e9c5b7f850d90117cab870d340c2b46cf73586b (patch) | |
tree | 3f8adce1a04efcb641d0eec28502a016dfabf633 | |
parent | 7feaefb229d567a5f56e8a29746d49db77f63314 (diff) | |
download | scala-0e9c5b7f850d90117cab870d340c2b46cf73586b.tar.gz scala-0e9c5b7f850d90117cab870d340c2b46cf73586b.tar.bz2 scala-0e9c5b7f850d90117cab870d340c2b46cf73586b.zip |
close #2708
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/Trees.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Definitions.scala | 11 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 82 | ||||
-rw-r--r-- | src/library/scala/annotation/target/param.scala | 51 |
4 files changed, 106 insertions, 41 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index dbbb306130..736a11a261 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -624,8 +624,9 @@ trait Trees { var vparamss1 = vparamss map (vps => vps.map { vd => atPos(vd.pos.focus) { + val pa = if (vd.hasFlag(PRIVATE | LOCAL)) OL else PARAMACCESSOR ValDef( - Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM) | PARAM) withAnnotations vd.mods.annotations, + Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM) | PARAM | pa) withAnnotations vd.mods.annotations, vd.name, vd.tpt.duplicate, vd.rhs.duplicate) }}) val (edefs, rest) = body span treeInfo.isEarlyDef diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index d10c896bc1..4d1650b0bc 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -121,11 +121,12 @@ trait Definitions { lazy val TailrecClass = getClass("scala.annotation.tailrec") lazy val SwitchClass = getClass("scala.annotation.switch") lazy val ElidableMethodClass = getClass("scala.annotation.elidable") - lazy val FieldClass = getClass("scala.annotation.target.field") - lazy val GetterClass = getClass("scala.annotation.target.getter") - lazy val SetterClass = getClass("scala.annotation.target.setter") - lazy val BeanGetterClass = getClass("scala.annotation.target.beanGetter") - lazy val BeanSetterClass = getClass("scala.annotation.target.beanSetter") + lazy val FieldTargetClass = getClass("scala.annotation.target.field") + lazy val GetterTargetClass = getClass("scala.annotation.target.getter") + lazy val SetterTargetClass = getClass("scala.annotation.target.setter") + lazy val BeanGetterTargetClass = getClass("scala.annotation.target.beanGetter") + lazy val BeanSetterTargetClass = getClass("scala.annotation.target.beanSetter") + lazy val ParamTargetClass = getClass("scala.annotation.target.param") // fundamental reference classes lazy val ScalaObjectClass = getClass("scala.ScalaObject") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 58146ffd80..f5062ba665 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1321,48 +1321,17 @@ trait Typers { self: Analyzer => case ValDef(mods, name, tpt, rhs) if (mods.flags & (PRIVATE | LOCAL)) != (PRIVATE | LOCAL).toLong && !stat.symbol.isModuleVar => - /** The annotations amongst `annots` that should go on a member of class - * `memberClass` (field, getter, setter, beanGetter, beanSetter) - */ - def memberAnnots(annots: List[AnnotationInfo], memberClass: Symbol) = { - - def hasMatching(metaAnnots: List[AnnotationInfo], orElse: => Boolean) = { - // either one of the meta-annotations matches the `memberClass` - metaAnnots.exists(_.atp.typeSymbol == memberClass) || - // else, if there is no `target` meta-annotation at all, use the default case - (metaAnnots.forall(ann => { - val annClass = ann.atp.typeSymbol - annClass != GetterClass && annClass != SetterClass && - annClass != BeanGetterClass && annClass != BeanSetterClass - }) && orElse) - } - - // there was no meta-annotation on `ann`. Look if the class annotations of - // `ann` has a `target` annotation, otherwise put `ann` only on fields. - def noMetaAnnot(ann: AnnotationInfo) = { - hasMatching(ann.atp.typeSymbol.annotations, memberClass == FieldClass) - } - - annots.filter(ann => ann.atp match { - // the annotation type has meta-annotations, e.g. @(foo @getter) - case AnnotatedType(metaAnnots, _, _) => - hasMatching(metaAnnots, noMetaAnnot(ann)) - // there are no meta-annotations, e.g. @foo - case _ => noMetaAnnot(ann) - }) - } - val isDeferred = mods hasFlag DEFERRED val value = stat.symbol val allAnnots = value.annotations if (!isDeferred) - value.setAnnotations(memberAnnots(allAnnots, FieldClass)) + value.setAnnotations(memberAnnots(allAnnots, FieldTargetClass)) val getter = if (isDeferred) value else value.getter(value.owner) assert(getter != NoSymbol, stat) if (getter hasFlag OVERLOADED) error(getter.pos, getter+" is defined twice") - getter.setAnnotations(memberAnnots(allAnnots, GetterClass)) + getter.setAnnotations(memberAnnots(allAnnots, GetterTargetClass)) if (value.hasFlag(LAZY)) List(stat) else { @@ -1385,7 +1354,7 @@ trait Typers { self: Analyzer => } checkNoEscaping.privates(getter, getterDef.tpt) def setterDef(setter: Symbol, isBean: Boolean = false): DefDef = { - setter.setAnnotations(memberAnnots(allAnnots, if (isBean) BeanSetterClass else SetterClass)) + setter.setAnnotations(memberAnnots(allAnnots, if (isBean) BeanSetterTargetClass else SetterTargetClass)) val result = typed { atPos(vdef.pos.focus) { DefDef( @@ -1417,7 +1386,7 @@ trait Typers { self: Analyzer => (if (value.hasAnnotation(BooleanBeanPropertyAttr)) "is" else "get") + nameSuffix val beanGetter = value.owner.info.decl(beanGetterName) - beanGetter.setAnnotations(memberAnnots(allAnnots, BeanGetterClass)) + beanGetter.setAnnotations(memberAnnots(allAnnots, BeanGetterTargetClass)) if (mods hasFlag MUTABLE) { val beanSetterName = "set" + nameSuffix val beanSetter = value.owner.info.decl(beanSetterName) @@ -1436,6 +1405,38 @@ trait Typers { self: Analyzer => List(stat) } + /** The annotations amongst `annots` that should go on a member of class + * `memberClass` (field, getter, setter, beanGetter, beanSetter, param) + */ + protected def memberAnnots(annots: List[AnnotationInfo], memberClass: Symbol) = { + + def hasMatching(metaAnnots: List[AnnotationInfo], orElse: => Boolean) = { + // either one of the meta-annotations matches the `memberClass` + metaAnnots.exists(_.atp.typeSymbol == memberClass) || + // else, if there is no `target` meta-annotation at all, use the default case + (metaAnnots.forall(ann => { + val annClass = ann.atp.typeSymbol + annClass != FieldTargetClass && annClass != GetterTargetClass && + annClass != SetterTargetClass && annClass != BeanGetterTargetClass && + annClass != BeanSetterTargetClass && annClass != ParamTargetClass + }) && orElse) + } + + // there was no meta-annotation on `ann`. Look if the class annotations of + // `ann` has a `target` annotation, otherwise put `ann` only on fields. + def noMetaAnnot(ann: AnnotationInfo) = { + hasMatching(ann.atp.typeSymbol.annotations, memberClass == FieldTargetClass) + } + + annots.filter(ann => ann.atp match { + // the annotation type has meta-annotations, e.g. @(foo @getter) + case AnnotatedType(metaAnnots, _, _) => + hasMatching(metaAnnots, noMetaAnnot(ann)) + // there are no meta-annotations, e.g. @foo + case _ => noMetaAnnot(ann) + }) + } + protected def enterSyms(txt: Context, trees: List[Tree]) = { var txt0 = txt for (tree <- trees) txt0 = enterSym(txt0, tree) @@ -1726,6 +1727,17 @@ trait Typers { self: Analyzer => reenterTypeParams(ddef.tparams) reenterValueParams(ddef.vparamss) + + // for `val` and `var` parameter, look at `target` meta-annotation + if (phase.id <= currentRun.typerPhase.id && meth.isPrimaryConstructor) { + for (vparams <- ddef.vparamss; vd <- vparams) { + if (vd hasFlag PARAMACCESSOR) { + val sym = vd.symbol + sym.setAnnotations(memberAnnots(sym.annotations, ParamTargetClass)) + } + } + } + val tparams1 = ddef.tparams mapConserve typedTypeDef val vparamss1 = ddef.vparamss mapConserve (_ mapConserve typedValDef) diff --git a/src/library/scala/annotation/target/param.scala b/src/library/scala/annotation/target/param.scala new file mode 100644 index 0000000000..821bd81eba --- /dev/null +++ b/src/library/scala/annotation/target/param.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.annotation.target + +/** + * For every field of a class, the Scala compiler generates up to four + * synthetic accessors: getter, setter, bean getter and bean setter. + * The meta-annotations in package {{{scala.annotation.target}}} are + * used to control to which of the above members the annotations on + * the field are copied. By default, field annotations are only added + * to the actual field, but not to any of the accessors. By annotating + * the annotation type or the annotation class with one or several of + * the meta-annotations this behavior can be changed. + * + * In the following example, the annotation {{{@Id}}} will be added + * only to the bean getter {{{getX}}}. In order to annotate the field + * as well, the meta-annotation {{{@field}}} would need to be added. + * + * {{{ + * import javax.persistence.Id + * class A { + * @(Id @beanGetter) @BeanProperty val x = 0 + * } + * }}} + * + * The syntax can be improved using a type alias: + * + * {{{ + * object ScalaJPA { + * type Id = javax.persistence.Id @beanGetter + * } + * import ScalaJPA.Id + * class A { + * @Id @BeanProperty val x = 0 + * } + * }}} + * + * For annotations defined in Scala, a default target can be specified + * in the annotation class itself, for example + * + * {{{ + * @getter + * class myAnnotation extends Annotation + * }}} + */ +final class param extends StaticAnnotation |