1 /*
2 * Copyright (c) 2019, 2022, Oracle and/or its affiliates. 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 * RecordCompilationTests
26 *
27 * @test
28 * @bug 8250629 8252307 8247352 8241151 8246774 8259025 8288130 8282714 8289647 8294020
29 * @summary Negative compilation tests, and positive compilation (smoke) tests for records
30 * @library /lib/combo /tools/lib /tools/javac/lib
31 * @modules
32 * jdk.compiler/com.sun.tools.javac.api
33 * jdk.compiler/com.sun.tools.javac.code
34 * jdk.compiler/com.sun.tools.javac.util
35 * jdk.jdeps/com.sun.tools.classfile
36 * @build JavacTestingAbstractProcessor
37 * @run testng/othervm -DuseAP=false RecordCompilationTests
38 * @run testng/othervm -DuseAP=true RecordCompilationTests
39 */
40
41 import java.io.File;
42
43 import java.lang.annotation.ElementType;
44 import java.util.Arrays;
45 import java.util.EnumMap;
46 import java.util.EnumSet;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Set;
51 import java.util.stream.Collectors;
52 import java.util.stream.Stream;
53
54
55 import com.sun.tools.javac.util.Assert;
56
57 import javax.annotation.processing.AbstractProcessor;
58 import javax.annotation.processing.RoundEnvironment;
59 import javax.annotation.processing.SupportedAnnotationTypes;
60
61 import javax.lang.model.element.AnnotationMirror;
62 import javax.lang.model.element.AnnotationValue;
63 import javax.lang.model.element.Element;
64 import javax.lang.model.element.ElementKind;
65 import javax.lang.model.element.ExecutableElement;
66 import javax.lang.model.element.RecordComponentElement;
67 import javax.lang.model.element.TypeElement;
68 import javax.lang.model.element.VariableElement;
69
70 import javax.lang.model.type.ArrayType;
71 import javax.lang.model.type.TypeMirror;
72
73 import com.sun.tools.classfile.AccessFlags;
74 import com.sun.tools.classfile.Annotation;
75 import com.sun.tools.classfile.Attribute;
76 import com.sun.tools.classfile.Attributes;
77 import com.sun.tools.classfile.ClassFile;
78 import com.sun.tools.classfile.Code_attribute;
79 import com.sun.tools.classfile.ConstantPool;
80 import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
81 import com.sun.tools.classfile.ConstantPool.CPInfo;
82 import com.sun.tools.classfile.Field;
83 import com.sun.tools.classfile.Instruction;
84 import com.sun.tools.classfile.Method;
85 import com.sun.tools.classfile.Record_attribute;
86 import com.sun.tools.classfile.Record_attribute.ComponentInfo;
87 import com.sun.tools.classfile.RuntimeAnnotations_attribute;
88 import com.sun.tools.classfile.RuntimeTypeAnnotations_attribute;
89 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
90 import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
91 import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
92 import com.sun.tools.classfile.TypeAnnotation;
93
94 import com.sun.tools.javac.api.ClientCodeWrapper.DiagnosticSourceUnwrapper;
95 import com.sun.tools.javac.code.Attribute.TypeCompound;
96 import com.sun.tools.javac.code.Symbol;
97 import com.sun.tools.javac.code.Symbol.VarSymbol;
98 import com.sun.tools.javac.util.JCDiagnostic;
99
100 import org.testng.annotations.Test;
101 import tools.javac.combo.CompilationTestCase;
102
103 import static java.lang.annotation.ElementType.*;
104 import static org.testng.Assert.assertEquals;
105
106 /** Records are the first feature which sports automatic injection of (declarative and type) annotations : from a
107 * given record component to one or more record members, if applicable.
108 * This implies that the record's implementation can be stressed with the presence of annotation processors. Which is
109 * something the implementator could easily skip. For this reason this test is executed twice, once without the
110 * presence of any annotation processor and one with a simple annotation processor (which does not annotation processing
111 * at all) just to force at least a round of annotation processing.
112 *
113 * Tests needing special compilation options need to store current options, set its customs options by invoking method
114 * `setCompileOptions` and then reset the previous compilation options for other tests. To see an example of this check
115 * method: testAnnos()
116 */
117
118 @Test
119 public class RecordCompilationTests extends CompilationTestCase {
120 private static String[] OPTIONS_WITH_AP = {"-processor", SimplestAP.class.getName()};
121
122 private static final List<String> BAD_COMPONENT_NAMES = List.of(
123 "clone", "finalize", "getClass", "hashCode",
124 "notify", "notifyAll", "toString", "wait");
125
126 /* simplest annotation processor just to force a round of annotation processing for all tests
127 */
128 @SupportedAnnotationTypes("*")
129 public static class SimplestAP extends AbstractProcessor {
130 @Override
131 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
132 return true;
133 }
134 }
135
136 boolean useAP;
137
138 public RecordCompilationTests() {
139 useAP = System.getProperty("useAP", "false").equals("true");
140 setDefaultFilename("R.java");
141 if (useAP) {
142 setCompileOptions(OPTIONS_WITH_AP);
143 }
144 System.out.println(useAP ? "running all tests using an annotation processor" : "running all tests without annotation processor");
145 }
146
147 public void testMalformedDeclarations() {
148 assertFail("compiler.err.premature.eof", "record R()");
149 assertFail("compiler.err.expected", "record R();");
150 assertFail("compiler.err.illegal.start.of.type", "record R(,) { }");
151 assertFail("compiler.err.illegal.start.of.type", "record R((int x)) { }");
152 assertFail("compiler.err.expected", "record R { }");
153 assertFail("compiler.err.expected", "record R(foo) { }");
154 assertFail("compiler.err.expected", "record R(int int) { }");
155 assertFail("compiler.err.mod.not.allowed.here", "abstract record R(String foo) { }");
156 //assertFail("compiler.err.illegal.combination.of.modifiers", "non-sealed record R(String foo) { }");
157 assertFail("compiler.err.repeated.modifier", "public public record R(String foo) { }");
158 assertFail("compiler.err.repeated.modifier", "private private record R(String foo) { }");
159 assertFail("compiler.err.already.defined", "record R(int x, int x) {}");
160 for (String s : List.of("var", "record"))
161 assertFail("compiler.err.restricted.type.not.allowed.here", "record R(# x) { }", s);
162 for (String s : List.of("public", "protected", "private", "static", "final", "transient", "volatile",
163 "abstract", "synchronized", "native", "strictfp")) // missing: sealed and non-sealed
164 assertFail("compiler.err.record.cant.declare.field.modifiers", "record R(# String foo) { }", s);
165 assertFail("compiler.err.varargs.must.be.last", "record R(int... x, int... y) {}");
166 assertFail("compiler.err.instance.initializer.not.allowed.in.records", "record R(int i) { {} }");
167 }
168
169 public void testGoodDeclarations() {
170 assertOK("public record R() { }");
171 assertOK("record R() { }");
172 assertOK("record R() implements java.io.Serializable, Runnable { public void run() { } }");
173 assertOK("record R(int x) { }");
174 assertOK("record R(int x, int y) { }");
175 assertOK("record R(int... xs) { }");
176 assertOK("record R(String... ss) { }");
177 assertOK("@Deprecated record R(int x, int y) { }");
178 assertOK("record R(@Deprecated int x, int y) { }");
179 assertOK("record R<T>(T x, T y) { }");
180 assertOK(
181 """
182 record R<T>(T x) {
183 public T x() {
184 return this.x;
185 }
186 }
187 """);
188 assertOK(
189 """
190 import java.util.List;
191 record R<T>(List<T> x) {
192 public List<T> x() {
193 return this.x;
194 }
195 }
196 """);
197 }
198
199 public void testGoodMemberDeclarations() {
200 String template = "public record R(int x) {\n"
201 + " public R(int x) { this.x = x; }\n"
202 + " public int x() { return x; }\n"
203 + " public boolean equals(Object o) { return true; }\n"
204 + " public int hashCode() { return 0; }\n"
205 + " public String toString() { return null; }\n"
206 + "}";
207 assertOK(template);
208 }
209
210 public void testBadComponentNames() {
211 for (String s : BAD_COMPONENT_NAMES)
212 assertFail("compiler.err.illegal.record.component.name", "record R(int #) { } ", s);
213 }
214
215 public void testRestrictedIdentifiers() {
216 for (String s : List.of("interface record { void m(); }",
217 "@interface record { }",
218 "class record { }",
219 "record record(int x) { }",
220 "enum record { A, B }",
221 "class R<record> { }")) {
222 assertFail(
223 "compiler.err.restricted.type.not.allowed",
224 diagWrapper -> {
225 JCDiagnostic diagnostic = ((DiagnosticSourceUnwrapper)diagWrapper).d;
226 Object[] args = diagnostic.getArgs();
227 Assert.check(args.length == 2);
228 Assert.check(args[1].toString().equals("JDK14"));
229 },
230 s);
231 }
232 }
233
234 public void testValidMembers() {
235 for (String s : List.of("record X(int j) { }",
236 "interface I { }",
237 "static { }",
238 "enum E { A, B }",
239 "class C { }"
240 )) {
241 assertOK("record R(int i) { # }", s);
242 }
243 }
244
245 public void testCyclic() {
246 // Cyclic records are OK, but cyclic inline records would not be
247 assertOK("record R(R r) { }");
248 }
249
250 public void testBadExtends() {
251 assertFail("compiler.err.expected", "record R(int x) extends Object { }");
252 assertFail("compiler.err.expected", "record R(int x) {}\n"
253 + "record R2(int x) extends R { }");
254 assertFail("compiler.err.cant.inherit.from.final", "record R(int x) {}\n"
255 + "class C extends R { }");
256 }
257
258 public void testNoExtendRecord() {
259 assertFail("compiler.err.invalid.supertype.record",
260 """
261 class R extends Record {
262 public String toString() { return null; }
263 public int hashCode() { return 0; }
264 public boolean equals(Object o) { return false; }
265 }
266 """
267 );
268 }
269
270 public void testFieldDeclarations() {
271 // static fields are OK
272 assertOK("public record R(int x) {\n" +
273 " static int I = 1;\n" +
274 " static final String S = \"Hello World!\";\n" +
275 " static private Object O = null;\n" +
276 " static protected Object O2 = null;\n" +
277 "}");
278
279 // instance fields are not
280 assertFail("compiler.err.record.cannot.declare.instance.fields",
281 "public record R(int x) {\n" +
282 " private final int y = 0;" +
283 "}");
284
285 // mutable instance fields definitely not
286 assertFail("compiler.err.record.cannot.declare.instance.fields",
287 "public record R(int x) {\n" +
288 " private int y = 0;" +
289 "}");
290
291 // redeclaring components also not
292 assertFail("compiler.err.record.cannot.declare.instance.fields",
293 "public record R(int x) {\n" +
294 " private final int x;" +
295 "}");
296 }
297
298 public void testAccessorRedeclaration() {
299 assertOK("public record R(int x) {\n" +
300 " public int x() { return x; };" +
301 "}");
302
303 assertOK("public record R(int... x) {\n" +
304 " public int[] x() { return x; };" +
305 "}");
306
307 assertOK("public record R(int x) {\n" +
308 " public final int x() { return 0; };" +
309 "}");
310
311 assertOK("public record R(int x) {\n" +
312 " public final int x() { return 0; };" +
313 "}");
314
315 assertFail("compiler.err.invalid.accessor.method.in.record",
316 "public record R(int x) {\n" +
317 " final int x() { return 0; };" +
318 "}");
319
320 assertFail("compiler.err.invalid.accessor.method.in.record",
321 "public record R(int x) {\n" +
322 " int x() { return 0; };" +
323 "}");
324
325 assertFail("compiler.err.invalid.accessor.method.in.record",
326 "public record R(int x) {\n" +
327 " private int x() { return 0; };" +
328 "}");
329
330 assertFail("compiler.err.invalid.accessor.method.in.record",
331 "public record R(int x) {\n" +
332 " public int x() throws Exception { return 0; };" +
333 "}");
334
335 for (String s : List.of("List", "List<?>", "Object", "ArrayList<String>", "int"))
336 assertFail("compiler.err.invalid.accessor.method.in.record",
337 "import java.util.*;\n" +
338 "public record R(List<String> x) {\n" +
339 " public # x() { return null; };" +
340 "}", s);
341
342 assertFail("compiler.err.invalid.accessor.method.in.record",
343 "public record R(int x) {\n" +
344 " public <T> int x() { return x; };" +
345 "}");
346
347 assertFail("compiler.err.invalid.accessor.method.in.record",
348 "public record R(int x) {\n" +
349 " static private final j = 0;" +
350 " static public int x() { return j; };" +
351 "}");
352 }
353
354 public void testConstructorRedeclaration() {
355 for (String goodCtor : List.of(
356 "public R(int x) { this(x, 0); }",
357 "public R(int x, int y) { this.x = x; this.y = y; }",
358 "public R { }"))
359 assertOK("record R(int x, int y) { # }", goodCtor);
360
361 assertOK("import java.util.*; record R(String x, String y) { public R { Objects.requireNonNull(x); Objects.requireNonNull(y); } }");
362
363 // The lambda expressions in the constructor should be compiled successfully.
364 assertOK("""
365 import static java.util.Objects.*;
366 record R(String v) {
367 R {
368 requireNonNull(v, () -> "v must be provided");
369 requireNonNullElseGet(v, () -> "w");
370 }
371 }""");
372
373 // Not OK to redeclare canonical without DA
374 assertFail("compiler.err.var.might.not.have.been.initialized", "record R(int x, int y) { # }",
375 "public R(int x, int y) { this.x = x; }");
376
377 // Not OK to rearrange or change names
378 for (String s : List.of("public R(int y, int x) { this.x = x; this.y = y; }",
379 "public R(int _x, int _y) { this.x = _x; this.y = _y; }"))
380 assertFail("compiler.err.invalid.canonical.constructor.in.record", "record R(int x, int y) { # }", s);
381
382 // ctor args must match types
383 assertFail("compiler.err.invalid.canonical.constructor.in.record",
384 "import java.util.*;\n" +
385 "record R(List<String> list) { # }",
386 "R(List list) { this.list = list; }");
387
388 // canonical ctor should not throw checked exceptions
389 assertFail("compiler.err.invalid.canonical.constructor.in.record",
390 "record R() { # }",
391 "public R() throws Exception { }");
392
393 // same for compact
394 assertFail("compiler.err.invalid.canonical.constructor.in.record",
395 "record R() { # }",
396 "public R throws Exception { }");
397
398 // not even unchecked exceptions
399 assertFail("compiler.err.invalid.canonical.constructor.in.record",
400 "record R() { # }",
401 "public R() throws IllegalArgumentException { }");
402
403 // ditto
404 assertFail("compiler.err.invalid.canonical.constructor.in.record",
405 "record R() { # }",
406 "public R throws IllegalArgumentException { }");
407
408 // If types match, names must match
409 assertFail("compiler.err.invalid.canonical.constructor.in.record",
410 "record R(int x, int y) { public R(int y, int x) { this.x = this.y = 0; }}");
411
412 // first invocation should be one to the canonical
413 assertFail("compiler.err.first.statement.must.be.call.to.another.constructor",
414 "record R(int x, int y) { public R(int y, int x, int z) { this.x = this.y = 0; } }");
415
416 assertFail("compiler.err.first.statement.must.be.call.to.another.constructor",
417 "record R(int x, int y) { public R(int y, int x, int z) { super(); this.x = this.y = 0; } }");
418
419 assertOK("record R(int x, int y) { " +
420 " public R(int x, int y, int z) { this(x, y); } " +
421 "}");
422
423 assertOK("record R(int x) { " +
424 " public R(int x, int y) { this(x, y, 0); } " +
425 " public R(int x, int y, int z) { this(x); } " +
426 "}");
427
428 assertFail("compiler.err.invalid.canonical.constructor.in.record",
429 "record R<T>(T a) { # }",
430 "public <T> R {}");
431
432 assertFail("compiler.err.invalid.canonical.constructor.in.record",
433 "record R(int i) { # }",
434 "public <T> R(int i) { this.i = i; }");
435
436 assertFail("compiler.err.invalid.canonical.constructor.in.record",
437 "record R<T>(T a) { # }",
438 "public <T> R(T a) { this.a = a; }");
439
440 assertFail("compiler.err.invalid.canonical.constructor.in.record",
441 "record R(int a) { # }",
442 "public R(int a) { super(); this.a = a; }");
443 }
444
445 public void testAnnotationCriteria() {
446 String imports = "import java.lang.annotation.*;\n";
447 String template = "@Target({ # }) @interface A {}\n";
448 EnumMap<ElementType, String> annotations = new EnumMap<>(ElementType.class);
449 for (ElementType e : values())
450 annotations.put(e, template.replace("#", "ElementType." + e.name()));
451 EnumSet<ElementType> goodSet = EnumSet.of(RECORD_COMPONENT, FIELD, METHOD, PARAMETER, TYPE_USE);
452 EnumSet<ElementType> badSet = EnumSet.of(CONSTRUCTOR, PACKAGE, TYPE, LOCAL_VARIABLE, ANNOTATION_TYPE, TYPE_PARAMETER, MODULE);
453
454 assertEquals(goodSet.size() + badSet.size(), values().length);
455 String A_GOOD = template.replace("#",
456 goodSet.stream().map(ElementType::name).map(s -> "ElementType." + s).collect(Collectors.joining(",")));
457 String A_BAD = template.replace("#",
458 badSet.stream().map(ElementType::name).map(s -> "ElementType." + s).collect(Collectors.joining(",")));
459 String A_ALL = template.replace("#",
460 Stream.of(ElementType.values()).map(ElementType::name).map(s -> "ElementType." + s).collect(Collectors.joining(",")));
461 String A_NONE = "@interface A {}";
462
463 for (ElementType e : goodSet)
464 assertOK(imports + annotations.get(e) + "record R(@A int x) { }");
465 assertOK(imports + A_GOOD + "record R(@A int x) { }");
466 assertOK(imports + A_ALL + "record R(@A int x) { }");
467 assertOK(imports + A_NONE);
468
469 for (ElementType e : badSet) {
470 assertFail("compiler.err.annotation.type.not.applicable", imports + annotations.get(e) + "record R(@A int x) { }");
471 }
472
473 assertFail("compiler.err.annotation.type.not.applicable", imports + A_BAD + "record R(@A int x) { }");
474
475 // TODO: OK to redeclare with or without same annos
476 }
477
478 public void testNestedRecords() {
479 String template = "class R { \n" +
480 " # record RR(int a) { }\n" +
481 "}";
482
483 for (String s : List.of("", "static", "final",
484 "private", "public", "protected",
485 "private static", "public static", "private static final"))
486 assertOK(template, s);
487
488 for (String s : List.of("class C { }",
489 "static class C { }",
490 "enum X { A; }",
491 "interface I { }",
492 "record RR(int y) { }"))
493 assertOK("record R(int x) { # }", s);
494 }
495
496 public void testDuplicatedMember() {
497 String template
498 = " record R(int i) {\n" +
499 " public int i() { return i; }\n" +
500 " public int i() { return i; }\n" +
501 " }";
502 assertFail("compiler.err.already.defined", template);
503 }
504
505 public void testStaticLocals() {
506 // static locals can't capture local variables, instance fields or type variables
507 for (String s : List.of(
508 "record RR(int x) { public int x() { return y; }};",
509 "record RR(int x) { public int x() { return z; }};",
510 "record RR(int x) { public int x() { return instance; }};",
511 "record RR(T t) {};",
512 "record RR(U u) {};",
513
514 "interface I { default int x() { return y; }};",
515 "interface I { default int x() { return z; }};",
516 "interface I { default int x() { return instance; }};",
517 "interface I { default int x(T t) { return 0; }};",
518 "interface I { default int x(U u) { return 0; }};",
519
520 "enum E { A; int x() { return y; }};",
521 "enum E { A; int x() { return z; }};",
522 "enum E { A; int x() { return instance; }};",
523 "enum E { A; int x(T t) { return 0; }};",
524 "enum E { A; int x(U u) { return 0; }};"
525 )) {
526 assertFail("compiler.err.non-static.cant.be.ref",
527 """
528 class R<T> {
529 int instance = 0;
530 <U> U m(int y) {
531 int z;
532 #S
533 return null;
534 }
535 }
536 """.replaceFirst("#S", s));
537 }
538
539 // a similar example but a bit more complex
540 for (String s : List.of(
541 "record R() { void test1() { class X { void test2() { System.err.println(localVar); } } } }",
542 "record R() { void test1() { class X { void test2() { System.err.println(param); } } } }",
543 "record R() {void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
544 "record R() { void test1() { class X { T t; } } }",
545 "record R() { void test1() { class X { U u; } } }",
546
547 "interface I { default void test1() { class X { void test2() { System.err.println(localVar); } } } }",
548 "interface I() { default void test1() { class X { void test2() {System.err.println(param);} } } }",
549 "interface I { default void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
550 "interface I { default void test1() { class X { T t; } } }",
551 "interface I() { default void test1() { class X {U u;} } }",
552
553 "enum E { A; void test1() { class X { void test2() { System.err.println(localVar); } } } }",
554 "enum E { A; void test1() { class X { void test2() {System.err.println(param);} } } }",
555 "enum E { A; void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
556 "enum E { A; void test1() { class X { T t; } } }",
557 "enum E { A; void test1() { class X {U u;} } }"
558 )) {
559 assertFail("compiler.err.non-static.cant.be.ref",
560 """
561 class C<T> {
562 String instanceField = "instance";
563 static <U> U m(String param) {
564 String localVar = "local";
565 #S
566 return null;
567 }
568 }
569 """.replaceFirst("#S", s));
570 }
571
572 // can't self-shadow
573 for (String s : List.of("record R() {}", "interface R {}", "enum R { A }")) {
574 assertFail("compiler.err.already.defined", "class R { void m() { #S } }".replaceFirst("#S", s));
575 }
576
577 // can't be explicitly static
578 for (String s : List.of("static record RR() { }", "static interface I {}", "static enum E { A }")) {
579 assertFail("compiler.err.illegal.start.of.expr", "class R { void m() { #S } }".replaceFirst("#S", s));
580 }
581
582 // but static fields can be accessed
583 for (String s : List.of(
584 "record RR() { public int x() { return z; } };",
585 "interface I { default int x() { return z; } }",
586 "enum E { A; int x() { return z; } }"
587 )) {
588 assertOK("class R { static int z = 0; void m() { #S } }".replaceFirst("#S", s));
589 }
590
591 // local records can also be final
592 assertOK("class R { void m() { final record RR(int x) { }; } }");
593 }
594
595 public void testStaticDefinitionsInInnerClasses() {
596 // static defs in inner classes can't capture instance fields or type variables
597 for (String s : List.of(
598 """
599 record R() {
600 void test() { System.err.println(field); }
601 }
602 """,
603 """
604 record R() {
605 void test(T t) {}
606 }
607 """,
608 """
609 record R() {
610 void test1() {
611 class X {
612 void test2() { System.err.println(field); }
613 }
614 }
615 }
616 """,
617 """
618 record R() {
619 void test1() {
620 class X { void test2(T t) {} }
621 }
622 }
623 """,
624
625 """
626 interface I {
627 default void test() { System.err.println(field); }
628 }
629 """,
630 """
631 interface I {
632 default void test(T t) {}
633 }
634 """,
635 """
636 interface I {
637 default void test1() {
638 class X {
639 void test2() { System.err.println(field); }
640 }
641 }
642 }
643 """,
644 """
645 interface I {
646 default void test1() {
647 class X { void test2(T t) {} }
648 }
649 }
650 """,
651
652 """
653 enum E {
654 A;
655 void test() { System.err.println(field); }
656 }
657 """,
658 """
659 enum E {
660 A;
661 void test(T t) {}
662 }
663 """,
664 """
665 enum E {
666 A;
667 void test1() {
668 class X {
669 void test2() { System.err.println(field); }
670 }
671 }
672 }
673 """,
674 """
675 enum E {
676 A;
677 void test1() {
678 class X { void test2(T t) {} }
679 }
680 }
681 """,
682
683 """
684 static class SC {
685 void test() { System.err.println(field); }
686 }
687 """,
688 """
689 static class SC {
690 void test(T t) {}
691 }
692 """,
693 """
694 static class SC {
695 void test1() {
696 class X {
697 void test2() { System.err.println(field); }
698 }
699 }
700 }
701 """,
702 """
703 static class SC {
704 void test1() {
705 class X { void test2(T t) {} }
706 }
707 }
708 """
709 )) {
710 assertFail("compiler.err.non-static.cant.be.ref",
711 """
712 class C<T> {
713 String field = "field";
714 class Inner {
715 #S
716 }
717 }
718 """.replaceFirst("#S", s));
719 }
720
721 // another, more complex, example
722 // static defs in inner classes can't capture instance locals, fields or type variables
723 for (String s : List.of(
724 """
725 record R() {
726 void test() { System.err.println(field); }
727 }
728 """,
729 """
730 record R() {
731 void test1() {
732 class X { void test2() { System.err.println(field); } }
733 }
734 }
735 """,
736 """
737 record R() {
738 void test() { System.err.println(param); }
739 }
740 """,
741 """
742 record R() {
743 void test1() {
744 class X { void test2() { System.err.println(param); } }
745 }
746 }
747 """,
748 """
749 record R() {
750 void test() { System.err.println(local); }
751 }
752 """,
753 """
754 record R() {
755 void test1() {
756 class X { void test2() { System.err.println(local); } }
757 }
758 }
759 """,
760 """
761 record R() {
762 void test(T t) {}
763 }
764 """,
765 """
766 record R() {
767 void test(U u) {}
768 }
769 """,
770 """
771 record R() {
772 void test1() {
773 class X { void test2(T t) {} }
774 }
775 }
776 """,
777 """
778 record R() {
779 void test1() {
780 class X { void test2(U u) {} }
781 }
782 }
783 """,
784
785 """
786 interface I {
787 default void test() { System.err.println(field); }
788 }
789 """,
790 """
791 interface I {
792 default void test1() {
793 class X {
794 void test2() { System.err.println(field); }
795 }
796 }
797 }
798 """,
799 """
800 interface I {
801 default void test() { System.err.println(param); }
802 }
803 """,
804 """
805 interface I {
806 default void test1() {
807 class X {
808 void test2() { System.err.println(param); }
809 }
810 }
811 }
812 """,
813 """
814 interface I {
815 default void test() { System.err.println(local); }
816 }
817 """,
818 """
819 interface I {
820 default void test1() {
821 class X {
822 void test2() { System.err.println(local); }
823 }
824 }
825 }
826 """,
827 """
828 interface I {
829 default void test(T t) {}
830 }
831 """,
832 """
833 interface I {
834 default void test(U u) {}
835 }
836 """,
837 """
838 interface I {
839 default void test1() {
840 class X { void test2(T t) {} }
841 }
842 }
843 """,
844 """
845 interface I {
846 default void test1() {
847 class X { void test2(U u) {} }
848 }
849 }
850 """,
851
852 """
853 enum E {
854 A;
855 void test() { System.err.println(field); }
856 }
857 """,
858 """
859 enum E {
860 A;
861 void test1() {
862 class X {
863 void test2() { System.err.println(field); }
864 }
865 }
866 }
867 """,
868 """
869 enum E {
870 A;
871 void test() { System.err.println(param); }
872 }
873 """,
874 """
875 enum E {
876 A;
877 void test1() {
878 class X {
879 void test2() { System.err.println(param); }
880 }
881 }
882 }
883 """,
884 """
885 enum E {
886 A;
887 void test() { System.err.println(local); }
888 }
889 """,
890 """
891 enum E {
892 A;
893 void test1() {
894 class X {
895 void test2() { System.err.println(local); }
896 }
897 }
898 }
899 """,
900 """
901 enum E {
902 A;
903 void test(T t) {}
904 }
905 """,
906 """
907 enum E {
908 A;
909 void test(U u) {}
910 }
911 """,
912 """
913 enum E {
914 A;
915 void test1() {
916 class X { void test2(T t) {} }
917 }
918 }
919 """,
920 """
921 enum E {
922 A;
923 void test1() {
924 class X { void test2(U u) {} }
925 }
926 }
927 """,
928
929 """
930 static class SC {
931 void test() { System.err.println(field); }
932 }
933 """,
934 """
935 static class SC {
936 void test1() {
937 class X {
938 void test2() { System.err.println(field); }
939 }
940 }
941 }
942 """,
943 """
944 static class SC {
945 void test() { System.err.println(param); }
946 }
947 """,
948 """
949 static class SC {
950 void test1() {
951 class X {
952 void test2() { System.err.println(param); }
953 }
954 }
955 }
956 """,
957 """
958 static class SC {
959 void test() { System.err.println(local); }
960 }
961 """,
962 """
963 static class SC {
964 void test1() {
965 class X {
966 void test2() { System.err.println(local); }
967 }
968 }
969 }
970 """,
971 """
972 static class SC {
973 void test(T t) {}
974 }
975 """,
976 """
977 static class SC {
978 void test(U u) {}
979 }
980 """,
981 """
982 static class SC {
983 void test1() {
984 class X { void test2(T t) {} }
985 }
986 }
987 """,
988 """
989 static class SC {
990 void test1() {
991 class X { void test2(U u) {} }
992 }
993 }
994 """
995 )) {
996 assertFail("compiler.err.non-static.cant.be.ref",
997 """
998 class C<T> {
999 String field = "field";
1000 <U> U m(String param) {
1001 String local = "local";
1002 class Local {
1003 class Inner { #S }
1004 }
1005 return null;
1006 }
1007 }
1008 """.replaceFirst("#S", s));
1009 }
1010
1011 // inner classes can contain static methods too
1012 assertOK(
1013 """
1014 class C {
1015 class Inner {
1016 // static method inside inner class
1017 static void m() {}
1018 }
1019 }
1020 """
1021 );
1022
1023 assertOK(
1024 """
1025 class C {
1026 void m() {
1027 new Object() {
1028 // static method inside inner class
1029 static void m() {}
1030 };
1031 }
1032 }
1033 """
1034 );
1035
1036 // but still non-static declarations can't be accessed from a static method inside a local class
1037 for (String s : List.of(
1038 "System.out.println(localVar)",
1039 "System.out.println(param)",
1040 "System.out.println(field)",
1041 "T t",
1042 "U u"
1043 )) {
1044 assertFail("compiler.err.non-static.cant.be.ref",
1045 """
1046 class C<T> {
1047 int field = 0;
1048 <U> void foo(int param) {
1049 int localVar = 1;
1050 class Local {
1051 static void m() {
1052 #S;
1053 }
1054 }
1055 }
1056 }
1057 """.replaceFirst("#S", s));
1058 }
1059 }
1060
1061 public void testReturnInCanonical_Compact() {
1062 assertFail("compiler.err.invalid.canonical.constructor.in.record", "record R(int x) { # }",
1063 "public R { return; }");
1064 assertFail("compiler.err.invalid.canonical.constructor.in.record", "record R(int x) { # }",
1065 "public R { if (i < 0) { return; }}");
1066 assertOK("record R(int x) { public R(int x) { this.x = x; return; } }");
1067 assertOK("record R(int x) { public R { Runnable r = () -> { return; };} }");
1068 }
1069
1070 public void testArgumentsAreNotFinalInCompact() {
1071 assertOK(
1072 """
1073 record R(int x) {
1074 public R {
1075 x++;
1076 }
1077 }
1078 """);
1079 }
1080
1081 public void testNoNativeMethods() {
1082 assertFail("compiler.err.mod.not.allowed.here", "record R(int x) { # }",
1083 "public native R {}");
1084 assertFail("compiler.err.mod.not.allowed.here", "record R(int x) { # }",
1085 "public native void m();");
1086 }
1087
1088 public void testRecordsInsideInner() {
1089 assertOK(
1090 """
1091 class Outer {
1092 class Inner {
1093 record R(int a) {}
1094 }
1095 }
1096 """
1097 );
1098 assertOK(
1099 """
1100 class Outer {
1101 public void test() {
1102 class Inner extends Outer {
1103 record R(int i) {}
1104 }
1105 }
1106 }
1107 """);
1108 assertOK(
1109 """
1110 class Outer {
1111 Runnable run = new Runnable() {
1112 record TestRecord(int i) {}
1113 public void run() {}
1114 };
1115 }
1116 """);
1117 assertOK(
1118 """
1119 class Outer {
1120 void m() {
1121 record A() {
1122 record B() { }
1123 }
1124 }
1125 }
1126 """);
1127 }
1128
1129 public void testAnnoInsideLocalOrAnonymous() {
1130 assertFail("compiler.err.annotation.decl.not.allowed.here",
1131 """
1132 class Outer {
1133 public void test() {
1134 class Local {
1135 @interface A {}
1136 }
1137 }
1138 }
1139 """);
1140 assertFail("compiler.err.annotation.decl.not.allowed.here",
1141 """
1142 class Outer {
1143 public void test() {
1144 interface I {
1145 @interface A {}
1146 }
1147 }
1148 }
1149 """);
1150 assertFail("compiler.err.annotation.decl.not.allowed.here",
1151 """
1152 class Outer {
1153 public void test() {
1154 record R() {
1155 @interface A {}
1156 }
1157 }
1158 }
1159 """);
1160 assertFail("compiler.err.annotation.decl.not.allowed.here",
1161 """
1162 class Outer {
1163 public void test() {
1164 enum E {
1165 E1;
1166 @interface A {}
1167 }
1168 }
1169 }
1170 """);
1171
1172 assertFail("compiler.err.annotation.decl.not.allowed.here",
1173 """
1174 class Outer {
1175 public void test() {
1176 class Local1 {
1177 class Local2 {
1178 @interface A {}
1179 }
1180 }
1181 }
1182 }
1183 """);
1184 assertFail("compiler.err.annotation.decl.not.allowed.here",
1185 """
1186 class Outer {
1187 public void test() {
1188 class Local {
1189 interface I {
1190 @interface A {}
1191 }
1192 }
1193 }
1194 }
1195 """);
1196 assertFail("compiler.err.annotation.decl.not.allowed.here",
1197 """
1198 class Outer {
1199 public void test() {
1200 class Local {
1201 record R() {
1202 @interface A {}
1203 }
1204 }
1205 }
1206 }
1207 """);
1208 assertFail("compiler.err.annotation.decl.not.allowed.here",
1209 """
1210 class Outer {
1211 public void test() {
1212 class Local {
1213 enum E {
1214 E1;
1215 @interface A {}
1216 }
1217 }
1218 }
1219 }
1220 """);
1221
1222 assertFail("compiler.err.annotation.decl.not.allowed.here",
1223 """
1224 class Outer {
1225 Runnable run = new Runnable() {
1226 @interface A {}
1227 public void run() {}
1228 };
1229 }
1230 """);
1231 }
1232
1233 public void testReceiverParameter() {
1234 assertFail("compiler.err.receiver.parameter.not.applicable.constructor.toplevel.class",
1235 """
1236 record R(int i) {
1237 public R(R this, int i) {
1238 this.i = i;
1239 }
1240 }
1241 """);
1242 assertFail("compiler.err.non-static.cant.be.ref",
1243 """
1244 class Outer {
1245 record R(int i) {
1246 public R(Outer Outer.this, int i) {
1247 this.i = i;
1248 }
1249 }
1250 }
1251 """);
1252 assertOK(
1253 """
1254 record R(int i) {
1255 void m(R this) {}
1256 public int i(R this) { return i; }
1257 }
1258 """);
1259 }
1260
1261 public void testOnlyOneFieldRef() throws Exception {
1262 for (String source : List.of(
1263 "record R(int recordComponent) {}",
1264 """
1265 class Test {
1266 class Inner {
1267 Inner() {
1268 record R(int recordComponent) {}
1269 }
1270 }
1271 }
1272 """,
1273 """
1274 class Test {
1275 class Inner {
1276 void m() {
1277 record R(int recordComponent) {}
1278 }
1279 }
1280 }
1281 """,
1282 """
1283 class Test {
1284 void m() {
1285 record R(int recordComponent) {}
1286 }
1287 }
1288 """
1289 )) {
1290 File dir = assertOK(true, source);
1291 int numberOfFieldRefs = 0;
1292 for (final File fileEntry : dir.listFiles()) {
1293 if (fileEntry.getName().endsWith("R.class")) {
1294 ClassFile classFile = ClassFile.read(fileEntry);
1295 for (CPInfo cpInfo : classFile.constant_pool.entries()) {
1296 if (cpInfo instanceof ConstantPool.CONSTANT_Fieldref_info) {
1297 numberOfFieldRefs++;
1298 ConstantPool.CONSTANT_NameAndType_info nameAndType =
1299 (ConstantPool.CONSTANT_NameAndType_info)classFile.constant_pool
1300 .get(((ConstantPool.CONSTANT_Fieldref_info)cpInfo).name_and_type_index);
1301 Assert.check(nameAndType.getName().equals("recordComponent"));
1302 }
1303 }
1304 Assert.check(numberOfFieldRefs == 1);
1305 }
1306 }
1307 }
1308 }
1309
1310 // check that fields are initialized in a canonical constructor in the same declaration order as the corresponding
1311 // record component
1312 public void testCheckInitializationOrderInCompactConstructor() throws Exception {
1313 int putField1 = -1;
1314 int putField2 = -1;
1315 File dir = assertOK(true, "record R(int i, String s) { R {} }");
1316 for (final File fileEntry : dir.listFiles()) {
1317 if (fileEntry.getName().equals("R.class")) {
1318 ClassFile classFile = ClassFile.read(fileEntry);
1319 for (Method method : classFile.methods) {
1320 if (method.getName(classFile.constant_pool).equals("<init>")) {
1321 Code_attribute code_attribute = (Code_attribute) method.attributes.get("Code");
1322 for (Instruction instruction : code_attribute.getInstructions()) {
1323 if (instruction.getMnemonic().equals("putfield")) {
1324 if (putField1 != -1 && putField2 != -1) {
1325 throw new AssertionError("was expecting only two putfield instructions in this method");
1326 }
1327 if (putField1 == -1) {
1328 putField1 = instruction.getShort(1);
1329 } else if (putField2 == -1) {
1330 putField2 = instruction.getShort(1);
1331 }
1332 }
1333 }
1334 // now we need to check that we are assigning to `i` first and to `s` afterwards
1335 CONSTANT_Fieldref_info fieldref_info1 = (CONSTANT_Fieldref_info)classFile.constant_pool.get(putField1);
1336 if (!fieldref_info1.getNameAndTypeInfo().getName().equals("i")) {
1337 throw new AssertionError("was expecting variable name 'i'");
1338 }
1339
1340 CONSTANT_Fieldref_info fieldref_info2 = (CONSTANT_Fieldref_info)classFile.constant_pool.get(putField2);
1341 if (!fieldref_info2.getNameAndTypeInfo().getName().equals("s")) {
1342 throw new AssertionError("was expecting variable name 's'");
1343 }
1344 }
1345 }
1346 }
1347 }
1348 }
1349
1350 public void testAcceptRecordId() {
1351 String[] previousOptions = getCompileOptions();
1352 try {
1353 String[] testOptions = {};
1354 setCompileOptions(testOptions);
1355 assertFail("compiler.err.illegal.start.of.type",
1356 "class R {\n" +
1357 " record RR(int i) {\n" +
1358 " return null;\n" +
1359 " }\n" +
1360 " class record {}\n" +
1361 "}");
1362 } finally {
1363 setCompileOptions(previousOptions);
1364 }
1365 }
1366
1367 public void testMultipleAnnosInRecord() throws Exception {
1368 String[] previousOptions = getCompileOptions();
1369
1370 try {
1371 String imports = """
1372 import java.lang.annotation.ElementType;
1373 import java.lang.annotation.Target;
1374 """;
1375
1376 String annotTemplate =
1377 """
1378 @Target(ElementType.#TARGET)
1379 @interface anno#TARGET { }
1380 """;
1381
1382 String recordTemplate =
1383 """
1384 record R(#TARGETS String s) {}
1385 """;
1386
1387 String[] generalOptions = {
1388 "-processor", Processor.class.getName(),
1389 };
1390
1391 List<String> targets = List.of("FIELD", "RECORD_COMPONENT", "PARAMETER", "METHOD");
1392
1393 var interfaces = targets.stream().map(t -> annotTemplate.replaceAll("#TARGET", t)).collect(Collectors.joining("\n"));
1394 var recordAnnotations = targets.stream().map(t -> "@anno" + t).collect(Collectors.joining(" "));
1395 String record = recordTemplate.replaceFirst("#TARGETS", recordAnnotations);
1396 String code = String.format("%s\n%s\n%s\n",imports,interfaces,record);
1397 String[] testOptions = generalOptions.clone();
1398 setCompileOptions(testOptions);
1399
1400 assertOK(true, code);
1401
1402 // let's reset the default compiler options for other tests
1403 } finally {
1404 setCompileOptions(previousOptions);
1405 }
1406 }
1407
1408 public void testAnnos() throws Exception {
1409 String[] previousOptions = getCompileOptions();
1410 try {
1411 String srcTemplate =
1412 """
1413 import java.lang.annotation.*;
1414 @Target({#TARGET})
1415 @Retention(RetentionPolicy.RUNTIME)
1416 @interface Anno { }
1417 record R(@Anno String s) {}
1418 """;
1419
1420 // testing several combinations, adding even more combinations won't add too much value
1421 List<String> annoApplicableTargets = List.of(
1422 "ElementType.FIELD",
1423 "ElementType.METHOD",
1424 "ElementType.PARAMETER",
1425 "ElementType.RECORD_COMPONENT",
1426 "ElementType.TYPE_USE",
1427 "ElementType.TYPE_USE,ElementType.FIELD",
1428 "ElementType.TYPE_USE,ElementType.METHOD",
1429 "ElementType.TYPE_USE,ElementType.PARAMETER",
1430 "ElementType.TYPE_USE,ElementType.RECORD_COMPONENT",
1431 "ElementType.TYPE_USE,ElementType.FIELD,ElementType.METHOD",
1432 "ElementType.TYPE_USE,ElementType.FIELD,ElementType.PARAMETER",
1433 "ElementType.TYPE_USE,ElementType.FIELD,ElementType.RECORD_COMPONENT",
1434 "ElementType.FIELD,ElementType.TYPE_USE",
1435 "ElementType.FIELD,ElementType.CONSTRUCTOR",
1436 "ElementType.FIELD,ElementType.LOCAL_VARIABLE",
1437 "ElementType.FIELD,ElementType.ANNOTATION_TYPE",
1438 "ElementType.FIELD,ElementType.PACKAGE",
1439 "ElementType.FIELD,ElementType.TYPE_PARAMETER",
1440 "ElementType.FIELD,ElementType.MODULE",
1441 "ElementType.METHOD,ElementType.TYPE_USE",
1442 "ElementType.PARAMETER,ElementType.TYPE_USE",
1443 "ElementType.RECORD_COMPONENT,ElementType.TYPE_USE",
1444 "ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE",
1445 "ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE_USE",
1446 "ElementType.FIELD,ElementType.RECORD_COMPONENT,ElementType.TYPE_USE"
1447 );
1448
1449 String[] generalOptions = {
1450 "-processor", Processor.class.getName(),
1451 "-Atargets="
1452 };
1453
1454 for (String target : annoApplicableTargets) {
1455 String code = srcTemplate.replaceFirst("#TARGET", target);
1456 String[] testOptions = generalOptions.clone();
1457 testOptions[testOptions.length - 1] = testOptions[testOptions.length - 1] + target;
1458 setCompileOptions(testOptions);
1459
1460 File dir = assertOK(true, code);
1461
1462 ClassFile classFile = ClassFile.read(findClassFileOrFail(dir, "R.class"));
1463
1464 // field first
1465 Assert.check(classFile.fields.length == 1);
1466 Field field = classFile.fields[0];
1467 // if FIELD is one of the targets then there must be a declaration annotation applied to the field, apart from
1468 // the type annotation
1469 if (target.contains("ElementType.FIELD")) {
1470 checkAnno(classFile,
1471 (RuntimeAnnotations_attribute) findAttributeOrFail(
1472 field.attributes,
1473 RuntimeVisibleAnnotations_attribute.class),
1474 "Anno");
1475 } else {
1476 assertAttributeNotPresent(field.attributes, RuntimeVisibleAnnotations_attribute.class);
1477 }
1478
1479 // lets check now for the type annotation
1480 if (target.contains("ElementType.TYPE_USE")) {
1481 checkTypeAnno(
1482 classFile,
1483 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
1484 "FIELD",
1485 "Anno");
1486 } else {
1487 assertAttributeNotPresent(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
1488 }
1489
1490 // checking for the annotation on the corresponding parameter of the canonical constructor
1491 Method init = findMethodOrFail(classFile, "<init>");
1492 // if PARAMETER is one of the targets then there must be a declaration annotation applied to the parameter, apart from
1493 // the type annotation
1494 if (target.contains("ElementType.PARAMETER")) {
1495 checkParameterAnno(classFile,
1496 (RuntimeVisibleParameterAnnotations_attribute) findAttributeOrFail(
1497 init.attributes,
1498 RuntimeVisibleParameterAnnotations_attribute.class),
1499 "Anno");
1500 } else {
1501 assertAttributeNotPresent(init.attributes, RuntimeVisibleAnnotations_attribute.class);
1502 }
1503 // let's check now for the type annotation
1504 if (target.contains("ElementType.TYPE_USE")) {
1505 checkTypeAnno(
1506 classFile,
1507 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
1508 "METHOD_FORMAL_PARAMETER", "Anno");
1509 } else {
1510 assertAttributeNotPresent(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
1511 }
1512
1513 // checking for the annotation in the accessor
1514 Method accessor = findMethodOrFail(classFile, "s");
1515 // if METHOD is one of the targets then there must be a declaration annotation applied to the accessor, apart from
1516 // the type annotation
1517 if (target.contains("ElementType.METHOD")) {
1518 checkAnno(classFile,
1519 (RuntimeAnnotations_attribute) findAttributeOrFail(
1520 accessor.attributes,
1521 RuntimeVisibleAnnotations_attribute.class),
1522 "Anno");
1523 } else {
1524 assertAttributeNotPresent(accessor.attributes, RuntimeVisibleAnnotations_attribute.class);
1525 }
1526 // let's check now for the type annotation
1527 if (target.contains("ElementType.TYPE_USE")) {
1528 checkTypeAnno(
1529 classFile,
1530 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
1531 "METHOD_RETURN", "Anno");
1532 } else {
1533 assertAttributeNotPresent(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
1534 }
1535
1536 // checking for the annotation in the Record attribute
1537 Record_attribute record = (Record_attribute) findAttributeOrFail(classFile.attributes, Record_attribute.class);
1538 Assert.check(record.component_count == 1);
1539 // if RECORD_COMPONENT is one of the targets then there must be a declaration annotation applied to the
1540 // field, apart from the type annotation
1541 if (target.contains("ElementType.RECORD_COMPONENT")) {
1542 checkAnno(classFile,
1543 (RuntimeAnnotations_attribute) findAttributeOrFail(
1544 record.component_info_arr[0].attributes,
1545 RuntimeVisibleAnnotations_attribute.class),
1546 "Anno");
1547 } else {
1548 assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleAnnotations_attribute.class);
1549 }
1550 // lets check now for the type annotation
1551 if (target.contains("ElementType.TYPE_USE")) {
1552 checkTypeAnno(
1553 classFile,
1554 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(
1555 record.component_info_arr[0].attributes,
1556 RuntimeVisibleTypeAnnotations_attribute.class),
1557 "FIELD", "Anno");
1558 } else {
1559 assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleTypeAnnotations_attribute.class);
1560 }
1561 }
1562
1563 // let's reset the default compiler options for other tests
1564 } finally {
1565 setCompileOptions(previousOptions);
1566 }
1567 }
1568
1569 // JDK-8292159: TYPE_USE annotations on generic type arguments
1570 // of record components discarded
1571 public void testOnlyTypeAnnotationsOnComponentField() throws Exception {
1572 String code =
1573 """
1574 import java.lang.annotation.*;
1575 import java.util.List;
1576 @Target({ElementType.TYPE_USE})
1577 @Retention(RetentionPolicy.RUNTIME)
1578 @interface Anno { }
1579 record R(List<@Anno String> s) {}
1580 """;
1581
1582 File dir = assertOK(true, code);
1583
1584 ClassFile classFile = ClassFile.read(findClassFileOrFail(dir, "R.class"));
1585
1586 // field first
1587 Assert.check(classFile.fields.length == 1);
1588 Field field = classFile.fields[0];
1589 checkTypeAnno(
1590 classFile,
1591 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
1592 "FIELD",
1593 "Anno");
1594
1595 // checking for the annotation on the corresponding parameter of the canonical constructor
1596 Method init = findMethodOrFail(classFile, "<init>");
1597 checkTypeAnno(
1598 classFile,
1599 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
1600 "METHOD_FORMAL_PARAMETER", "Anno");
1601
1602 // checking for the annotation in the accessor
1603 Method accessor = findMethodOrFail(classFile, "s");
1604 checkTypeAnno(
1605 classFile,
1606 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
1607 "METHOD_RETURN", "Anno");
1608
1609 // checking for the annotation in the Record attribute
1610 Record_attribute record = (Record_attribute) findAttributeOrFail(classFile.attributes, Record_attribute.class);
1611 Assert.check(record.component_count == 1);
1612 checkTypeAnno(
1613 classFile,
1614 (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(
1615 record.component_info_arr[0].attributes,
1616 RuntimeVisibleTypeAnnotations_attribute.class),
1617 "FIELD", "Anno");
1618 }
1619
1620 private void checkTypeAnno(ClassFile classFile,
1621 RuntimeTypeAnnotations_attribute rtAnnos,
1622 String positionType,
1623 String annoName) throws Exception {
1624 // containing only one type annotation
1625 Assert.check(rtAnnos.annotations.length == 1);
1626 TypeAnnotation tAnno = (TypeAnnotation)rtAnnos.annotations[0];
1627 Assert.check(tAnno.position.type.toString().equals(positionType));
1628 String annotationName = classFile.constant_pool.getUTF8Value(tAnno.annotation.type_index).toString().substring(1);
1629 Assert.check(annotationName.startsWith(annoName));
1630 }
1631
1632 private void checkAnno(ClassFile classFile,
1633 RuntimeAnnotations_attribute rAnnos,
1634 String annoName) throws Exception {
1635 // containing only one type annotation
1636 Assert.check(rAnnos.annotations.length == 1);
1637 Annotation anno = (Annotation)rAnnos.annotations[0];
1638 String annotationName = classFile.constant_pool.getUTF8Value(anno.type_index).toString().substring(1);
1639 Assert.check(annotationName.startsWith(annoName));
1640 }
1641
1642 // special case for parameter annotations
1643 private void checkParameterAnno(ClassFile classFile,
1644 RuntimeVisibleParameterAnnotations_attribute rAnnos,
1645 String annoName) throws Exception {
1646 // containing only one type annotation
1647 Assert.check(rAnnos.parameter_annotations.length == 1);
1648 Assert.check(rAnnos.parameter_annotations[0].length == 1);
1649 Annotation anno = (Annotation)rAnnos.parameter_annotations[0][0];
1650 String annotationName = classFile.constant_pool.getUTF8Value(anno.type_index).toString().substring(1);
1651 Assert.check(annotationName.startsWith(annoName));
1652 }
1653
1654 private File findClassFileOrFail(File dir, String name) {
1655 for (final File fileEntry : dir.listFiles()) {
1656 if (fileEntry.getName().equals("R.class")) {
1657 return fileEntry;
1658 }
1659 }
1660 throw new AssertionError("file not found");
1661 }
1662
1663 private Method findMethodOrFail(ClassFile classFile, String name) throws Exception {
1664 for (Method method : classFile.methods) {
1665 if (method.getName(classFile.constant_pool).equals(name)) {
1666 return method;
1667 }
1668 }
1669 throw new AssertionError("method not found");
1670 }
1671
1672 private Attribute findAttributeOrFail(Attributes attributes, Class<? extends Attribute> attrClass) {
1673 for (Attribute attribute : attributes) {
1674 if (attribute.getClass() == attrClass) {
1675 return attribute;
1676 }
1677 }
1678 throw new AssertionError("attribute not found");
1679 }
1680
1681 private void assertAttributeNotPresent(Attributes attributes, Class<? extends Attribute> attrClass) {
1682 for (Attribute attribute : attributes) {
1683 if (attribute.getClass() == attrClass) {
1684 throw new AssertionError("attribute not expected");
1685 }
1686 }
1687 }
1688
1689 @SupportedAnnotationTypes("*")
1690 public static final class Processor extends JavacTestingAbstractProcessor {
1691
1692 String targets;
1693 int numberOfTypeAnnotations;
1694
1695 @Override
1696 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1697 targets = processingEnv.getOptions().get("targets");
1698 for (TypeElement te : annotations) {
1699 if (te.toString().equals("Anno")) {
1700 checkElements(te, roundEnv, targets);
1701 if (targets.contains("TYPE_USE")) {
1702 Element element = processingEnv.getElementUtils().getTypeElement("R");
1703 numberOfTypeAnnotations = 0;
1704 checkTypeAnnotations(element);
1705 Assert.check(numberOfTypeAnnotations == 4);
1706 }
1707 }
1708 }
1709 return true;
1710 }
1711
1712 void checkElements(TypeElement te, RoundEnvironment renv, String targets) {
1713 Set<? extends Element> annoElements = renv.getElementsAnnotatedWith(te);
1714 Set<String> targetSet = new HashSet<>(Arrays.asList(targets.split(",")));
1715 // we will check for type annotation in another method
1716 targetSet.remove("ElementType.TYPE_USE");
1717 for (Element e : annoElements) {
1718 Symbol s = (Symbol) e;
1719 switch (s.getKind()) {
1720 case FIELD:
1721 Assert.check(targetSet.contains("ElementType.FIELD"));
1722 targetSet.remove("ElementType.FIELD");
1723 break;
1724 case METHOD:
1725 Assert.check(targetSet.contains("ElementType.METHOD"));
1726 targetSet.remove("ElementType.METHOD");
1727 break;
1728 case PARAMETER:
1729 Assert.check(targetSet.contains("ElementType.PARAMETER"));
1730 targetSet.remove("ElementType.PARAMETER");
1731 break;
1732 case RECORD_COMPONENT:
1733 Assert.check(targetSet.contains("ElementType.RECORD_COMPONENT"));
1734 targetSet.remove("ElementType.RECORD_COMPONENT");
1735 break;
1736 default:
1737 throw new AssertionError("unexpected element kind");
1738 }
1739 }
1740 }
1741
1742 private void checkTypeAnnotations(Element rootElement) {
1743 new ElementScanner<Void, Void>() {
1744 @Override public Void visitVariable(VariableElement e, Void p) {
1745 Symbol s = (Symbol) e;
1746 if (s.getKind() == ElementKind.FIELD ||
1747 s.getKind() == ElementKind.PARAMETER &&
1748 s.name.toString().equals("s")) {
1749 int currentTAs = numberOfTypeAnnotations;
1750 verifyTypeAnnotations(e.asType().getAnnotationMirrors());
1751 Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
1752 }
1753 return null;
1754 }
1755 @Override
1756 public Void visitExecutable(ExecutableElement e, Void p) {
1757 Symbol s = (Symbol) e;
1758 if (s.getKind() == ElementKind.METHOD &&
1759 s.name.toString().equals("s")) {
1760 int currentTAs = numberOfTypeAnnotations;
1761 verifyTypeAnnotations(e.getReturnType().getAnnotationMirrors());
1762 Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
1763 }
1764 scan(e.getParameters(), p);
1765 return null;
1766 }
1767 @Override public Void visitRecordComponent(RecordComponentElement e, Void p) {
1768 int currentTAs = numberOfTypeAnnotations;
1769 verifyTypeAnnotations(e.asType().getAnnotationMirrors());
1770 Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
1771 return null;
1772 }
1773 }.scan(rootElement, null);
1774 }
1775
1776 private void verifyTypeAnnotations(Iterable<? extends AnnotationMirror> annotations) {
1777 for (AnnotationMirror mirror : annotations) {
1778 Assert.check(mirror.toString().startsWith("@Anno"));
1779 if (mirror instanceof TypeCompound) {
1780 numberOfTypeAnnotations++;
1781 }
1782 }
1783 }
1784 }
1785
1786 public void testMethodsInheritedFromRecordArePublicAndFinal() throws Exception {
1787 int numberOfFieldRefs = 0;
1788 File dir = assertOK(true, "record R() {}");
1789 for (final File fileEntry : dir.listFiles()) {
1790 if (fileEntry.getName().equals("R.class")) {
1791 ClassFile classFile = ClassFile.read(fileEntry);
1792 for (Method method : classFile.methods)
1793 switch (method.getName(classFile.constant_pool)) {
1794 case "toString", "equals", "hashCode" ->
1795 Assert.check(method.access_flags.is(AccessFlags.ACC_PUBLIC) && method.access_flags.is(AccessFlags.ACC_FINAL));
1796 default -> {}
1797 }
1798 }
1799 }
1800 }
1801
1802 private static final List<String> ACCESSIBILITY = List.of(
1803 "public", "protected", "", "private");
1804
1805 public void testCanonicalAccessibility() throws Exception {
1806 // accessibility of canonical can't be stronger than that of the record type
1807 for (String a1 : ACCESSIBILITY) {
1808 for (String a2 : ACCESSIBILITY) {
1809 if (protection(a2) > protection(a1)) {
1810 assertFail("compiler.err.invalid.canonical.constructor.in.record", "class R {# record RR() { # RR {} } }", a1, a2);
1811 } else {
1812 assertOK("class R {# record RR() { # RR {} } }", a1, a2);
1813 }
1814 }
1815 }
1816
1817 // now lets check that when compiler the compiler generates the canonical, it has the same accessibility
1818 // as the record type
1819 for (String a : ACCESSIBILITY) {
1820 File dir = assertOK(true, "class R {# record RR() {} }", a);
1821 for (final File fileEntry : dir.listFiles()) {
1822 if (fileEntry.getName().equals("R$RR.class")) {
1823 ClassFile classFile = ClassFile.read(fileEntry);
1824 for (Method method : classFile.methods)
1825 if (method.getName(classFile.constant_pool).equals("<init>")) {
1826 Assert.check(method.access_flags.flags == accessFlag(a),
1827 "was expecting access flag " + accessFlag(a) + " but found " + method.access_flags.flags);
1828 }
1829 }
1830 }
1831 }
1832 }
1833
1834 private int protection(String access) {
1835 switch (access) {
1836 case "private": return 3;
1837 case "protected": return 1;
1838 case "public": return 0;
1839 case "": return 2;
1840 default:
1841 throw new AssertionError();
1842 }
1843 }
1844
1845 private int accessFlag(String access) {
1846 switch (access) {
1847 case "private": return AccessFlags.ACC_PRIVATE;
1848 case "protected": return AccessFlags.ACC_PROTECTED;
1849 case "public": return AccessFlags.ACC_PUBLIC;
1850 case "": return 0;
1851 default:
1852 throw new AssertionError();
1853 }
1854 }
1855
1856 public void testSameArity() {
1857 for (String source : List.of(
1858 """
1859 record R(int... args) {
1860 public R(int... args) {
1861 this.args = args;
1862 }
1863 }
1864 """,
1865 """
1866 record R(int[] args) {
1867 public R(int[] args) {
1868 this.args = args;
1869 }
1870 }
1871 """,
1872 """
1873 record R(@A int... ints) {}
1874
1875 @java.lang.annotation.Target({
1876 java.lang.annotation.ElementType.TYPE_USE,
1877 java.lang.annotation.ElementType.RECORD_COMPONENT})
1878 @interface A {}
1879 """,
1880 """
1881 record R(@A int... ints) {
1882 R(@A int... ints) {
1883 this.ints = ints;
1884 }
1885 }
1886
1887 @java.lang.annotation.Target({
1888 java.lang.annotation.ElementType.TYPE_USE,
1889 java.lang.annotation.ElementType.RECORD_COMPONENT})
1890 @interface A {}
1891 """
1892 )) {
1893 assertOK(source);
1894 }
1895
1896 for (String source : List.of(
1897 """
1898 record R(int... args) {
1899 public R(int[] args) {
1900 this.args = args;
1901 }
1902 }
1903 """,
1904 """
1905 record R(int... args) {
1906 public R(int[] args) {
1907 this.args = args;
1908 }
1909 }
1910 """,
1911 """
1912 record R(String... args) {
1913 public R(String[] args) {
1914 this.args = args;
1915 }
1916 }
1917 """,
1918 """
1919 record R(String... args) {
1920 public R(String[] args) {
1921 this.args = args;
1922 }
1923 }
1924 """
1925 )) {
1926 assertFail("compiler.err.invalid.canonical.constructor.in.record", source);
1927 }
1928 }
1929
1930 public void testSafeVararsAnno() {
1931 assertFail("compiler.err.annotation.type.not.applicable",
1932 """
1933 @SafeVarargs
1934 record R<T>(T... t) {}
1935 """,
1936 """
1937 @SafeVarargs
1938 record R<T>(T... t) {
1939 R(T... t) {
1940 this.t = t;
1941 }
1942 }
1943 """
1944 );
1945
1946 assertOK(
1947 """
1948 record R<T>(T... t) {
1949 @SafeVarargs
1950 R(T... t) {
1951 this.t = t;
1952 }
1953 }
1954 """
1955 );
1956
1957 appendCompileOptions("-Xlint:unchecked");
1958 assertOKWithWarning("compiler.warn.unchecked.varargs.non.reifiable.type",
1959 """
1960 record R<T>(T... t) {
1961 R(T... t) {
1962 this.t = t;
1963 }
1964 }
1965 """
1966 );
1967 removeLastCompileOptions(1);
1968
1969 assertOK(
1970 """
1971 @SuppressWarnings("unchecked")
1972 record R<T>(T... t) {
1973 R(T... t) {
1974 this.t = t;
1975 }
1976 }
1977 """
1978 );
1979
1980 assertOK(
1981 """
1982 record R<T>(T... t) {
1983 @SuppressWarnings("unchecked")
1984 R(T... t) {
1985 this.t = t;
1986 }
1987 }
1988 """
1989 );
1990 }
1991
1992 public void testOverrideAtAccessor() {
1993 assertOK(
1994 """
1995 record R(int i) {
1996 @Override
1997 public int i() { return i; }
1998 }
1999 """,
2000 """
2001 record R(int i, int j) {
2002 @Override
2003 public int i() { return i; }
2004 public int j() { return j; }
2005 }
2006 """,
2007 """
2008 interface I { int i(); }
2009 record R(int i) implements I {
2010 @Override
2011 public int i() { return i; }
2012 }
2013 """,
2014 """
2015 interface I { int i(); }
2016 record R(int i) implements I {
2017 public int i() { return i; }
2018 }
2019 """,
2020 """
2021 interface I { default int i() { return 0; } }
2022 record R(int i) implements I {
2023 @Override
2024 public int i() { return i; }
2025 }
2026 """
2027 );
2028 }
2029
2030 public void testNoAssigmentInsideCompactRecord() {
2031 assertFail("compiler.err.cant.assign.val.to.final.var",
2032 """
2033 record R(int i) {
2034 R {
2035 this.i = i;
2036 }
2037 }
2038 """
2039 );
2040 assertFail("compiler.err.cant.assign.val.to.final.var",
2041 """
2042 record R(int i) {
2043 R {
2044 (this).i = i;
2045 }
2046 }
2047 """
2048 );
2049 }
2050
2051 public void testNoNPEStaticAnnotatedFields() {
2052 assertOK(
2053 """
2054 import java.lang.annotation.Native;
2055 record R() {
2056 @Native public static final int i = 0;
2057 }
2058 """
2059 );
2060 assertOK(
2061 """
2062 import java.lang.annotation.Native;
2063 class Outer {
2064 record R() {
2065 @Native public static final int i = 0;
2066 }
2067 }
2068 """
2069 );
2070 assertOK(
2071 """
2072 import java.lang.annotation.Native;
2073 class Outer {
2074 void m() {
2075 record R () {
2076 @Native public static final int i = 0;
2077 }
2078 }
2079 }
2080 """
2081 );
2082 }
2083
2084 public void testDoNotAllowCStyleArraySyntaxForRecComponents() {
2085 assertFail("compiler.err.record.component.and.old.array.syntax",
2086 """
2087 record R(int i[]) {}
2088 """
2089 );
2090 assertFail("compiler.err.record.component.and.old.array.syntax",
2091 """
2092 record R(String s[]) {}
2093 """
2094 );
2095 assertFail("compiler.err.record.component.and.old.array.syntax",
2096 """
2097 record R<T>(T t[]) {}
2098 """
2099 );
2100 }
2101
2102 public void testNoWarningForSerializableRecords() {
2103 if (!useAP) {
2104 // dont execute this test when the default annotation processor is on as it will fail due to
2105 // spurious warnings
2106 appendCompileOptions("-Werror", "-Xlint:serial");
2107 assertOK(
2108 """
2109 import java.io.*;
2110 record R() implements java.io.Serializable {}
2111 """
2112 );
2113 removeLastCompileOptions(2);
2114 }
2115 }
2116
2117 public void testAnnotationsOnVarargsRecComp() {
2118 assertOK(
2119 """
2120 import java.lang.annotation.*;
2121
2122 @Target({ElementType.TYPE_USE})
2123 @interface Simple {}
2124
2125 record R(@Simple int... val) {
2126 static void test() {
2127 R rec = new R(10, 20);
2128 }
2129 }
2130 """
2131 );
2132 assertOK(
2133 """
2134 import java.lang.annotation.*;
2135
2136 @Target({ElementType.TYPE_USE})
2137 @interface SimpleContainer{ Simple[] value(); }
2138
2139 @Repeatable(SimpleContainer.class)
2140 @Target({ElementType.TYPE_USE})
2141 @interface Simple {}
2142
2143 record R(@Simple int... val) {
2144 static void test() {
2145 R rec = new R(10, 20);
2146 }
2147 }
2148 """
2149 );
2150 }
2151
2152 public void testSaveVarargsAnno() {
2153 // the compiler would generate an erronous accessor
2154 assertFail("compiler.err.varargs.invalid.trustme.anno",
2155 """
2156 record R(@SafeVarargs String... s) {}
2157 """
2158 );
2159 // but this is OK
2160 assertOK(
2161 """
2162 record R(@SafeVarargs String... s) {
2163 public String[] s() { return s; }
2164 }
2165 """
2166 );
2167 }
2168 }
--- EOF ---