Skip to content

Commit 17d8d26

Browse files
ECJ eats a class annotation if null analysis is performed (eclipse-jdt#4299)
+ fix confusion tagBits vs. extendedTagBits + don't skip annotation resolution when only some have been resolved + don't return incompletely initialized annotations array Fixes eclipse-jdt#4243
1 parent 95b6e3e commit 17d8d26

4 files changed

Lines changed: 68 additions & 6 deletions

File tree

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ public static void resolveAnnotations(BlockScope scope, Annotation[] sourceAnnot
810810
case Binding.FIELD :
811811
case Binding.RECORD_COMPONENT :
812812
case Binding.LOCAL :
813-
if ((recipient.extendedTagBits & ExtendedTagBits.AnnotationResolved) != 0) return annotations;
813+
if (ExtendedTagBits.areAllAnnotationsResolved(recipient.extendedTagBits)) return annotations;
814814
recipient.extendedTagBits |= ExtendedTagBits.AllAnnotationsResolved;
815815
if (length > 0) {
816816
annotations = new AnnotationBinding[length];
@@ -849,7 +849,7 @@ public static void resolveAnnotations(BlockScope scope, Annotation[] sourceAnnot
849849
annotations[j] = annot.getCompilerAnnotation();
850850
}
851851
}
852-
break;
852+
return annotations;
853853
case Binding.LOCAL :
854854
LocalVariableBinding local = (LocalVariableBinding) recipient;
855855
// Note for JDK>=14, this could be LVB or RCB, hence typecasting to VB
@@ -890,9 +890,10 @@ public static void resolveAnnotations(BlockScope scope, Annotation[] sourceAnnot
890890
// copy the se8 annotations.
891891
if (annotationRecipient instanceof RecordComponentBinding && copySE8AnnotationsToType)
892892
copySE8AnnotationsToType(scope, recipient, sourceAnnotations, false);
893-
break;
893+
return annotations;
894+
default:
895+
annotations[i] = annotation.compilerAnnotation;
894896
}
895-
return annotations;
896897
} else {
897898
annotation.recipient = recipient;
898899
annotation.resolveType(scope);
@@ -1354,7 +1355,7 @@ public static void resolveNullDefaultAnnotations(BlockScope scope, Annotation[]
13541355
if (annotations != null) {
13551356
int length;
13561357
if ((length = annotations.length) >= 0) {
1357-
if ((recipient.tagBits & ExtendedTagBits.NullDefaultAnnotationResolved) != 0) return;
1358+
if ((recipient.extendedTagBits & ExtendedTagBits.NullDefaultAnnotationResolved) != 0) return;
13581359
for (int i = 0; i < length; i++) {
13591360
TypeReference annotationTypeRef = annotations[i].type;
13601361
// only resolve type name if 'NonNullByDefault' last token (or corresponding configured annotation name)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,7 @@ public interface ExtendedTagBits {
3636
int DeprecatedAnnotationResolved = ASTNode.Bit7;
3737
int NullDefaultAnnotationResolved = ASTNode.Bit8; // package, type, method or variable
3838
int AllAnnotationsResolved = ExtendedTagBits.AnnotationResolved | ExtendedTagBits.DeprecatedAnnotationResolved | ExtendedTagBits.NullDefaultAnnotationResolved;
39+
static boolean areAllAnnotationsResolved(long extendedTagBits) {
40+
return (extendedTagBits & AllAnnotationsResolved) == AllAnnotationsResolved;
41+
}
3942
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,7 @@ public long getAnnotationTagBits() {
11351135
if (!isPrototype())
11361136
return this.prototype.getAnnotationTagBits();
11371137

1138-
if ((this.extendedTagBits & ExtendedTagBits.AnnotationResolved) == 0 && this.scope != null) {
1138+
if (!ExtendedTagBits.areAllAnnotationsResolved(this.extendedTagBits) && this.scope != null) {
11391139
TypeDeclaration typeDecl = this.scope.referenceContext;
11401140
boolean old = typeDecl.staticInitializerScope.insideTypeAnnotation;
11411141
try {

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12057,4 +12057,62 @@ public void testIssue4107() {
1205712057
null,
1205812058
true);
1205912059
}
12060+
public void testGH4243() throws Exception {
12061+
if (this.complianceLevel < ClassFileConstants.JDK16) return; // uses records
12062+
Runner runner = new Runner();
12063+
runner.customOptions = getCompilerOptions();
12064+
runner.customOptions.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED);
12065+
runner.customOptions.put(JavaCore.COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_NAME, "p.nonnullbydefault");
12066+
runner.testFiles = new String[] {
12067+
"p/Main.java",
12068+
"""
12069+
package p;
12070+
import java.lang.annotation.*;
12071+
12072+
@nonnullbydefault
12073+
@Main.NeededAnnotation
12074+
public final class Main {
12075+
12076+
public static void main(String[] args) {
12077+
for (Annotation anno : Main.class.getAnnotations()) {
12078+
System.out.println(anno);
12079+
}
12080+
}
12081+
12082+
public record Foo(String needed) {}
12083+
12084+
@Retention(RetentionPolicy.RUNTIME)
12085+
@interface NeededAnnotation {}
12086+
}
12087+
""",
12088+
"p/nonnullbydefault.java",
12089+
"""
12090+
package p;
12091+
public @interface nonnullbydefault {}
12092+
""",
12093+
};
12094+
runner.expectedOutputString = "@p.Main.NeededAnnotation()"; // the other annotation is not runtime visible
12095+
runner.runConformTest();
12096+
12097+
ClassFileBytesDisassembler disassembler = ToolFactory.createDefaultClassFileBytesDisassembler();
12098+
byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(
12099+
new File(OUTPUT_DIR + File.separator + "p"+ File.separator + "Main.class"));
12100+
String actualOutput =
12101+
disassembler.disassemble(
12102+
classFileBytes,
12103+
"\n",
12104+
ClassFileBytesDisassembler.DETAILED);
12105+
12106+
String expectedOutput =
12107+
"""
12108+
@p.nonnullbydefault
12109+
@p.Main.NeededAnnotation
12110+
public final class p.Main {
12111+
""";
12112+
12113+
if (actualOutput.indexOf(expectedOutput) == -1) {
12114+
System.out.println(org.eclipse.jdt.core.tests.util.Util.displayString(actualOutput, 2));
12115+
}
12116+
assertTrue("unexpected bytecode sequence", actualOutput.indexOf(expectedOutput) != -1);
12117+
}
1206012118
}

0 commit comments

Comments
 (0)