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