Skip to content

Commit 57de5b8

Browse files
committed
Annotation filtering & derivation in one place.
This logic was scattered all over the hierarchy, even though it's only needed in one spot, and is unlikely to evolve.
1 parent c0454b1 commit 57de5b8

File tree

2 files changed

+47
-34
lines changed

2 files changed

+47
-34
lines changed

src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,6 @@ trait MethodSynthesis {
2222
import definitions._
2323
import CODE._
2424

25-
/** The annotations amongst those found on the original symbol which
26-
* should be propagated to this kind of accessor.
27-
*/
28-
def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = {
29-
def annotationFilter(ann: AnnotationInfo) = ann.metaAnnotations match {
30-
case Nil if ann.defaultTargets.isEmpty => keepClean // no meta-annotations or default targets
31-
case Nil => ann.defaultTargets contains category // default targets exist for ann
32-
case metas => metas exists (_ matches category) // meta-annotations attached to ann
33-
}
34-
initial filter annotationFilter
35-
}
3625

3726
class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) {
3827
def mkThis = This(clazz) setPos clazz.pos.focus
@@ -160,13 +149,15 @@ trait MethodSynthesis {
160149
enterBeans(tree)
161150
}
162151

152+
import AnnotationInfo.{mkFilter => annotationFilter}
153+
163154
/** This is called for those ValDefs which addDerivedTrees ignores, but
164155
* which might have a warnable annotation situation.
165156
*/
166157
private def warnForDroppedAnnotations(tree: Tree) {
167158
val annotations = tree.symbol.initialize.annotations
168159
val targetClass = defaultAnnotationTarget(tree)
169-
val retained = deriveAnnotations(annotations, targetClass, keepClean = true)
160+
val retained = annotations filter annotationFilter(targetClass, defaultRetention = true)
170161

171162
annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass))
172163
}
@@ -208,8 +199,8 @@ trait MethodSynthesis {
208199
context.unit.synthetics get meth match {
209200
case Some(mdef) =>
210201
context.unit.synthetics -= meth
211-
meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, keepClean = false)
212-
cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, keepClean = true)
202+
meth setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false))
203+
cd.symbol setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true))
213204
List(cd, mdef)
214205
case _ =>
215206
// Shouldn't happen, but let's give ourselves a reasonable error when it does
@@ -293,10 +284,6 @@ trait MethodSynthesis {
293284
def tree: ValDef
294285
final def enclClass = basisSym.enclClass
295286

296-
/** Which meta-annotation is associated with this kind of entity.
297-
* Presently one of: field, getter, setter, beanGetter, beanSetter, param.
298-
*/
299-
def category: Symbol
300287

301288
/* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */
302289
final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter)
@@ -307,7 +294,6 @@ trait MethodSynthesis {
307294

308295
def isSetter = false
309296
def isDeferred = mods.isDeferred
310-
def keepClean = false // whether annotations whose definitions are not meta-annotated should be kept.
311297
def validate() { }
312298
def createAndEnterSymbol(): MethodSymbol = {
313299
val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags)
@@ -323,7 +309,29 @@ trait MethodSynthesis {
323309
}
324310
final def derive(initial: List[AnnotationInfo]): Tree = {
325311
validate()
326-
derivedSym setAnnotations deriveAnnotations(initial, category, keepClean)
312+
313+
// see scala.annotation.meta's package class for more info
314+
// Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param.
315+
// The defaults are:
316+
// - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity.
317+
// - val/var member annotations solely end up on the underlying field.
318+
//
319+
// TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit?
320+
// (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter)
321+
val annotFilter: AnnotationInfo => Boolean = this match {
322+
case _: Param => annotationFilter(ParamTargetClass, defaultRetention = true)
323+
// By default annotations go to the field, except if the field is generated for a class parameter (PARAMACCESSOR).
324+
case _: Field => annotationFilter(FieldTargetClass, defaultRetention = !mods.isParamAccessor)
325+
case _: BaseGetter => annotationFilter(GetterTargetClass, defaultRetention = false)
326+
case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false)
327+
case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false)
328+
case _: AnyBeanGetter => annotationFilter(BeanGetterTargetClass, defaultRetention = false)
329+
}
330+
331+
// The annotations amongst those found on the original symbol which
332+
// should be propagated to this kind of accessor.
333+
derivedSym setAnnotations (initial filter annotFilter)
334+
327335
logDerived(derivedTree)
328336
}
329337
}
@@ -370,7 +378,6 @@ trait MethodSynthesis {
370378

371379
sealed abstract class BaseGetter(tree: ValDef) extends DerivedGetter {
372380
def name = tree.name
373-
def category = GetterTargetClass
374381
def flagsMask = GetterFlags
375382
def flagsExtra = ACCESSOR.toLong | ( if (tree.mods.isMutable) 0 else STABLE )
376383

@@ -450,7 +457,6 @@ trait MethodSynthesis {
450457
}
451458
case class Setter(tree: ValDef) extends DerivedSetter {
452459
def name = tree.setterName
453-
def category = SetterTargetClass
454460
def flagsMask = SetterFlags
455461
def flagsExtra = ACCESSOR
456462

@@ -470,18 +476,14 @@ trait MethodSynthesis {
470476
// NOTE: do not look at `vd.symbol` when called from `enterGetterSetter` (luckily, that call-site implies `!mods.isLazy`),
471477
// as the symbol info is in the process of being created then.
472478
// TODO: harmonize tree & symbol creation
473-
// TODO: the `def field` call-site does not tollerate including `|| vd.symbol.owner.isTrait` --> tests break
474-
def noFieldFor(vd: ValDef) = vd.mods.isDeferred || (vd.mods.isLazy && isUnitType(vd.symbol.info)) // || vd.symbol.owner.isTrait))
479+
// TODO: the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite)
480+
def noFieldFor(vd: ValDef) = vd.mods.isDeferred || (vd.mods.isLazy && isUnitType(vd.symbol.info))
475481
}
476482

477483
case class Field(tree: ValDef) extends DerivedFromValDef {
478484
def name = tree.localName
479-
def category = FieldTargetClass
480485
def flagsMask = FieldFlags
481486
def flagsExtra = PrivateLocal
482-
// By default annotations go to the field, except if the field is
483-
// generated for a class parameter (PARAMACCESSOR).
484-
override def keepClean = !mods.isParamAccessor
485487

486488
// handle lazy val first for now (we emit a Field even though we probably shouldn't...)
487489
override def derivedTree =
@@ -492,10 +494,8 @@ trait MethodSynthesis {
492494
}
493495
case class Param(tree: ValDef) extends DerivedFromValDef {
494496
def name = tree.name
495-
def category = ParamTargetClass
496497
def flagsMask = -1L
497498
def flagsExtra = 0L
498-
override def keepClean = true
499499
override def derivedTree = EmptyTree
500500
}
501501
def validateParam(tree: ValDef) {
@@ -509,7 +509,6 @@ trait MethodSynthesis {
509509
override def derivedSym = enclClass.info decl name
510510
}
511511
sealed trait AnyBeanGetter extends BeanAccessor with DerivedGetter {
512-
def category = BeanGetterTargetClass
513512
override def validate() {
514513
if (derivedSym == NoSymbol) {
515514
// the namer decides whether to generate these symbols or not. at that point, we don't
@@ -532,9 +531,7 @@ trait MethodSynthesis {
532531
}
533532
case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { }
534533
case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { }
535-
case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter {
536-
def category = BeanSetterTargetClass
537-
}
534+
case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter
538535

539536
// No Symbols available.
540537
private def beanAccessorsFromNames(tree: ValDef) = {

src/reflect/scala/reflect/internal/AnnotationInfos.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,22 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
169169

170170
def unapply(info: AnnotationInfo): Option[(Type, List[Tree], List[(Name, ClassfileAnnotArg)])] =
171171
Some((info.atp, info.args, info.assocs))
172+
173+
def mkFilter(category: Symbol, defaultRetention: Boolean)(ann: AnnotationInfo) =
174+
(ann.metaAnnotations, ann.defaultTargets) match {
175+
case (Nil, Nil) => defaultRetention
176+
case (Nil, defaults) => defaults contains category
177+
case (metas, _) => metas exists (_ matches category)
178+
}
179+
180+
def mkFilter(categories: List[Symbol], defaultRetention: Boolean)(ann: AnnotationInfo) =
181+
(ann.metaAnnotations, ann.defaultTargets) match {
182+
case (Nil, Nil) => defaultRetention
183+
case (Nil, defaults) => categories exists defaults.contains
184+
case (metas, _) =>
185+
val metaSyms = metas collect { case ann if !ann.symbol.isInstanceOf[StubSymbol] => ann.symbol }
186+
categories exists (category => metaSyms exists (_ isNonBottomSubClass category))
187+
}
172188
}
173189

174190
class CompleteAnnotationInfo(

0 commit comments

Comments
 (0)