|
@@ -2908,7 +2908,7 @@ private void validateAnnotation(JCAnnotation a, JCTree declarationTree, Symbol s |
|
|
* the corresponding record component |
|
|
*/ |
|
|
ClassSymbol recordClass = (ClassSymbol) s.owner; |
|
|
RecordComponent rc = recordClass.getRecordComponent((VarSymbol)s, false); |
|
|
RecordComponent rc = recordClass.getRecordComponent((VarSymbol)s); |
|
|
SymbolMetadata metadata = rc.getMetadata(); |
|
|
if (metadata == null || metadata.isEmpty()) { |
|
|
/* if not is empty then we have already been here, which is the case if multiple annotations are applied |
|
@@ -2917,34 +2917,93 @@ private void validateAnnotation(JCAnnotation a, JCTree declarationTree, Symbol s |
|
|
rc.appendAttributes(s.getRawAttributes().stream().filter(anno -> |
|
|
Arrays.stream(getTargetNames(anno.type.tsym)).anyMatch(name -> name == names.RECORD_COMPONENT) |
|
|
).collect(List.collector())); |
|
|
rc.appendUniqueTypeAttributes(s.getRawTypeAttributes()); |
|
|
rc.setTypeAttributes(s.getRawTypeAttributes()); |
|
|
// to get all the type annotations applied to the type |
|
|
rc.type = s.type; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (a.type.tsym.isAnnotationType() && !annotationApplicable(a, s)) { |
|
|
if (isRecordMember && (s.flags_field & Flags.GENERATED_MEMBER) != 0) { |
|
|
/* so we have found an annotation that is not applicable to a record member that was generated by the |
|
|
* compiler. This was intentionally done at TypeEnter, now is the moment strip away the annotations |
|
|
* that are not applicable to the given record member |
|
|
*/ |
|
|
JCModifiers modifiers = TreeInfo.getModifiers(declarationTree); |
|
|
// lets first remove the annotation from the modifier |
|
|
if (modifiers != null) { |
|
|
ListBuffer<JCAnnotation> newAnnotations = new ListBuffer<>(); |
|
|
for (JCAnnotation anno : modifiers.annotations) { |
|
|
if (anno != a) { |
|
|
newAnnotations.add(anno); |
|
|
/* the section below is tricky. Annotations applied to record components are propagated to the corresponding |
|
|
* record member so if an annotation has target: FIELD, it is propagated to the corresponding FIELD, if it has |
|
|
* target METHOD, it is propagated to the accessor and so on. But at the moment when method members are generated |
|
|
* there is no enough information to propagate only the right annotations. So all the annotations are propagated |
|
|
* to all the possible locations. |
|
|
* |
|
|
* At this point we need to remove all the annotations that are not in place before going on with the annotation |
|
|
* party. On top of the above there is the issue that there is no AST representing record components, just symbols |
|
|
* so the corresponding field has been holding all the annotations and it's metadata has been modified as if it |
|
|
* was both a field and a record component. |
|
|
* |
|
|
* So there are two places where we need to trim annotations from: the metadata of the symbol and / or the modifiers |
|
|
* in the AST. Whatever is in the metadata will be written to the class file, whatever is in the modifiers could |
|
|
* be see by annotation processors. |
|
|
* |
|
|
* The metadata contains both type annotations and declaration annotations. At this point of the game we don't |
|
|
* need to care about type annotations, they are all in the right place. But we could need to remove declaration |
|
|
* annotations. So for declaration annotations if they are not applicable to the record member, excluding type |
|
|
* annotations which are already correct, then we will remove it. For the AST modifiers if the annotation is not |
|
|
* applicable either as type annotation and or declaration annotation, only in that case it will be removed. |
|
|
* |
|
|
* So it could be that annotation is removed as a declaration annotation but it is kept in the AST modifier for |
|
|
* further inspection by annotation processors. |
|
|
* |
|
|
* For example: |
|
|
* |
|
|
* import java.lang.annotation.*; |
|
|
* |
|
|
* @Target({ElementType.TYPE_USE, ElementType.RECORD_COMPONENT}) |
|
|
* @Retention(RetentionPolicy.RUNTIME) |
|
|
* @interface Anno { } |
|
|
* |
|
|
* record R(@Anno String s) {} |
|
|
* |
|
|
* at this point we will have for the case of the generated field: |
|
|
* - @Anno in the modifier |
|
|
* - @Anno as a type annotation |
|
|
* - @Anno as a declaration annotation |
|
|
* |
|
|
* the last one should be removed because the annotation has not FIELD as target but it was applied as a |
|
|
* declaration annotation because the field was being treated both as a field and as a record component |
|
|
* as we have already copied the annotations to the record component, now the field doesn't need to hold |
|
|
* annotations that are not intended for it anymore. Still @Anno has to be kept in the AST's modifiers as it |
|
|
* is applicable as a type annotation to the type of the field. |
|
|
*/ |
|
|
|
|
|
if (a.type.tsym.isAnnotationType()) { |
|
|
Optional<Set<Name>> applicableTargetsOp = getApplicableTargets(a, s); |
|
|
if (!applicableTargetsOp.isEmpty()) { |
|
|
Set<Name> applicableTargets = applicableTargetsOp.get(); |
|
|
boolean notApplicableOrIsTypeUseOnly = applicableTargets.isEmpty() || |
|
|
applicableTargets.size() == 1 && applicableTargets.contains(names.TYPE_USE); |
|
|
boolean isRecordMemberWithNonApplicableDeclAnno = |
|
|
isRecordMember && (s.flags_field & Flags.GENERATED_MEMBER) != 0 && notApplicableOrIsTypeUseOnly; |
|
|
|
|
|
if (applicableTargets.isEmpty() || isRecordMemberWithNonApplicableDeclAnno) { |
|
|
if (isRecordMemberWithNonApplicableDeclAnno) { |
|
|
/* so we have found an annotation that is not applicable to a record member that was generated by the |
|
|
* compiler. This was intentionally done at TypeEnter, now is the moment strip away the annotations |
|
|
* that are not applicable to the given record member |
|
|
*/ |
|
|
JCModifiers modifiers = TreeInfo.getModifiers(declarationTree); |
|
|
/* lets first remove the annotation from the modifier if it is not applicable, we have to check again as |
|
|
* it could be a type annotation |
|
|
*/ |
|
|
if (modifiers != null && applicableTargets.isEmpty()) { |
|
|
ListBuffer<JCAnnotation> newAnnotations = new ListBuffer<>(); |
|
|
for (JCAnnotation anno : modifiers.annotations) { |
|
|
if (anno != a) { |
|
|
newAnnotations.add(anno); |
|
|
} |
|
|
} |
|
|
modifiers.annotations = newAnnotations.toList(); |
|
|
} |
|
|
// now lets remove it from the symbol |
|
|
s.getMetadata().removeDeclarationMetadata(a.attribute); |
|
|
} else { |
|
|
log.error(a.pos(), Errors.AnnotationTypeNotApplicable); |
|
|
} |
|
|
modifiers.annotations = newAnnotations.toList(); |
|
|
} |
|
|
// now lets remove it from the symbol |
|
|
s.getMetadata().remove(a.attribute); |
|
|
} else { |
|
|
log.error(a.pos(), Errors.AnnotationTypeNotApplicable); |
|
|
} |
|
|
} |
|
|
|
|
@@ -3221,10 +3280,20 @@ boolean isTypeAnnotation(Attribute a, boolean isTypeParameter) { |
|
|
return targets; |
|
|
} |
|
|
|
|
|
@SuppressWarnings("preview") |
|
|
boolean annotationApplicable(JCAnnotation a, Symbol s) { |
|
|
Optional<Set<Name>> targets = getApplicableTargets(a, s); |
|
|
/* the optional could be emtpy if the annotation is unknown in that case |
|
|
* we return that it is applicable and if it is erroneous that should imply |
|
|
* an error at the declaration site |
|
|
*/ |
|
|
return targets.isEmpty() || targets.isPresent() && !targets.get().isEmpty(); |
|
|
} |
|
|
|
|
|
@SuppressWarnings("preview") |
|
|
Optional<Set<Name>> getApplicableTargets(JCAnnotation a, Symbol s) { |
|
|
Attribute.Array arr = getAttributeTargetAttribute(a.annotationType.type.tsym); |
|
|
Name[] targets; |
|
|
Set<Name> applicableTargets = new HashSet<>(); |
|
|
|
|
|
if (arr == null) { |
|
|
targets = defaultTargetMetaInfo(); |
|
@@ -3234,7 +3303,8 @@ boolean annotationApplicable(JCAnnotation a, Symbol s) { |
|
|
for (int i=0; i<arr.values.length; ++i) { |
|
|
Attribute app = arr.values[i]; |
|
|
if (!(app instanceof Attribute.Enum)) { |
|
|
return true; // recovery |
|
|
// recovery |
|
|
return Optional.empty(); |
|
|
} |
|
|
Attribute.Enum e = (Attribute.Enum) app; |
|
|
targets[i] = e.value.name; |
|
@@ -3243,55 +3313,55 @@ boolean annotationApplicable(JCAnnotation a, Symbol s) { |
|
|
for (Name target : targets) { |
|
|
if (target == names.TYPE) { |
|
|
if (s.kind == TYP) |
|
|
return true; |
|
|
applicableTargets.add(names.TYPE); |
|
|
} else if (target == names.FIELD) { |
|
|
if (s.kind == VAR && s.owner.kind != MTH) |
|
|
return true; |
|
|
applicableTargets.add(names.FIELD); |
|
|
} else if (target == names.RECORD_COMPONENT) { |
|
|
if (s.getKind() == ElementKind.RECORD_COMPONENT) { |
|
|
return true; |
|
|
applicableTargets.add(names.RECORD_COMPONENT); |
|
|
} |
|
|
} else if (target == names.METHOD) { |
|
|
if (s.kind == MTH && !s.isConstructor()) |
|
|
return true; |
|
|
applicableTargets.add(names.METHOD); |
|
|
} else if (target == names.PARAMETER) { |
|
|
if (s.kind == VAR && |
|
|
(s.owner.kind == MTH && (s.flags() & PARAMETER) != 0)) { |
|
|
return true; |
|
|
applicableTargets.add(names.PARAMETER); |
|
|
} |
|
|
} else if (target == names.CONSTRUCTOR) { |
|
|
if (s.kind == MTH && s.isConstructor()) |
|
|
return true; |
|
|
applicableTargets.add(names.CONSTRUCTOR); |
|
|
} else if (target == names.LOCAL_VARIABLE) { |
|
|
if (s.kind == VAR && s.owner.kind == MTH && |
|
|
(s.flags() & PARAMETER) == 0) { |
|
|
return true; |
|
|
applicableTargets.add(names.LOCAL_VARIABLE); |
|
|
} |
|
|
} else if (target == names.ANNOTATION_TYPE) { |
|
|
if (s.kind == TYP && (s.flags() & ANNOTATION) != 0) { |
|
|
return true; |
|
|
applicableTargets.add(names.ANNOTATION_TYPE); |
|
|
} |
|
|
} else if (target == names.PACKAGE) { |
|
|
if (s.kind == PCK) |
|
|
return true; |
|
|
applicableTargets.add(names.PACKAGE); |
|
|
} else if (target == names.TYPE_USE) { |
|
|
if (s.kind == VAR && s.owner.kind == MTH && s.type.hasTag(NONE)) { |
|
|
//cannot type annotate implicitly typed locals |
|
|
return false; |
|
|
continue; |
|
|
} else if (s.kind == TYP || s.kind == VAR || |
|
|
(s.kind == MTH && !s.isConstructor() && |
|
|
!s.type.getReturnType().hasTag(VOID)) || |
|
|
(s.kind == MTH && s.isConstructor())) { |
|
|
return true; |
|
|
applicableTargets.add(names.TYPE_USE); |
|
|
} |
|
|
} else if (target == names.TYPE_PARAMETER) { |
|
|
if (s.kind == TYP && s.type.hasTag(TYPEVAR)) |
|
|
return true; |
|
|
applicableTargets.add(names.TYPE_PARAMETER); |
|
|
} else |
|
|
return true; // Unknown ElementType. This should be an error at declaration site, |
|
|
// assume applicable. |
|
|
return Optional.empty(); // Unknown ElementType. This should be an error at declaration site, |
|
|
// assume applicable. |
|
|
} |
|
|
return false; |
|
|
return Optional.of(applicableTargets); |
|
|
} |
|
|
|
|
|
Attribute.Array getAttributeTargetAttribute(TypeSymbol s) { |
|
|