Skip to content

Commit 6b7b324

Browse files
committed
8351431: Type annotations on new class creation expressions can't be retrieved
Reviewed-by: vromero
1 parent 64b691a commit 6b7b324

File tree

3 files changed

+159
-7
lines changed

3 files changed

+159
-7
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2496,7 +2496,6 @@ public void visitReturn(JCReturn tree) {
24962496
attribExpr(tree.expr, env, env.info.yieldResult.pt);
24972497
}
24982498
} else if (!env.info.isLambda &&
2499-
!env.info.isNewClass &&
25002499
env.enclMethod != null &&
25012500
TreeInfo.isCompactConstructor(env.enclMethod)) {
25022501
log.error(env.enclMethod,
@@ -2782,12 +2781,12 @@ public void visitNewClass(final JCNewClass tree) {
27822781
Type clazztype;
27832782

27842783
try {
2785-
env.info.isNewClass = true;
2784+
env.info.isAnonymousNewClass = tree.def != null;
27862785
clazztype = TreeInfo.isEnumInit(env.tree) ?
27872786
attribIdentAsEnumType(env, (JCIdent)clazz) :
27882787
attribType(clazz, env);
27892788
} finally {
2790-
env.info.isNewClass = false;
2789+
env.info.isAnonymousNewClass = false;
27912790
}
27922791

27932792
clazztype = chk.checkDiamond(tree, clazztype);
@@ -5239,7 +5238,7 @@ public void visitAnnotatedType(JCAnnotatedType tree) {
52395238
Type underlyingType = attribType(tree.underlyingType, env);
52405239
Type annotatedType = underlyingType.preannotatedType();
52415240

5242-
if (!env.info.isNewClass)
5241+
if (!env.info.isAnonymousNewClass)
52435242
annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType);
52445243
result = tree.type = annotatedType;
52455244
}

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ public class AttrContext {
8080
boolean isAnonymousDiamond = false;
8181

8282
/**
83-
* Is this an attribution environment for an instance creation expression?
83+
* Is this an attribution environment for an anonymous instance creation expression?
8484
*/
85-
boolean isNewClass = false;
85+
boolean isAnonymousNewClass = false;
8686

8787
/** Indicate if the type being visited is a service implementation
8888
*/
@@ -149,7 +149,7 @@ AttrContext dup(WriteableScope scope) {
149149
info.isSerializableLambda = isSerializableLambda;
150150
info.attributionMode = attributionMode;
151151
info.isAnonymousDiamond = isAnonymousDiamond;
152-
info.isNewClass = isNewClass;
152+
info.isAnonymousNewClass = isAnonymousNewClass;
153153
info.preferredTreeForDiagnostics = preferredTreeForDiagnostics;
154154
info.visitingServiceImplementation = visitingServiceImplementation;
155155
info.allowProtectedAccess = allowProtectedAccess;
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright (c) 2025, Google LLC. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8351431
27+
* @summary Type annotations on new class creation expressions can't be retrieved
28+
* @library /tools/lib
29+
* @build toolbox.ToolBox toolbox.JavacTask
30+
* @modules jdk.compiler/com.sun.tools.javac.api jdk.compiler/com.sun.tools.javac.main
31+
* @run main NewClassTypeAnnotation
32+
*/
33+
import com.sun.source.tree.NewClassTree;
34+
import com.sun.source.tree.Tree;
35+
import com.sun.source.util.TaskEvent;
36+
import com.sun.source.util.TaskListener;
37+
import com.sun.source.util.TreePathScanner;
38+
import com.sun.source.util.Trees;
39+
40+
import toolbox.JavacTask;
41+
import toolbox.Task;
42+
import toolbox.TestRunner;
43+
import toolbox.ToolBox;
44+
45+
import java.nio.file.Files;
46+
import java.nio.file.Path;
47+
import java.nio.file.Paths;
48+
import java.util.ArrayList;
49+
import java.util.List;
50+
import java.util.concurrent.atomic.AtomicBoolean;
51+
52+
import javax.lang.model.type.TypeMirror;
53+
54+
public class NewClassTypeAnnotation extends TestRunner {
55+
56+
private ToolBox tb;
57+
58+
public static void main(String[] args) throws Exception {
59+
new NewClassTypeAnnotation().runTests();
60+
}
61+
62+
NewClassTypeAnnotation() {
63+
super(System.err);
64+
tb = new ToolBox();
65+
}
66+
67+
public void runTests() throws Exception {
68+
runTests(m -> new Object[] {Paths.get(m.getName())});
69+
}
70+
71+
@Test
72+
public void testTypeAnnotations(Path base) throws Exception {
73+
Path current = base.resolve(".");
74+
Path src = current.resolve("src");
75+
Path classes = current.resolve("classes");
76+
tb.writeJavaFiles(
77+
src,
78+
"""
79+
package test;
80+
81+
import java.lang.annotation.ElementType;
82+
import java.lang.annotation.Retention;
83+
import java.lang.annotation.RetentionPolicy;
84+
import java.lang.annotation.Target;
85+
86+
class Test<T> {
87+
88+
@Target(ElementType.TYPE_USE)
89+
@Retention(RetentionPolicy.RUNTIME)
90+
@interface TypeAnnotation {}
91+
92+
public void testMethod() {
93+
new Test<@TypeAnnotation String>();
94+
}
95+
}
96+
""");
97+
98+
Files.createDirectories(classes);
99+
100+
AtomicBoolean seenAnnotationMirror = new AtomicBoolean();
101+
102+
List<String> actual = new ArrayList<>();
103+
104+
class Scanner extends TreePathScanner<Void, Void> {
105+
106+
private final Trees trees;
107+
108+
Scanner(Trees trees) {
109+
this.trees = trees;
110+
}
111+
112+
@Override
113+
public Void visitNewClass(final NewClassTree node, final Void unused) {
114+
TypeMirror type = trees.getTypeMirror(getCurrentPath());
115+
System.err.println(">>> " + type);
116+
for (Tree t : getCurrentPath()) {
117+
System.err.println(t);
118+
}
119+
actual.add(String.format("Expression: %s, Type: %s", node, type));
120+
return null;
121+
}
122+
}
123+
124+
new JavacTask(tb)
125+
.outdir(classes)
126+
.callback(
127+
task -> {
128+
task.addTaskListener(
129+
new TaskListener() {
130+
@Override
131+
public void finished(TaskEvent e) {
132+
if (e.getKind() != TaskEvent.Kind.ANALYZE) {
133+
return;
134+
}
135+
System.err.println(e);
136+
new Scanner(Trees.instance(task))
137+
.scan(e.getCompilationUnit(), null);
138+
}
139+
});
140+
})
141+
.files(tb.findJavaFiles(src))
142+
.run(Task.Expect.SUCCESS)
143+
.writeAll();
144+
145+
List<String> expected =
146+
List.of(
147+
"Expression: new Test<@TypeAnnotation String>(), Type:"
148+
+ " test.Test<[email protected] String>");
149+
if (!expected.equals(actual)) {
150+
throw new AssertionError("expected: " + expected + ", actual: " + actual);
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)