8288130: compiler error with AP and explicit record accessor
Reviewed-by: jlahoda
This commit is contained in:
parent
08288819dd
commit
53b37fe153
@ -1503,30 +1503,25 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecordComponent getRecordComponent(JCVariableDecl var, boolean addIfMissing, List<JCAnnotation> annotations) {
|
/* creates a record component if non is related to the given variable and recreates a brand new one
|
||||||
|
* in other case
|
||||||
|
*/
|
||||||
|
public RecordComponent createRecordComponent(JCVariableDecl var, List<JCAnnotation> annotations) {
|
||||||
RecordComponent toRemove = null;
|
RecordComponent toRemove = null;
|
||||||
for (RecordComponent rc : recordComponents) {
|
for (RecordComponent rc : recordComponents) {
|
||||||
/* it could be that a record erroneously declares two record components with the same name, in that
|
/* it could be that a record erroneously declares two record components with the same name, in that
|
||||||
* case we need to use the position to disambiguate
|
* case we need to use the position to disambiguate
|
||||||
*/
|
*/
|
||||||
if (rc.name == var.name && var.pos == rc.pos) {
|
if (rc.name == var.name && var.pos == rc.pos) {
|
||||||
if (rc.type.hasTag(TypeTag.ERROR) && !var.sym.type.hasTag(TypeTag.ERROR)) {
|
toRemove = rc;
|
||||||
// Found a record component with an erroneous type: save it so that it can be removed later.
|
|
||||||
// If the class type of the record component is generated by annotation processor, it should
|
|
||||||
// use the new actual class type and symbol instead of the old dummy ErrorType.
|
|
||||||
toRemove = rc;
|
|
||||||
} else {
|
|
||||||
// Found a good record component: just return.
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RecordComponent rc = null;
|
RecordComponent rc = null;
|
||||||
if (toRemove != null) {
|
if (toRemove != null) {
|
||||||
// Found a record component with an erroneous type: remove it and create a new one
|
// Found a record component with an erroneous type: remove it and create a new one
|
||||||
recordComponents = List.filter(recordComponents, toRemove);
|
recordComponents = List.filter(recordComponents, toRemove);
|
||||||
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));
|
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, toRemove.originalAnnos, toRemove.isVarargs));
|
||||||
} else if (addIfMissing) {
|
} else {
|
||||||
// Didn't find the record component: create one.
|
// Didn't find the record component: create one.
|
||||||
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));
|
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));
|
||||||
}
|
}
|
||||||
@ -1809,6 +1804,10 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
|
|||||||
}
|
}
|
||||||
|
|
||||||
public RecordComponent(VarSymbol field, List<JCAnnotation> annotations) {
|
public RecordComponent(VarSymbol field, List<JCAnnotation> annotations) {
|
||||||
|
this(field, annotations, field.type.hasTag(TypeTag.ARRAY) && ((ArrayType)field.type).isVarargs());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordComponent(VarSymbol field, List<JCAnnotation> annotations, boolean isVarargs) {
|
||||||
super(PUBLIC, field.name, field.type, field.owner);
|
super(PUBLIC, field.name, field.type, field.owner);
|
||||||
this.originalAnnos = annotations;
|
this.originalAnnos = annotations;
|
||||||
this.pos = field.pos;
|
this.pos = field.pos;
|
||||||
@ -1817,7 +1816,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
|
|||||||
* the symbol will be blown out and we won't be able to know if the original
|
* the symbol will be blown out and we won't be able to know if the original
|
||||||
* record component was declared varargs or not.
|
* record component was declared varargs or not.
|
||||||
*/
|
*/
|
||||||
this.isVarargs = type.hasTag(TypeTag.ARRAY) && ((ArrayType)type).isVarargs();
|
this.isVarargs = isVarargs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JCAnnotation> getOriginalAnnos() { return originalAnnos; }
|
public List<JCAnnotation> getOriginalAnnos() { return originalAnnos; }
|
||||||
|
@ -2960,6 +2960,9 @@ public class Check {
|
|||||||
/** Check an annotation of a symbol.
|
/** Check an annotation of a symbol.
|
||||||
*/
|
*/
|
||||||
private void validateAnnotation(JCAnnotation a, JCTree declarationTree, Symbol s) {
|
private void validateAnnotation(JCAnnotation a, JCTree declarationTree, Symbol s) {
|
||||||
|
/** NOTE: if annotation processors are present, annotation processing rounds can happen after this method,
|
||||||
|
* this can impact in particular records for which annotations are forcibly propagated.
|
||||||
|
*/
|
||||||
validateAnnotationTree(a);
|
validateAnnotationTree(a);
|
||||||
boolean isRecordMember = ((s.flags_field & RECORD) != 0 || s.enclClass() != null && s.enclClass().isRecord());
|
boolean isRecordMember = ((s.flags_field & RECORD) != 0 || s.enclClass() != null && s.enclClass().isRecord());
|
||||||
|
|
||||||
|
@ -983,7 +983,7 @@ public class TypeEnter implements Completer {
|
|||||||
List<JCVariableDecl> fields = TreeInfo.recordFields(tree);
|
List<JCVariableDecl> fields = TreeInfo.recordFields(tree);
|
||||||
memberEnter.memberEnter(fields, env);
|
memberEnter.memberEnter(fields, env);
|
||||||
for (JCVariableDecl field : fields) {
|
for (JCVariableDecl field : fields) {
|
||||||
sym.getRecordComponent(field, true,
|
sym.createRecordComponent(field,
|
||||||
field.mods.annotations.isEmpty() ?
|
field.mods.annotations.isEmpty() ?
|
||||||
List.nil() :
|
List.nil() :
|
||||||
new TreeCopier<JCTree>(make.at(field.pos)).copy(field.mods.annotations));
|
new TreeCopier<JCTree>(make.at(field.pos)).copy(field.mods.annotations));
|
||||||
@ -1229,10 +1229,37 @@ public class TypeEnter implements Completer {
|
|||||||
memberEnter.memberEnter(equals, env);
|
memberEnter.memberEnter(equals, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields can't be varargs, lets remove the flag
|
/** Some notes regarding the code below. Annotations applied to elements of a record header are propagated
|
||||||
|
* to other elements which, when applicable, not explicitly declared by the user: the canonical constructor,
|
||||||
|
* accessors, fields and record components. Of all these the only ones that can't be explicitly declared are
|
||||||
|
* the fields and the record components.
|
||||||
|
*
|
||||||
|
* Now given that annotations are propagated to all possible targets regardless of applicability,
|
||||||
|
* annotations not applicable to a given element should be removed. See Check::validateAnnotation. Once
|
||||||
|
* annotations are removed we could lose the whole picture, that's why original annotations are stored in
|
||||||
|
* the record component, see RecordComponent::originalAnnos, but there is no real AST representing a record
|
||||||
|
* component so if there is an annotation processing round it could be that we need to reenter a record for
|
||||||
|
* which we need to re-attribute its annotations. This is why one of the things the code below is doing is
|
||||||
|
* copying the original annotations from the record component to the corresponding field, again this applies
|
||||||
|
* only if APs are present.
|
||||||
|
*
|
||||||
|
* We need to copy the annotations to the field so that annotations applicable only to the record component
|
||||||
|
* can be attributed as if declared in the field and then stored in the metadata associated to the record
|
||||||
|
* component.
|
||||||
|
*/
|
||||||
List<JCVariableDecl> recordFields = TreeInfo.recordFields(tree);
|
List<JCVariableDecl> recordFields = TreeInfo.recordFields(tree);
|
||||||
for (JCVariableDecl field: recordFields) {
|
for (JCVariableDecl field: recordFields) {
|
||||||
|
RecordComponent rec = tree.sym.getRecordComponent(field.sym);
|
||||||
|
TreeCopier<JCTree> tc = new TreeCopier<>(make.at(field.pos));
|
||||||
|
List<JCAnnotation> originalAnnos = tc.copy(rec.getOriginalAnnos());
|
||||||
|
|
||||||
field.mods.flags &= ~Flags.VARARGS;
|
field.mods.flags &= ~Flags.VARARGS;
|
||||||
|
if (originalAnnos.length() != field.mods.annotations.length()) {
|
||||||
|
field.mods.annotations = originalAnnos;
|
||||||
|
annotate.annotateLater(originalAnnos, env, field.sym, field.pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
// also here
|
||||||
field.sym.flags_field &= ~Flags.VARARGS;
|
field.sym.flags_field &= ~Flags.VARARGS;
|
||||||
}
|
}
|
||||||
// now lets add the accessors
|
// now lets add the accessors
|
||||||
|
@ -65,8 +65,8 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T extends JCTree> List<T> copy(List<T> trees, P p) {
|
public <T extends JCTree> List<T> copy(List<T> trees, P p) {
|
||||||
if (trees == null)
|
if (trees == null || trees.isEmpty())
|
||||||
return null;
|
return trees;
|
||||||
ListBuffer<T> lb = new ListBuffer<>();
|
ListBuffer<T> lb = new ListBuffer<>();
|
||||||
for (T tree: trees)
|
for (T tree: trees)
|
||||||
lb.append(copy(tree, p));
|
lb.append(copy(tree, p));
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
* RecordCompilationTests
|
* RecordCompilationTests
|
||||||
*
|
*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8250629 8252307 8247352 8241151 8246774 8259025
|
* @bug 8250629 8252307 8247352 8241151 8246774 8259025 8288130
|
||||||
* @summary Negative compilation tests, and positive compilation (smoke) tests for records
|
* @summary Negative compilation tests, and positive compilation (smoke) tests for records
|
||||||
* @library /lib/combo /tools/lib /tools/javac/lib
|
* @library /lib/combo /tools/lib /tools/javac/lib
|
||||||
* @modules
|
* @modules
|
||||||
@ -177,6 +177,23 @@ public class RecordCompilationTests extends CompilationTestCase {
|
|||||||
assertOK("@Deprecated record R(int x, int y) { }");
|
assertOK("@Deprecated record R(int x, int y) { }");
|
||||||
assertOK("record R(@Deprecated int x, int y) { }");
|
assertOK("record R(@Deprecated int x, int y) { }");
|
||||||
assertOK("record R<T>(T x, T y) { }");
|
assertOK("record R<T>(T x, T y) { }");
|
||||||
|
assertOK(
|
||||||
|
"""
|
||||||
|
record R<T>(T x) {
|
||||||
|
public T x() {
|
||||||
|
return this.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
assertOK(
|
||||||
|
"""
|
||||||
|
import java.util.List;
|
||||||
|
record R<T>(List<T> x) {
|
||||||
|
public List<T> x() {
|
||||||
|
return this.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGoodMemberDeclarations() {
|
public void testGoodMemberDeclarations() {
|
||||||
@ -1261,9 +1278,8 @@ public class RecordCompilationTests extends CompilationTestCase {
|
|||||||
Assert.check(numberOfFieldRefs == 1);
|
Assert.check(numberOfFieldRefs == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check that fields are initialized in a canonical constructor in the same declaration order as the corresponding
|
// check that fields are initialized in a canonical constructor in the same declaration order as the corresponding
|
||||||
* record component
|
// record component
|
||||||
*/
|
|
||||||
public void testCheckInitializationOrderInCompactConstructor() throws Exception {
|
public void testCheckInitializationOrderInCompactConstructor() throws Exception {
|
||||||
int putField1 = -1;
|
int putField1 = -1;
|
||||||
int putField2 = -1;
|
int putField2 = -1;
|
||||||
@ -1304,179 +1320,180 @@ public class RecordCompilationTests extends CompilationTestCase {
|
|||||||
|
|
||||||
public void testAcceptRecordId() {
|
public void testAcceptRecordId() {
|
||||||
String[] previousOptions = getCompileOptions();
|
String[] previousOptions = getCompileOptions();
|
||||||
String[] testOptions = {/* no options */};
|
try {
|
||||||
setCompileOptions(testOptions);
|
String[] testOptions = {};
|
||||||
assertFail("compiler.err.illegal.start.of.type",
|
setCompileOptions(testOptions);
|
||||||
"class R {\n" +
|
assertFail("compiler.err.illegal.start.of.type",
|
||||||
" record RR(int i) {\n" +
|
"class R {\n" +
|
||||||
" return null;\n" +
|
" record RR(int i) {\n" +
|
||||||
" }\n" +
|
" return null;\n" +
|
||||||
" class record {}\n" +
|
" }\n" +
|
||||||
"}");
|
" class record {}\n" +
|
||||||
setCompileOptions(previousOptions);
|
"}");
|
||||||
|
} finally {
|
||||||
|
setCompileOptions(previousOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAnnos() throws Exception {
|
public void testAnnos() throws Exception {
|
||||||
String[] previousOptions = getCompileOptions();
|
String[] previousOptions = getCompileOptions();
|
||||||
String srcTemplate =
|
try {
|
||||||
"""
|
String srcTemplate =
|
||||||
|
"""
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
@Target({#TARGET})
|
@Target({#TARGET})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@interface Anno { }
|
@interface Anno { }
|
||||||
|
|
||||||
record R(@Anno String s) {}
|
record R(@Anno String s) {}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
// testing several combinations, adding even more combinations won't add too much value
|
// testing several combinations, adding even more combinations won't add too much value
|
||||||
List<String> annoApplicableTargets = List.of(
|
List<String> annoApplicableTargets = List.of(
|
||||||
"ElementType.FIELD",
|
"ElementType.FIELD",
|
||||||
"ElementType.METHOD",
|
"ElementType.METHOD",
|
||||||
"ElementType.PARAMETER",
|
"ElementType.PARAMETER",
|
||||||
"ElementType.RECORD_COMPONENT",
|
"ElementType.RECORD_COMPONENT",
|
||||||
"ElementType.TYPE_USE",
|
"ElementType.TYPE_USE",
|
||||||
"ElementType.TYPE_USE,ElementType.FIELD",
|
"ElementType.TYPE_USE,ElementType.FIELD",
|
||||||
"ElementType.TYPE_USE,ElementType.METHOD",
|
"ElementType.TYPE_USE,ElementType.METHOD",
|
||||||
"ElementType.TYPE_USE,ElementType.PARAMETER",
|
"ElementType.TYPE_USE,ElementType.PARAMETER",
|
||||||
"ElementType.TYPE_USE,ElementType.RECORD_COMPONENT",
|
"ElementType.TYPE_USE,ElementType.RECORD_COMPONENT",
|
||||||
"ElementType.TYPE_USE,ElementType.FIELD,ElementType.METHOD",
|
"ElementType.TYPE_USE,ElementType.FIELD,ElementType.METHOD",
|
||||||
"ElementType.TYPE_USE,ElementType.FIELD,ElementType.PARAMETER",
|
"ElementType.TYPE_USE,ElementType.FIELD,ElementType.PARAMETER",
|
||||||
"ElementType.TYPE_USE,ElementType.FIELD,ElementType.RECORD_COMPONENT",
|
"ElementType.TYPE_USE,ElementType.FIELD,ElementType.RECORD_COMPONENT",
|
||||||
"ElementType.FIELD,ElementType.TYPE_USE",
|
"ElementType.FIELD,ElementType.TYPE_USE",
|
||||||
"ElementType.FIELD,ElementType.CONSTRUCTOR",
|
"ElementType.FIELD,ElementType.CONSTRUCTOR",
|
||||||
"ElementType.FIELD,ElementType.LOCAL_VARIABLE",
|
"ElementType.FIELD,ElementType.LOCAL_VARIABLE",
|
||||||
"ElementType.FIELD,ElementType.ANNOTATION_TYPE",
|
"ElementType.FIELD,ElementType.ANNOTATION_TYPE",
|
||||||
"ElementType.FIELD,ElementType.PACKAGE",
|
"ElementType.FIELD,ElementType.PACKAGE",
|
||||||
"ElementType.FIELD,ElementType.TYPE_PARAMETER",
|
"ElementType.FIELD,ElementType.TYPE_PARAMETER",
|
||||||
"ElementType.FIELD,ElementType.MODULE",
|
"ElementType.FIELD,ElementType.MODULE",
|
||||||
"ElementType.METHOD,ElementType.TYPE_USE",
|
"ElementType.METHOD,ElementType.TYPE_USE",
|
||||||
"ElementType.PARAMETER,ElementType.TYPE_USE",
|
"ElementType.PARAMETER,ElementType.TYPE_USE",
|
||||||
"ElementType.RECORD_COMPONENT,ElementType.TYPE_USE",
|
"ElementType.RECORD_COMPONENT,ElementType.TYPE_USE",
|
||||||
"ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE",
|
"ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE",
|
||||||
"ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE_USE",
|
"ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE_USE",
|
||||||
"ElementType.FIELD,ElementType.RECORD_COMPONENT,ElementType.TYPE_USE"
|
"ElementType.FIELD,ElementType.RECORD_COMPONENT,ElementType.TYPE_USE"
|
||||||
);
|
);
|
||||||
|
|
||||||
String[] generalOptions = {
|
String[] generalOptions = {
|
||||||
"-processor", Processor.class.getName(),
|
"-processor", Processor.class.getName(),
|
||||||
"-Atargets="
|
"-Atargets="
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String target : annoApplicableTargets) {
|
for (String target : annoApplicableTargets) {
|
||||||
String code = srcTemplate.replaceFirst("#TARGET", target);
|
String code = srcTemplate.replaceFirst("#TARGET", target);
|
||||||
String[] testOptions = generalOptions.clone();
|
String[] testOptions = generalOptions.clone();
|
||||||
testOptions[testOptions.length - 1] = testOptions[testOptions.length - 1] + target;
|
testOptions[testOptions.length - 1] = testOptions[testOptions.length - 1] + target;
|
||||||
setCompileOptions(testOptions);
|
setCompileOptions(testOptions);
|
||||||
|
|
||||||
File dir = assertOK(true, code);
|
File dir = assertOK(true, code);
|
||||||
|
|
||||||
ClassFile classFile = ClassFile.read(findClassFileOrFail(dir, "R.class"));
|
ClassFile classFile = ClassFile.read(findClassFileOrFail(dir, "R.class"));
|
||||||
|
|
||||||
// field first
|
// field first
|
||||||
Assert.check(classFile.fields.length == 1);
|
Assert.check(classFile.fields.length == 1);
|
||||||
Field field = classFile.fields[0];
|
Field field = classFile.fields[0];
|
||||||
/* if FIELD is one of the targets then there must be a declaration annotation applied to the field, apart from
|
// if FIELD is one of the targets then there must be a declaration annotation applied to the field, apart from
|
||||||
* the type annotation
|
// the type annotation
|
||||||
*/
|
if (target.contains("ElementType.FIELD")) {
|
||||||
if (target.contains("ElementType.FIELD")) {
|
checkAnno(classFile,
|
||||||
checkAnno(classFile,
|
(RuntimeAnnotations_attribute) findAttributeOrFail(
|
||||||
(RuntimeAnnotations_attribute)findAttributeOrFail(
|
field.attributes,
|
||||||
field.attributes,
|
RuntimeVisibleAnnotations_attribute.class),
|
||||||
RuntimeVisibleAnnotations_attribute.class),
|
"Anno");
|
||||||
"Anno");
|
} else {
|
||||||
} else {
|
assertAttributeNotPresent(field.attributes, RuntimeVisibleAnnotations_attribute.class);
|
||||||
assertAttributeNotPresent(field.attributes, RuntimeVisibleAnnotations_attribute.class);
|
}
|
||||||
|
|
||||||
|
// lets check now for the type annotation
|
||||||
|
if (target.contains("ElementType.TYPE_USE")) {
|
||||||
|
checkTypeAnno(
|
||||||
|
classFile,
|
||||||
|
(RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
|
||||||
|
"FIELD",
|
||||||
|
"Anno");
|
||||||
|
} else {
|
||||||
|
assertAttributeNotPresent(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// checking for the annotation on the corresponding parameter of the canonical constructor
|
||||||
|
Method init = findMethodOrFail(classFile, "<init>");
|
||||||
|
// if PARAMETER is one of the targets then there must be a declaration annotation applied to the parameter, apart from
|
||||||
|
// the type annotation
|
||||||
|
if (target.contains("ElementType.PARAMETER")) {
|
||||||
|
checkParameterAnno(classFile,
|
||||||
|
(RuntimeVisibleParameterAnnotations_attribute) findAttributeOrFail(
|
||||||
|
init.attributes,
|
||||||
|
RuntimeVisibleParameterAnnotations_attribute.class),
|
||||||
|
"Anno");
|
||||||
|
} else {
|
||||||
|
assertAttributeNotPresent(init.attributes, RuntimeVisibleAnnotations_attribute.class);
|
||||||
|
}
|
||||||
|
// let's check now for the type annotation
|
||||||
|
if (target.contains("ElementType.TYPE_USE")) {
|
||||||
|
checkTypeAnno(
|
||||||
|
classFile,
|
||||||
|
(RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
|
||||||
|
"METHOD_FORMAL_PARAMETER", "Anno");
|
||||||
|
} else {
|
||||||
|
assertAttributeNotPresent(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// checking for the annotation in the accessor
|
||||||
|
Method accessor = findMethodOrFail(classFile, "s");
|
||||||
|
// if METHOD is one of the targets then there must be a declaration annotation applied to the accessor, apart from
|
||||||
|
// the type annotation
|
||||||
|
if (target.contains("ElementType.METHOD")) {
|
||||||
|
checkAnno(classFile,
|
||||||
|
(RuntimeAnnotations_attribute) findAttributeOrFail(
|
||||||
|
accessor.attributes,
|
||||||
|
RuntimeVisibleAnnotations_attribute.class),
|
||||||
|
"Anno");
|
||||||
|
} else {
|
||||||
|
assertAttributeNotPresent(accessor.attributes, RuntimeVisibleAnnotations_attribute.class);
|
||||||
|
}
|
||||||
|
// let's check now for the type annotation
|
||||||
|
if (target.contains("ElementType.TYPE_USE")) {
|
||||||
|
checkTypeAnno(
|
||||||
|
classFile,
|
||||||
|
(RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
|
||||||
|
"METHOD_RETURN", "Anno");
|
||||||
|
} else {
|
||||||
|
assertAttributeNotPresent(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// checking for the annotation in the Record attribute
|
||||||
|
Record_attribute record = (Record_attribute) findAttributeOrFail(classFile.attributes, Record_attribute.class);
|
||||||
|
Assert.check(record.component_count == 1);
|
||||||
|
// if RECORD_COMPONENT is one of the targets then there must be a declaration annotation applied to the
|
||||||
|
// field, apart from the type annotation
|
||||||
|
if (target.contains("ElementType.RECORD_COMPONENT")) {
|
||||||
|
checkAnno(classFile,
|
||||||
|
(RuntimeAnnotations_attribute) findAttributeOrFail(
|
||||||
|
record.component_info_arr[0].attributes,
|
||||||
|
RuntimeVisibleAnnotations_attribute.class),
|
||||||
|
"Anno");
|
||||||
|
} else {
|
||||||
|
assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleAnnotations_attribute.class);
|
||||||
|
}
|
||||||
|
// lets check now for the type annotation
|
||||||
|
if (target.contains("ElementType.TYPE_USE")) {
|
||||||
|
checkTypeAnno(
|
||||||
|
classFile,
|
||||||
|
(RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(
|
||||||
|
record.component_info_arr[0].attributes,
|
||||||
|
RuntimeVisibleTypeAnnotations_attribute.class),
|
||||||
|
"FIELD", "Anno");
|
||||||
|
} else {
|
||||||
|
assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lets check now for the type annotation
|
// let's reset the default compiler options for other tests
|
||||||
if (target.contains("ElementType.TYPE_USE")) {
|
} finally {
|
||||||
checkTypeAnno(
|
setCompileOptions(previousOptions);
|
||||||
classFile,
|
|
||||||
(RuntimeVisibleTypeAnnotations_attribute)findAttributeOrFail(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
|
|
||||||
"FIELD",
|
|
||||||
"Anno");
|
|
||||||
} else {
|
|
||||||
assertAttributeNotPresent(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checking for the annotation on the corresponding parameter of the canonical constructor
|
|
||||||
Method init = findMethodOrFail(classFile, "<init>");
|
|
||||||
/* if PARAMETER is one of the targets then there must be a declaration annotation applied to the parameter, apart from
|
|
||||||
* the type annotation
|
|
||||||
*/
|
|
||||||
if (target.contains("ElementType.PARAMETER")) {
|
|
||||||
checkParameterAnno(classFile,
|
|
||||||
(RuntimeVisibleParameterAnnotations_attribute)findAttributeOrFail(
|
|
||||||
init.attributes,
|
|
||||||
RuntimeVisibleParameterAnnotations_attribute.class),
|
|
||||||
"Anno");
|
|
||||||
} else {
|
|
||||||
assertAttributeNotPresent(init.attributes, RuntimeVisibleAnnotations_attribute.class);
|
|
||||||
}
|
|
||||||
// let's check now for the type annotation
|
|
||||||
if (target.contains("ElementType.TYPE_USE")) {
|
|
||||||
checkTypeAnno(
|
|
||||||
classFile,
|
|
||||||
(RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
|
|
||||||
"METHOD_FORMAL_PARAMETER", "Anno");
|
|
||||||
} else {
|
|
||||||
assertAttributeNotPresent(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checking for the annotation in the accessor
|
|
||||||
Method accessor = findMethodOrFail(classFile, "s");
|
|
||||||
/* if METHOD is one of the targets then there must be a declaration annotation applied to the accessor, apart from
|
|
||||||
* the type annotation
|
|
||||||
*/
|
|
||||||
if (target.contains("ElementType.METHOD")) {
|
|
||||||
checkAnno(classFile,
|
|
||||||
(RuntimeAnnotations_attribute)findAttributeOrFail(
|
|
||||||
accessor.attributes,
|
|
||||||
RuntimeVisibleAnnotations_attribute.class),
|
|
||||||
"Anno");
|
|
||||||
} else {
|
|
||||||
assertAttributeNotPresent(accessor.attributes, RuntimeVisibleAnnotations_attribute.class);
|
|
||||||
}
|
|
||||||
// let's check now for the type annotation
|
|
||||||
if (target.contains("ElementType.TYPE_USE")) {
|
|
||||||
checkTypeAnno(
|
|
||||||
classFile,
|
|
||||||
(RuntimeVisibleTypeAnnotations_attribute)findAttributeOrFail(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
|
|
||||||
"METHOD_RETURN", "Anno");
|
|
||||||
} else {
|
|
||||||
assertAttributeNotPresent(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checking for the annotation in the Record attribute
|
|
||||||
Record_attribute record = (Record_attribute)findAttributeOrFail(classFile.attributes, Record_attribute.class);
|
|
||||||
Assert.check(record.component_count == 1);
|
|
||||||
/* if RECORD_COMPONENT is one of the targets then there must be a declaration annotation applied to the
|
|
||||||
* field, apart from the type annotation
|
|
||||||
*/
|
|
||||||
if (target.contains("ElementType.RECORD_COMPONENT")) {
|
|
||||||
checkAnno(classFile,
|
|
||||||
(RuntimeAnnotations_attribute)findAttributeOrFail(
|
|
||||||
record.component_info_arr[0].attributes,
|
|
||||||
RuntimeVisibleAnnotations_attribute.class),
|
|
||||||
"Anno");
|
|
||||||
} else {
|
|
||||||
assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleAnnotations_attribute.class);
|
|
||||||
}
|
|
||||||
// lets check now for the type annotation
|
|
||||||
if (target.contains("ElementType.TYPE_USE")) {
|
|
||||||
checkTypeAnno(
|
|
||||||
classFile,
|
|
||||||
(RuntimeVisibleTypeAnnotations_attribute)findAttributeOrFail(
|
|
||||||
record.component_info_arr[0].attributes,
|
|
||||||
RuntimeVisibleTypeAnnotations_attribute.class),
|
|
||||||
"FIELD", "Anno");
|
|
||||||
} else {
|
|
||||||
assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleTypeAnnotations_attribute.class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// let's reset the default compiler options for other tests
|
|
||||||
setCompileOptions(previousOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkTypeAnno(ClassFile classFile,
|
private void checkTypeAnno(ClassFile classFile,
|
||||||
@ -1655,7 +1672,7 @@ public class RecordCompilationTests extends CompilationTestCase {
|
|||||||
switch (method.getName(classFile.constant_pool)) {
|
switch (method.getName(classFile.constant_pool)) {
|
||||||
case "toString", "equals", "hashCode" ->
|
case "toString", "equals", "hashCode" ->
|
||||||
Assert.check(method.access_flags.is(AccessFlags.ACC_PUBLIC) && method.access_flags.is(AccessFlags.ACC_FINAL));
|
Assert.check(method.access_flags.is(AccessFlags.ACC_PUBLIC) && method.access_flags.is(AccessFlags.ACC_FINAL));
|
||||||
default -> { /* do nothing */ }
|
default -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1963,9 +1980,8 @@ public class RecordCompilationTests extends CompilationTestCase {
|
|||||||
|
|
||||||
public void testNoWarningForSerializableRecords() {
|
public void testNoWarningForSerializableRecords() {
|
||||||
if (!useAP) {
|
if (!useAP) {
|
||||||
/* dont execute this test when the default annotation processor is on as it will fail due to
|
// dont execute this test when the default annotation processor is on as it will fail due to
|
||||||
* spurious warnings
|
// spurious warnings
|
||||||
*/
|
|
||||||
appendCompileOptions("-Werror", "-Xlint:serial");
|
appendCompileOptions("-Werror", "-Xlint:serial");
|
||||||
assertOK(
|
assertOK(
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user