Skip to content

Commit 9368256

Browse files
committed
annotations cleanup/fix for slitting between fields and getter annotation targets
1 parent d59494e commit 9368256

File tree

5 files changed

+47
-26
lines changed

5 files changed

+47
-26
lines changed

src/compiler/scala/tools/nsc/transform/Fields.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
275275
val clonedAccessor = (accessor cloneSymbol clazz) setPos clazz.pos
276276
setMixedinAccessorFlags(accessor, clonedAccessor)
277277

278+
if (clonedAccessor.isGetter)
279+
clonedAccessor setAnnotations (clonedAccessor.annotations filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false))
280+
278281
// if we don't cloneInfo, method argument symbols are shared between trait and subclasses --> lambalift proxy crash
279282
// TODO: use derive symbol variant?
280283
// println(s"cloning accessor $accessor to $clazz / $clonedInfo -> $relativeInfo")

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

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -306,44 +306,42 @@ trait MethodSynthesis {
306306

307307
result
308308
}
309+
309310
final def derive(initial: List[AnnotationInfo]): Tree = {
310311
validate()
311312

312-
// Which meta-annotation is associated with this kind of entity.
313-
// Presently one of: field, getter, setter, beanGetter, beanSetter, param.
314-
// For traits, getter must play role of field as there isn't one until Constructors (we'll triage there)
315-
val categories = this match {
316-
case _: BaseGetter => if (owner.isTrait) List(FieldTargetClass, GetterTargetClass) else List(GetterTargetClass)
317-
case _: Setter => List(SetterTargetClass)
318-
case _: BeanSetter => List(BeanSetterTargetClass)
319-
case _: AnyBeanGetter => List(BeanGetterTargetClass)
320-
case _: Param => List(ParamTargetClass)
321-
case _: Field => List(FieldTargetClass)
322-
}
323-
324-
// whether annotations whose definitions are not meta-annotated should be kept.
325-
val defaultRetention = this match {
326-
case _: Param => true
327-
// By default annotations go to the field, except if the field is
328-
// generated for a class parameter (PARAMACCESSOR).
329-
case _: Field => !mods.isParamAccessor
330-
case _: BaseGetter if owner.isTrait => true // like, Field, but cannot be a param accessor in a trait
331-
case _ => false
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, except in traits (@since 2.12),
318+
// where there is no field, and the getter thus holds annotations targetting both getter & field.
319+
// As soon as there is a field/getter (in subclasses mixing in the trait), we triage the annotations.
320+
//
321+
// TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit?
322+
// (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter)
323+
val annotFilter: AnnotationInfo => Boolean = this match {
324+
case _: Param => annotationFilter(ParamTargetClass, defaultRetention = true)
325+
// By default annotations go to the field, except if the field is generated for a class parameter (PARAMACCESSOR).
326+
case _: Field => annotationFilter(FieldTargetClass, defaultRetention = !mods.isParamAccessor)
327+
case _: BaseGetter if owner.isTrait => annotationFilter(List(FieldTargetClass, GetterTargetClass), defaultRetention = true)
328+
case _: BaseGetter => annotationFilter(GetterTargetClass, defaultRetention = false)
329+
case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false)
330+
case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false)
331+
case _: AnyBeanGetter => annotationFilter(BeanGetterTargetClass, defaultRetention = false)
332332
}
333333

334-
val derivedAnnotations = initial filter annotationFilter(categories, defaultRetention)
335-
// println(s"annots for $derivedSym: $derivedAnnotations (out of $initial)")
336334
// The annotations amongst those found on the original symbol which
337335
// should be propagated to this kind of accessor.
338-
derivedSym setAnnotations derivedAnnotations
336+
derivedSym setAnnotations (initial filter annotFilter)
339337

340338
if (derivedSym.isSetter && owner.isTrait && !isDeferred)
341339
derivedSym addAnnotation TraitSetterAnnotationClass
342340

343341
logDerived(derivedTree)
344342
}
345-
346343
}
344+
347345
sealed trait DerivedGetter extends DerivedFromValDef {
348346
def needsSetter = mods.isMutable
349347
}

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
461461
overrideError("cannot be used here - only term macros can override term macros")
462462
} else {
463463
checkOverrideTypes()
464-
checkOverrideDeprecated()
464+
if (!other.hasFlag(SYNTHESIZE_IMPL_IN_SUBCLASS))
465+
checkOverrideDeprecated()
465466
if (settings.warnNullaryOverride) {
466467
if (other.paramss.isEmpty && !member.paramss.isEmpty) {
467468
reporter.warning(member.pos, "non-nullary method overrides nullary method")
@@ -1450,7 +1451,20 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
14501451
tree match {
14511452
case m: MemberDef =>
14521453
val sym = m.symbol
1453-
applyChecks(sym.annotations)
1454+
val allAnnots = sym.annotations
1455+
1456+
// drop field-targeting annotations from getters
1457+
// (in traits, getters must also hold annotations that target the underlying field,
1458+
// because the latter won't be created until the trait is mixed into a class)
1459+
val annots =
1460+
if (sym.isGetter && sym.owner.isTrait) {
1461+
val annots = allAnnots filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false)
1462+
// if (annots != allAnnots) println(s"filtering for $sym: $allAnnots to $annots")
1463+
sym setAnnotations annots
1464+
annots
1465+
} else allAnnots
1466+
1467+
applyChecks(annots)
14541468

14551469
def messageWarning(name: String)(warn: String) =
14561470
reporter.warning(tree.pos, f"Invalid $name message for ${sym}%s${sym.locationString}%s:%n$warn")

src/library/scala/deprecatedInheritance.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
package scala
1010

11+
import scala.annotation.meta._
12+
1113
/** An annotation that designates that inheriting from a class is deprecated.
1214
*
1315
* This is usually done to warn about a non-final class being made final in a future version.
@@ -19,5 +21,6 @@ package scala
1921
* @since 2.10
2022
* @see [[scala.deprecatedOverriding]]
2123
*/
24+
@getter @setter @beanGetter @beanSetter
2225
private[scala] // for now, this needs to be generalized to communicate other modifier deltas
2326
class deprecatedInheritance(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation

src/library/scala/deprecatedOverriding.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
package scala
1010

11+
import scala.annotation.meta._
12+
1113
/** An annotation that designates that overriding a member is deprecated.
1214
*
1315
* Overriding such a member in a sub-class then generates a warning.
@@ -17,5 +19,6 @@ package scala
1719
* @since 2.10
1820
* @see [[scala.deprecatedInheritance]]
1921
*/
22+
@getter @setter @beanGetter @beanSetter
2023
private[scala] // for the same reasons as deprecatedInheritance
2124
class deprecatedOverriding(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation

0 commit comments

Comments
 (0)