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 }