Skip to content

Commit 88859c2

Browse files
authored
Annotation processor regression in 2025-12 #4687 (#4690)
Ensure the method() is invoked and all methods are initialized before being used for both BinaryTypeBinding and SourceTypeBinding.
1 parent b0135c9 commit 88859c2

File tree

11 files changed

+185
-8
lines changed

11 files changed

+185
-8
lines changed

org.eclipse.jdt.compiler.apt.tests/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %pluginName
44
Bundle-SymbolicName: org.eclipse.jdt.compiler.apt.tests;singleton:=true
5-
Bundle-Version: 1.3.1000.qualifier
5+
Bundle-Version: 1.3.1100.qualifier
66
Bundle-Vendor: %providerName
77
Bundle-Localization: plugin
88
Bundle-RequiredExecutionEnvironment: JavaSE-21
2.61 KB
Binary file not shown.

org.eclipse.jdt.compiler.apt.tests/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<relativePath>../tests-pom/</relativePath>
2020
</parent>
2121
<artifactId>org.eclipse.jdt.compiler.apt.tests</artifactId>
22-
<version>1.3.1000-SNAPSHOT</version>
22+
<version>1.3.1100-SNAPSHOT</version>
2323
<packaging>eclipse-test-plugin</packaging>
2424

2525
<properties>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.eclipse.jdt.compiler.apt.tests.annotations;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Target;
6+
7+
@Documented
8+
@Target(ElementType.TYPE)
9+
public @interface Generated {
10+
String from() default "";
11+
String generator() default "";
12+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.eclipse.jdt.compiler.apt.tests.annotations;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Target;
6+
7+
public @interface Value {
8+
@Documented
9+
@Target(ElementType.TYPE)
10+
@interface Immutable {
11+
12+
boolean singleton() default false;
13+
14+
boolean intern() default false;
15+
16+
boolean copy() default true;
17+
18+
boolean prehash() default false;
19+
20+
boolean lazyhash() default false;
21+
22+
boolean builder() default true;
23+
}
24+
@Target(ElementType.TYPE)
25+
@interface Style {
26+
String[] get() default "get*";
27+
String init() default "*";
28+
}
29+
@Target(ElementType.TYPE)
30+
@interface Builder {}
31+
}

org.eclipse.jdt.compiler.apt.tests/processors/org/eclipse/jdt/compiler/apt/tests/processors/genclass/GenClassProc.java

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.io.IOException;
1818
import java.io.PrintWriter;
1919
import java.io.Writer;
20+
import java.util.List;
2021
import java.util.Map;
2122
import java.util.Set;
2223
import javax.annotation.processing.AbstractProcessor;
@@ -31,19 +32,25 @@
3132
import javax.lang.model.element.AnnotationValue;
3233
import javax.lang.model.element.Element;
3334
import javax.lang.model.element.ExecutableElement;
35+
import javax.lang.model.element.RecordComponentElement;
3436
import javax.lang.model.element.TypeElement;
3537
import javax.lang.model.util.Elements;
3638
import javax.tools.Diagnostic;
3739
import javax.tools.JavaFileObject;
3840

39-
@SupportedAnnotationTypes("org.eclipse.jdt.compiler.apt.tests.annotations.GenClass")
41+
@SupportedAnnotationTypes({"org.eclipse.jdt.compiler.apt.tests.annotations.GenClass",
42+
"org.eclipse.jdt.compiler.apt.tests.annotations.Value.Immutable",
43+
"org.eclipse.jdt.compiler.apt.tests.annotations.Value.Style",
44+
"org.eclipse.jdt.compiler.apt.tests.annotations.Generated",})
4045
@SupportedSourceVersion(SourceVersion.RELEASE_6)
4146
public class GenClassProc extends AbstractProcessor {
4247

4348
private Messager _messager;
4449
private Filer _filer;
4550
private Elements _elementUtil;
4651
private TypeElement _annoDecl;
52+
private TypeElement _immutableDecl;
53+
private TypeElement _styleDecl;
4754

4855
@Override
4956
public synchronized void init(ProcessingEnvironment processingEnv) {
@@ -52,6 +59,8 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
5259
_messager = processingEnv.getMessager();
5360
_elementUtil = processingEnv.getElementUtils();
5461
_annoDecl = _elementUtil.getTypeElement("org.eclipse.jdt.compiler.apt.tests.annotations.GenClass");
62+
_immutableDecl = _elementUtil.getTypeElement("org.eclipse.jdt.compiler.apt.tests.annotations.Value.Immutable");
63+
_styleDecl = _elementUtil.getTypeElement("org.eclipse.jdt.compiler.apt.tests.annotations.Value.Style");
5564

5665
//System.out.println("Processor options are: ");
5766
//for (Map.Entry<String, String> option : processingEnv.getOptions().entrySet()) {
@@ -62,10 +71,35 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
6271
@Override
6372
public boolean process(Set<? extends TypeElement> annotations,
6473
RoundEnvironment roundEnv) {
65-
6674
if (annotations == null || annotations.isEmpty()) {
6775
return true;
6876
}
77+
78+
if (annotations.contains(_immutableDecl)) {
79+
List<? extends Element> enclosedElements = _styleDecl.getEnclosedElements();
80+
int size = enclosedElements.size();
81+
if (size > 0) {
82+
processGH4687_1();
83+
return true;
84+
}
85+
return false;
86+
}
87+
if (annotations.contains(_styleDecl)) {
88+
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(_styleDecl);
89+
TypeElement brokenRecord = null;
90+
for (Element element : elementsAnnotatedWith) {
91+
if (element instanceof TypeElement te && te.getSimpleName().toString().equals("BrokenRecord")) {
92+
brokenRecord = te;
93+
break;
94+
}
95+
}
96+
if (brokenRecord == null) {
97+
throw new IllegalArgumentException("TypeElement not found for targets.gh4687.Container.BrokenRecord");
98+
}
99+
processGH4687_2(brokenRecord);
100+
return true;
101+
}
102+
69103
// sanity check
70104
if (!annotations.contains(_annoDecl)) {
71105
throw new IllegalArgumentException("process() called on an unexpected set of annotations");
@@ -116,7 +150,62 @@ public boolean process(Set<? extends TypeElement> annotations,
116150
}
117151
return true;
118152
}
153+
private void processGH4687_1() {
154+
createSourceFile1(null, "foobar.ImmutableComp", "toString");
155+
}
156+
private void processGH4687_2(TypeElement brokenRecord) {
157+
List<? extends RecordComponentElement> recordComponents = brokenRecord.getRecordComponents();
158+
RecordComponentElement rce = null;
159+
for (Element element : recordComponents) {
160+
if (element instanceof RecordComponentElement) {
161+
rce = (RecordComponentElement) element;
162+
break;
163+
}
164+
}
165+
if (rce == null) {
166+
throw new IllegalArgumentException("RecordComponentElement not found");
167+
}
168+
ExecutableElement accessor = rce.getAccessor();
169+
if (accessor == null) {
170+
throw new IllegalArgumentException("Accessor not found");
171+
}
172+
createSourceFile2(null, "foobar.WorkingRecordBuilder", "toString");
173+
}
119174

175+
private void createSourceFile1(Element parent, String clazz, String method) {
176+
int lastDot = clazz.lastIndexOf('.');
177+
if (lastDot <= 0 || clazz.length() == lastDot)
178+
return;
179+
try {
180+
JavaFileObject jfo = _filer.createSourceFile(clazz, parent);
181+
String source = """
182+
package foobar;
183+
public final class ImmutableComp {}
184+
""";
185+
try (Writer w = jfo.openWriter(); PrintWriter pw = new PrintWriter(w)) {
186+
pw.println(source.toString());
187+
}
188+
} catch (IOException e) {
189+
e.printStackTrace();
190+
}
191+
}
192+
private void createSourceFile2(Element parent, String clazz, String method) {
193+
int lastDot = clazz.lastIndexOf('.');
194+
if (lastDot <= 0 || clazz.length() == lastDot)
195+
return;
196+
try {
197+
JavaFileObject jfo = _filer.createSourceFile(clazz, parent);
198+
String source = """
199+
package foobar;
200+
public final class WorkingRecordBuilder {}
201+
""";
202+
try (Writer w = jfo.openWriter(); PrintWriter pw = new PrintWriter(w)) {
203+
pw.println(source.toString());
204+
}
205+
} catch (IOException e) {
206+
e.printStackTrace();
207+
}
208+
}
120209
/**
121210
* Create a source file named 'name', with contents
122211
* that reflect 'method' and 'name'.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package targets.gh4687;
2+
3+
import org.eclipse.jdt.compiler.apt.tests.annotations.Value;
4+
5+
@Value.Immutable
6+
//@Value.Style
7+
public class Comp {
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package targets.gh4687;
2+
import org.eclipse.jdt.compiler.apt.tests.annotations.Value;
3+
4+
interface Container {
5+
6+
@Value.Style
7+
@Value.Builder
8+
public static record BrokenRecord(String member) {
9+
}
10+
11+
}

org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/BatchDispatchTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,32 @@ public void testCompilerOneClassWithSystemCompiler() throws IOException {
118118
internalTestGenerateClass(compiler);
119119
}
120120

121+
/**
122+
* Read annotation values and generate a class using Eclipse compiler
123+
*/
124+
public void testGhIssue4687WithEclipseCompiler() throws IOException {
125+
JavaCompiler compiler = BatchTestUtils.getEclipseCompiler();
126+
File targetFolder = TestUtils.concatPath(BatchTestUtils.getSrcFolderName(), "targets", "gh4687");
127+
File inputFile = BatchTestUtils.copyResource("targets/gh4687/Comp.java", targetFolder);
128+
assertNotNull("No input file", inputFile);
129+
130+
List<String> options = new ArrayList<>();
131+
BatchTestUtils.compileOneClass(compiler, options, inputFile);
132+
133+
// check that the gen-src and class files were generated
134+
File genSrcFile = TestUtils.concatPath(BatchTestUtils.getGenFolderName(), "foobar", "ImmutableComp.java");
135+
assertTrue("generated src file does not exist", genSrcFile.exists());
136+
137+
inputFile = BatchTestUtils.copyResource("targets/gh4687/Container.java", targetFolder);
138+
assertNotNull("No input file", inputFile);
139+
140+
options = new ArrayList<>();
141+
BatchTestUtils.compileOneClass(compiler, options, inputFile);
142+
// check that the gen-src and class files were generated
143+
genSrcFile = TestUtils.concatPath(BatchTestUtils.getGenFolderName(), "foobar", "WorkingRecordBuilder.java");
144+
assertTrue("generated src file does not exist", genSrcFile.exists());
145+
}
146+
121147
/**
122148
* Read annotation values and generate a class using Eclipse compiler
123149
*/

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,10 +1771,10 @@ private ReferenceBinding[] maybeSortedMemberTypes() {
17711771
* @return the methods in the original order
17721772
*/
17731773
public MethodBinding[] methodsInOriginalOrder() {
1774-
if (this.methodsInOriginalOrder != null) {
1775-
return this.methodsInOriginalOrder;
1774+
if ((this.tagBits & TagBits.AreMethodsComplete) == 0) {
1775+
methods();
17761776
}
1777-
return this.methods;
1777+
return this.methodsInOriginalOrder == null ? this.methods : this.methodsInOriginalOrder;
17781778
}
17791779
// NOTE: the return type, arg & exception types of each method of a binary type are resolved when needed
17801780
@Override

0 commit comments

Comments
 (0)