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