Skip to content

Commit c49768f

Browse files
ECJ fails on Record used within Enum in Annotation (eclipse-jdt#4574)
* Fixes eclipse-jdt#4551
1 parent 1e0f58b commit c49768f

7 files changed

Lines changed: 95 additions & 24 deletions

File tree

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -669,8 +669,10 @@ public void addFieldInfos() {
669669
FieldBinding[] syntheticFields = currentBinding.syntheticFields();
670670
if (syntheticFields != null) {
671671
for (FieldBinding syntheticField : syntheticFields) {
672-
addFieldInfo(syntheticField);
673-
fieldCount++;
672+
if (syntheticField.type != null) { // complained already if null, skip field in problem type
673+
addFieldInfo(syntheticField);
674+
fieldCount++;
675+
}
674676
}
675677
}
676678
// write the number of fields

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Binding.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public abstract class Binding {
7272
public static final RecordComponentBinding[] NO_COMPONENTS = new RecordComponentBinding[0];
7373
public static final FieldBinding[] UNINITIALIZED_FIELDS = new FieldBinding[0];
7474
public static final MethodBinding[] UNINITIALIZED_METHODS = new MethodBinding[0];
75+
public static final RecordComponentBinding[] UNINITIALIZED_COMPONENTS = new RecordComponentBinding[0];
7576
public static final ReferenceBinding[] UNINITIALIZED_REFERENCE_TYPES = new ReferenceBinding[0];
7677

7778
static final InferenceVariable[] NO_INFERENCE_VARIABLES = new InferenceVariable[0];

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, Reference
139139
}
140140
anonymousType.tagBits |= TagBits.EndHierarchyCheck;
141141
connectMemberTypes();
142-
collateRecordComponents();
142+
buildComponents();
143143
buildFieldsAndMethods();
144144
anonymousType.faultInTypesForFieldsAndMethods();
145145
anonymousType.verifyMethods(environment().methodVerifier());
@@ -309,7 +309,7 @@ void buildLocalTypeBinding(SourceTypeBinding enclosingType) {
309309
checkParameterizedTypeBounds();
310310
checkParameterizedSuperTypeCollisions();
311311
this.referenceContext.updateSupertypesWithAnnotations(Collections.emptyMap());
312-
collateRecordComponents();
312+
buildComponents();
313313
buildFieldsAndMethods();
314314
localType.faultInTypesForFieldsAndMethods();
315315

@@ -1348,13 +1348,14 @@ private boolean connectSuperInterfaces() {
13481348
return noProblems;
13491349
}
13501350

1351-
void collateRecordComponents() {
1351+
void buildComponents() {
13521352
SourceTypeBinding sourceType = this.referenceContext.binding;
1353-
if (sourceType.components() == null) {
1354-
sourceType.setComponents(Binding.NO_COMPONENTS);
1353+
if (!sourceType.areComponentsInitialized()) {
13551354
RecordComponent[] components = this.referenceContext.recordComponents;
13561355
int length = components.length;
13571356
RecordComponentBinding[] rcbs = length == 0 ? Binding.NO_COMPONENTS : new RecordComponentBinding[length];
1357+
if (length != 0)
1358+
sourceType.tagBits |= TagBits.HasUnresolvedComponents;
13581359
HashMap<String, RecordComponentBinding> knownComponents = new HashMap<>(length);
13591360
int count = 0;
13601361
for (RecordComponent component : components) {
@@ -1370,18 +1371,17 @@ void collateRecordComponents() {
13701371
component.binding = null;
13711372
} else {
13721373
knownComponents.put(name, rcb);
1373-
if (sourceType.resolveTypeFor(rcb) != null)
1374-
rcbs[count++] = rcb;
1374+
rcbs[count++] = rcb;
13751375
}
13761376
}
13771377
if (count != rcbs.length) // remove duplicate or broken components
13781378
System.arraycopy(rcbs, 0, rcbs = count == 0 ? Binding.NO_COMPONENTS : new RecordComponentBinding[count], 0, count);
1379-
sourceType.setComponents(rcbs);
1379+
sourceType.components = rcbs;
13801380
}
13811381
ReferenceBinding[] memberTypes = sourceType.memberTypes;
13821382
if (memberTypes != null) {
13831383
for (ReferenceBinding memberType : memberTypes)
1384-
((SourceTypeBinding) memberType).scope.collateRecordComponents();
1384+
((SourceTypeBinding) memberType).scope.buildComponents();
13851385
}
13861386
}
13871387

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,9 @@ void sealTypeHierarchy() {
407407
sourceType.scope.connectPermittedTypes();
408408
}
409409
}
410-
void collateRecordComponents() {
410+
void buildComponents() {
411411
for (SourceTypeBinding sourceType : this.topLevelTypes) {
412-
sourceType.scope.collateRecordComponents();
412+
sourceType.scope.buildComponents();
413413
}
414414
}
415415
void integrateAnnotationsInHierarchy() {

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ void perform(CompilationUnitScope scope) {
206206
case CHECK_AND_SET_IMPORTS -> scope.checkAndSetImports();
207207
case CONNECT_TYPE_HIERARCHY -> scope.connectTypeHierarchy();
208208
case SEAL_TYPE_HIERARCHY -> scope.sealTypeHierarchy();
209-
case COLLATE_RECORD_COMPONENTS -> scope.collateRecordComponents();
209+
case COLLATE_RECORD_COMPONENTS -> scope.buildComponents();
210210
case BUILD_FIELDS_AND_METHODS -> scope.buildFieldsAndMethods();
211211
case INTEGRATE_ANNOTATIONS_IN_HIERARCHY -> scope.integrateAnnotationsInHierarchy();
212212
case CHECK_PARAMETERIZED_TYPES -> scope.checkParameterizedTypes();

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public class SourceTypeBinding extends ReferenceBinding {
7575
public ReferenceBinding superclass; // MUST NOT be modified directly, use setter !
7676
public ReferenceBinding[] superInterfaces; // MUST NOT be modified directly, use setter !
7777
private FieldBinding[] fields; // MUST NOT be modified directly, use setter !
78-
private RecordComponentBinding[] components; // MUST NOT be modified directly, use setter !
78+
RecordComponentBinding[] components; // MUST NOT be modified directly, use setter !
7979
private MethodBinding[] methods; // MUST NOT be modified directly, use setter !
8080
public ReferenceBinding[] memberTypes; // MUST NOT be modified directly, use setter !
8181
public TypeVariableBinding[] typeVariables; // MUST NOT be modified directly, use setter !
@@ -121,6 +121,7 @@ public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassSc
121121
// expect the fields & methods to be initialized correctly later
122122
this.fields = Binding.UNINITIALIZED_FIELDS;
123123
this.methods = Binding.UNINITIALIZED_METHODS;
124+
this.components = this.isRecord() ? Binding.UNINITIALIZED_COMPONENTS : NO_COMPONENTS;
124125
this.prototype = this;
125126
this.isImplicit = scope.referenceContext.isImplicitType();
126127
computeId();
@@ -138,6 +139,7 @@ public SourceTypeBinding(SourceTypeBinding prototype) {
138139
this.permittedTypes = prototype.permittedTypes;
139140
this.fields = prototype.fields;
140141
this.methods = prototype.methods;
142+
this.components = prototype.components;
141143
this.memberTypes = prototype.memberTypes;
142144
this.typeVariables = prototype.typeVariables;
143145
this.environment = prototype.environment;
@@ -757,6 +759,11 @@ public SyntheticMethodBinding addSyntheticRecordOverrideMethod(char[] selector)
757759
}
758760
return accessMethod;
759761
}
762+
boolean areComponentsInitialized() {
763+
if (!isPrototype())
764+
return this.prototype.areComponentsInitialized();
765+
return this.components != Binding.UNINITIALIZED_COMPONENTS;
766+
}
760767
boolean areFieldsInitialized() {
761768
if (!isPrototype())
762769
return this.prototype.areFieldsInitialized();
@@ -912,7 +919,21 @@ public RecordComponentBinding[] components() {
912919
if (!isPrototype()) {
913920
return this.components = this.prototype.components();
914921
}
915-
return this.components;
922+
if ((this.tagBits & TagBits.HasUnresolvedComponents) == 0)
923+
return this.components;
924+
925+
int length = this.components.length;
926+
int count = 0;
927+
RecordComponentBinding[] rcbs = length == 0 ? Binding.NO_COMPONENTS : new RecordComponentBinding[length];
928+
for (int i = 0; i < length; i++) {
929+
if (resolveTypeFor(this.components[i]) != null) {
930+
rcbs[count++] = this.components[i];
931+
}
932+
}
933+
if (count != rcbs.length) // remove duplicate or broken components
934+
System.arraycopy(rcbs, 0, rcbs = count == 0 ? Binding.NO_COMPONENTS : new RecordComponentBinding[count], 0, count);
935+
this.tagBits &= ~TagBits.HasUnresolvedComponents;
936+
return setComponents(rcbs);
916937
}
917938

918939
private VariableBinding resolveTypeFor(VariableBinding variable) {
@@ -923,6 +944,11 @@ private VariableBinding resolveTypeFor(VariableBinding variable) {
923944
if ((variable.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0)
924945
return variable;
925946

947+
if ((variable.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0)
948+
variable.modifiers |= ClassFileConstants.AccDeprecated;
949+
if (hasRestrictedAccess())
950+
variable.modifiers |= ExtraCompilerModifiers.AccRestrictedAccess;
951+
926952
MethodScope initializationScope = variable.isStatic()
927953
? this.scope.referenceContext.staticInitializerScope
928954
: this.scope.referenceContext.initializerScope;
@@ -931,7 +957,6 @@ private VariableBinding resolveTypeFor(VariableBinding variable) {
931957
if (variable instanceof FieldBinding field)
932958
initializationScope.initializedField = field;
933959
AbstractVariableDeclaration variableDeclaration = variable instanceof FieldBinding field ? field.sourceField() : ((RecordComponentBinding) variable).sourceRecordComponent();
934-
ASTNode.resolveNullDefaultAnnotations(initializationScope, variableDeclaration.annotations, variable);
935960
TypeBinding variableType =
936961
variableDeclaration.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT
937962
? initializationScope.environment().convertToRawType(this, false /*do not force conversion of enclosing types*/) // enum constant is implicitly of declaring enum type
@@ -960,11 +985,6 @@ private VariableBinding resolveTypeFor(VariableBinding variable) {
960985
variable.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
961986
}
962987

963-
if ((variable.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0)
964-
variable.modifiers |= ClassFileConstants.AccDeprecated;
965-
if (hasRestrictedAccess())
966-
variable.modifiers |= ExtraCompilerModifiers.AccRestrictedAccess;
967-
968988
Annotation [] annotations = variableDeclaration.annotations;
969989

970990
if (variableDeclaration instanceof RecordComponent componentDeclaration) {
@@ -1046,6 +1066,9 @@ public FieldBinding[] fields() {
10461066
if ((this.tagBits & TagBits.AreFieldsComplete) != 0)
10471067
return this.fields;
10481068

1069+
if ((this.tagBits & TagBits.HasUnresolvedComponents) != 0)
1070+
components();
1071+
10491072
int failed = 0;
10501073
FieldBinding[] resolvedFields = this.fields;
10511074
try {
@@ -1368,6 +1391,9 @@ public FieldBinding getField(char[] fieldName, boolean needResolve) {
13681391
if ((this.tagBits & TagBits.AreFieldsComplete) != 0)
13691392
return ReferenceBinding.binarySearch(fieldName, this.fields);
13701393

1394+
if ((this.tagBits & TagBits.HasUnresolvedComponents) != 0)
1395+
components();
1396+
13711397
// lazily sort fields
13721398
if ((this.tagBits & TagBits.AreFieldsSorted) == 0) {
13731399
int length = this.fields.length;
@@ -1563,7 +1589,7 @@ void initializeForStaticImports() {
15631589

15641590
if (this.superInterfaces == null)
15651591
this.scope.connectTypeHierarchy();
1566-
this.scope.collateRecordComponents();
1592+
this.scope.buildComponents();
15671593
this.scope.buildFields();
15681594
this.scope.buildMethods();
15691595
}
@@ -1721,6 +1747,9 @@ public MethodBinding[] methods() {
17211747
this.scope.buildMethods();
17221748
}
17231749

1750+
if ((this.tagBits & TagBits.HasUnresolvedComponents) != 0)
1751+
components();
1752+
17241753
// lazily sort methods
17251754
if ((this.tagBits & TagBits.AreMethodsSorted) == 0) {
17261755
int length = this.methods.length;

org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10942,7 +10942,7 @@ public static void main(String[] args) {
1094210942

1094310943
}
1094410944
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4412
10945-
// Internal Compile error problem since 2025-09
10945+
// Internal Compile error problem since 2025-09
1094610946
public void testIssue4412() throws Exception {
1094710947
this.runConformTest(
1094810948
new String[] {
@@ -11110,4 +11110,43 @@ The method i() from the type R is deprecated
1111011110
""";
1111111111
runner.runWarningTest();
1111211112
}
11113+
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4551
11114+
// ECJ fails on Record used within Enum in Annotation
11115+
public void testIssue4551() throws Exception {
11116+
this.runNegativeTest(
11117+
new String[] {
11118+
"X.java",
11119+
"""
11120+
public record X(
11121+
@ExampleAnnotation(value = { ExampleEnum.VALUE })
11122+
String string) {
11123+
11124+
@Target(ElementType.FIELD)
11125+
public @interface ExampleAnnotation {
11126+
ExampleEnum[] value();
11127+
}
11128+
}
11129+
11130+
enum ExampleEnum {
11131+
VALUE(new SecondExampleRecord());
11132+
11133+
private ExampleEnum(SecondExampleRecord exampleRecord) { }
11134+
}
11135+
11136+
record SecondExampleRecord() {
11137+
}
11138+
""",
11139+
},
11140+
"----------\n" +
11141+
"1. ERROR in X.java (at line 5)\r\n" +
11142+
" @Target(ElementType.FIELD)\r\n" +
11143+
" ^^^^^^\n" +
11144+
"Target cannot be resolved to a type\n" +
11145+
"----------\n" +
11146+
"2. ERROR in X.java (at line 5)\r\n" +
11147+
" @Target(ElementType.FIELD)\r\n" +
11148+
" ^^^^^^^^^^^\n" +
11149+
"ElementType cannot be resolved to a variable\n" +
11150+
"----------\n");
11151+
}
1111311152
}

0 commit comments

Comments
 (0)