1 /* 2 * Copyright (c) 2018, 2020, 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 * @test 26 * @bug 8205418 8207229 8207230 8230847 8245786 8247334 8248641 27 * @summary Test the outcomes from Trees.getScope 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.comp 30 * jdk.compiler/com.sun.tools.javac.tree 31 * jdk.compiler/com.sun.tools.javac.util 32 */ 33 34 import java.io.IOException; 35 import java.net.URI; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.List; 39 40 import javax.lang.model.element.Element; 41 import javax.tools.JavaCompiler; 42 import javax.tools.SimpleJavaFileObject; 43 import javax.tools.StandardJavaFileManager; 44 import javax.tools.ToolProvider; 45 46 import com.sun.source.tree.AnnotationTree; 47 import com.sun.source.tree.BlockTree; 48 import com.sun.source.tree.ClassTree; 49 import com.sun.source.tree.CompilationUnitTree; 50 import com.sun.source.tree.ConditionalExpressionTree; 51 import com.sun.source.tree.IdentifierTree; 52 import com.sun.source.tree.LambdaExpressionTree; 53 import com.sun.source.tree.MethodInvocationTree; 54 import com.sun.source.tree.MethodTree; 55 import com.sun.source.tree.Scope; 56 import com.sun.source.tree.Tree; 57 import com.sun.source.tree.VariableTree; 58 import com.sun.source.util.JavacTask; 59 import com.sun.source.util.TaskEvent; 60 import com.sun.source.util.TaskListener; 61 import com.sun.source.util.TreePath; 62 import com.sun.source.util.TreePathScanner; 63 import com.sun.source.util.Trees; 64 import com.sun.tools.javac.api.JavacScope; 65 66 import com.sun.tools.javac.api.JavacTool; 67 import com.sun.tools.javac.comp.Analyzer; 68 import com.sun.tools.javac.comp.AttrContext; 69 import com.sun.tools.javac.comp.Env; 70 import com.sun.tools.javac.tree.JCTree; 71 import com.sun.tools.javac.tree.JCTree.JCCase; 72 import com.sun.tools.javac.tree.JCTree.JCStatement; 73 import com.sun.tools.javac.util.Context; 74 import com.sun.tools.javac.util.Context.Factory; 75 76 import static javax.tools.JavaFileObject.Kind.SOURCE; 77 78 public class TestGetScopeResult { 79 public static void main(String... args) throws IOException { 80 new TestGetScopeResult().run(); 81 new TestGetScopeResult().testAnalyzerDisabled(); 82 new TestGetScopeResult().testVariablesInSwitch(); 83 new TestGetScopeResult().testMemberRefs(); 84 new TestGetScopeResult().testAnnotations(); 85 new TestGetScopeResult().testAnnotationsLazy(); 86 new TestGetScopeResult().testCircular(); 87 new TestGetScopeResult().testRecord(); 88 new TestGetScopeResult().testLocalRecordAnnotation(); 89 new TestGetScopeResult().testRuleCases(); 90 } 91 92 public void run() throws IOException { 93 String[] simpleLambda = { 94 "s:java.lang.String", 95 "i:Test.I", 96 "super:java.lang.Object", 97 "this:Test" 98 }; 99 doTest("class Test { void test() { I i = s -> { }; } interface I { public void test(String s); } }", 100 simpleLambda); 101 doTest("class Test { void test() { I i = s -> { }; } interface I { public int test(String s); } }", 102 simpleLambda); 103 doTest("class Test { void test() { I i = s -> { }; } interface I { public String test(String s); } }", 104 simpleLambda); 105 doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public void test(String s); } }", 106 simpleLambda); 107 doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public int test(String s); } }", 108 simpleLambda); 109 doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public String test(String s); } }", 110 simpleLambda); 111 String[] dualLambda = { 112 "s:java.lang.String", 113 "i:Test.I1", 114 "super:java.lang.Object", 115 "this:Test", 116 "s:java.lang.CharSequence", 117 "i:Test.I1", 118 "super:java.lang.Object", 119 "this:Test" 120 }; 121 doTest("class Test { void test() { I1 i; inv(s -> { }, s -> { }); } void inv(I1 i, I2 i) { } interface I1 { public String test(String s); } interface I2 { public void test(CharSequence s); } }", 122 dualLambda); 123 doTest("class Test { void test() { I1 i; inv(s -> { }, s -> { }); } void inv(I1 i, I2 i) { } interface I1 { public String test(String s); } interface I2 { public int test(CharSequence s); } }", 124 dualLambda); 125 String[] brokenType = { 126 "s:<any>", 127 "u:Undefined", 128 "super:java.lang.Object", 129 "this:Test" 130 }; 131 doTest("class Test { void test() { Undefined u = s -> { }; } }", 132 brokenType); 133 String[] multipleCandidates1 = { 134 "s:<any>", 135 "super:java.lang.Object", 136 "this:Test" 137 }; 138 doTest("class Test { void test() { cand1(s -> { }); } void cand1(I1 i) { } void cand1(I2 i) { } interface I1 { public String test(String s); } interface I2 { public int test(CharSequence s); } }", 139 multipleCandidates1); 140 String[] multipleCandidates2 = { 141 "s:java.lang.String", 142 "super:java.lang.Object", 143 "this:Test" 144 }; 145 doTest("class Test { void test() { cand1(s -> { }); } void cand1(I1 i) { } void cand1(I2 i, int i) { } interface I1 { public String test(String s); } interface I2 { public int test(CharSequence s); } }", 146 multipleCandidates2); 147 148 String[] implicitExplicitConflict1 = { 149 ":t", 150 "s:java.lang.String", 151 "super:java.lang.Object", 152 "this:Test" 153 }; 154 155 doTest("class Test { void test() { cand((var s, t) -> \"\"); } void cand(I i) { } interface I { public String test(String s); } }", 156 implicitExplicitConflict1); 157 158 String[] implicitExplicitConflict2 = { 159 "s:none", 160 ":t", 161 "super:java.lang.Object", 162 "this:Test" 163 }; 164 165 doTest("class Test { void test() { cand((t, var s) -> \"\"); } void cand(I i) { } interface I { public String test(String s); } }", 166 implicitExplicitConflict2); 167 168 String[] noFunctionInterface = { 169 "s:none", 170 ":t", 171 "super:java.lang.Object", 172 "this:Test" 173 }; 174 175 doTest("class Test { void test() { cand((t, var s) -> \"\"); } void cand(String s) { } }", 176 noFunctionInterface); 177 178 String[] invocationInMethodInvocation = { 179 "d2:java.lang.Double", 180 "d1:java.lang.Double", 181 "super:java.lang.Object", 182 "this:Test" 183 }; 184 185 doTest(""" 186 class Test { 187 void test() { test(reduce(0.0, (d1, d2) -> 0)); } 188 void test(int i) {} 189 <T> T reduce(T t, BiFunction<T, T, T> f1) {} 190 static interface BiFunction<R, P, Q> { 191 R apply(P p, Q q); 192 } 193 }""", 194 invocationInMethodInvocation); 195 } 196 197 public void doTest(String code, String... expected) throws IOException { 198 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 199 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 200 class MyFileObject extends SimpleJavaFileObject { 201 MyFileObject() { 202 super(URI.create("myfo:///Test.java"), SOURCE); 203 } 204 @Override 205 public String getCharContent(boolean ignoreEncodingErrors) { 206 return code; 207 } 208 } 209 JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, List.of(new MyFileObject())); 210 CompilationUnitTree cut = t.parse().iterator().next(); 211 t.analyze(); 212 213 List<String> actual = new ArrayList<>(); 214 215 new TreePathScanner<Void, Void>() { 216 @Override 217 public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { 218 Scope scope = Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getBody())); 219 actual.addAll(dumpScope(scope)); 220 return super.visitLambdaExpression(node, p); 221 } 222 }.scan(cut, null); 223 224 List<String> expectedList = List.of(expected); 225 226 if (!expectedList.equals(actual)) { 227 throw new IllegalStateException("Unexpected scope content: " + actual + "\n" + 228 "expected: " + expectedList); 229 } 230 } 231 } 232 233 void testAnalyzerDisabled() throws IOException { 234 JavacTool c = JavacTool.create(); 235 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 236 class MyFileObject extends SimpleJavaFileObject { 237 MyFileObject() { 238 super(URI.create("myfo:///Test.java"), SOURCE); 239 } 240 @Override 241 public String getCharContent(boolean ignoreEncodingErrors) { 242 return "class Test {" + 243 " void test() { cand(() -> { System.err.println(); }); }" + 244 " Runnable r = new Runnable() { public void test() { System.err.println(); } };" + 245 " void cand(Runnable r) { }" + 246 "}"; 247 } 248 } 249 Context ctx = new Context(); 250 TestAnalyzer.preRegister(ctx); 251 JavacTask t = (JavacTask) c.getTask(null, fm, null, List.of("-XDfind=lambda"), null, 252 List.of(new MyFileObject()), ctx); 253 CompilationUnitTree cut = t.parse().iterator().next(); 254 t.analyze(); 255 256 TestAnalyzer analyzer = (TestAnalyzer) TestAnalyzer.instance(ctx); 257 258 if (!analyzer.analyzeCalled) { 259 throw new IllegalStateException("Analyzer didn't run!"); 260 } 261 262 new TreePathScanner<Void, Void>() { 263 @Override 264 public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { 265 analyzer.analyzeCalled = false; 266 Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getBody())); 267 if (analyzer.analyzeCalled) { 268 throw new IllegalStateException("Analyzer was run during getScope!"); 269 } 270 return super.visitLambdaExpression(node, p); 271 } 272 273 @Override 274 public Void visitVariable(VariableTree node, Void p) { 275 if (node.getInitializer() != null) { 276 analyzer.analyzeCalled = false; 277 TreePath tp = new TreePath(getCurrentPath(), node.getInitializer()); 278 Trees.instance(t).getScope(tp); 279 if (analyzer.analyzeCalled) { 280 throw new IllegalStateException("Analyzer was run during getScope!"); 281 } 282 } 283 return super.visitVariable(node, p); 284 } 285 }.scan(cut, null); 286 } 287 } 288 289 private static final class TestAnalyzer extends Analyzer { 290 291 public static void preRegister(Context context) { 292 context.put(analyzerKey, (Factory<Analyzer>) ctx -> new TestAnalyzer(ctx)); 293 } 294 295 private boolean analyzeCalled; 296 297 public TestAnalyzer(Context context) { 298 super(context); 299 } 300 301 @Override 302 protected void analyze(JCStatement statement, Env<AttrContext> env) { 303 analyzeCalled = true; 304 super.analyze(statement, env); 305 } 306 } 307 308 void testVariablesInSwitch() throws IOException { 309 JavacTool c = JavacTool.create(); 310 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 311 class MyFileObject extends SimpleJavaFileObject { 312 MyFileObject() { 313 super(URI.create("myfo:///Test.java"), SOURCE); 314 } 315 @Override 316 public String getCharContent(boolean ignoreEncodingErrors) { 317 return "class Test {" + 318 " void test() {\n" + 319 " E e = E.A;\n" + 320 " Object o = E.A;\n" + 321 " switch (e) {\n" + 322 " case A:\n" + 323 " return;\n" + 324 " case B:\n" + 325 " test();\n" + 326 " E ee = null;\n" + 327 " break;\n" + 328 " }\n" + 329 " }\n" + 330 " enum E {A, B}\n" + 331 "}"; 332 } 333 } 334 Context ctx = new Context(); 335 TestAnalyzer.preRegister(ctx); 336 JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, 337 List.of(new MyFileObject()), ctx); 338 CompilationUnitTree cut = t.parse().iterator().next(); 339 t.analyze(); 340 341 new TreePathScanner<Void, Void>() { 342 @Override 343 public Void visitMethodInvocation(MethodInvocationTree node, Void p) { 344 Trees.instance(t).getScope(getCurrentPath()); 345 return super.visitMethodInvocation(node, p); 346 } 347 }.scan(cut, null); 348 } 349 } 350 351 void testMemberRefs() throws IOException { 352 JavacTool c = JavacTool.create(); 353 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 354 class MyFileObject extends SimpleJavaFileObject { 355 MyFileObject() { 356 super(URI.create("myfo:///Test.java"), SOURCE); 357 } 358 @Override 359 public String getCharContent(boolean ignoreEncodingErrors) { 360 return "class Test {" + 361 " void test() {\n" + 362 " Test t = this;\n" + 363 " Runnable r1 = t::test;\n" + 364 " Runnable r2 = true ? t::test : t::test;\n" + 365 " c(t::test);\n" + 366 " c(true ? t::test : t::test);\n" + 367 " }\n" + 368 " void c(Runnable r) {}\n" + 369 "}"; 370 } 371 } 372 Context ctx = new Context(); 373 TestAnalyzer.preRegister(ctx); 374 JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, 375 List.of(new MyFileObject()), ctx); 376 CompilationUnitTree cut = t.parse().iterator().next(); 377 t.analyze(); 378 379 new TreePathScanner<Void, Void>() { 380 @Override 381 public Void visitConditionalExpression(ConditionalExpressionTree node, Void p) { 382 Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getCondition())); 383 return super.visitConditionalExpression(node, p); 384 } 385 386 @Override 387 public Void visitBlock(BlockTree node, Void p) { 388 Trees.instance(t).getScope(getCurrentPath()); 389 return super.visitBlock(node, p); 390 } 391 }.scan(cut, null); 392 } 393 } 394 395 void testAnnotations() throws IOException { 396 JavacTool c = JavacTool.create(); 397 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 398 class MyFileObject extends SimpleJavaFileObject { 399 MyFileObject() { 400 super(URI.create("myfo:///Test.java"), SOURCE); 401 } 402 @Override 403 public String getCharContent(boolean ignoreEncodingErrors) { 404 return "class Test {" + 405 " void test() {\n" + 406 " new Object() {\n" + 407 " @A\n" + 408 " public String t() { return null; }\n" + 409 " };\n" + 410 " }\n" + 411 " @interface A {}\n" + 412 "}"; 413 } 414 } 415 Context ctx = new Context(); 416 TestAnalyzer.preRegister(ctx); 417 JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, 418 List.of(new MyFileObject()), ctx); 419 CompilationUnitTree cut = t.parse().iterator().next(); 420 t.analyze(); 421 422 new TreePathScanner<Void, Void>() { 423 @Override 424 public Void visitIdentifier(IdentifierTree node, Void p) { 425 if (node.getName().contentEquals("A")) { 426 Trees.instance(t).getScope(getCurrentPath()); 427 } 428 return super.visitIdentifier(node, p); 429 } 430 431 @Override 432 public Void visitMethod(MethodTree node, Void p) { 433 super.visitMethod(node, p); 434 if (node.getReturnType() != null) { 435 Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getReturnType())); 436 } 437 return null; 438 } 439 }.scan(cut, null); 440 } 441 } 442 443 void testAnnotationsLazy() throws IOException { 444 JavacTool c = JavacTool.create(); 445 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 446 class MyFileObject extends SimpleJavaFileObject { 447 MyFileObject() { 448 super(URI.create("myfo:///Test.java"), SOURCE); 449 } 450 @Override 451 public String getCharContent(boolean ignoreEncodingErrors) { 452 return "import java.lang.annotation.*;\n" + 453 "\n" + 454 "class ClassA {\n" + 455 " Object o = ClassB.lcv;\n" + 456 "}\n" + 457 "\n" + 458 "class ClassB {\n" + 459 " static final String[] lcv = new @TA String[0];\n" + 460 "}\n" + 461 "\n" + 462 "class ClassC {\n" + 463 " static final Object o = (@TA Object) null;\n" + 464 "}\n" + 465 "\n" + 466 "@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})\n" + 467 "@interface TA {}\n"; 468 } 469 } 470 Context ctx = new Context(); 471 TestAnalyzer.preRegister(ctx); 472 JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, 473 List.of(new MyFileObject()), ctx); 474 t.addTaskListener(new TaskListener() { 475 @Override 476 public void finished(TaskEvent e) { 477 if (e.getKind() == TaskEvent.Kind.ANALYZE) { 478 new TreePathScanner<Void, Void>() { 479 @Override 480 public Void scan(Tree tree, Void p) { 481 if (tree != null) { 482 Trees.instance(t).getScope(new TreePath(getCurrentPath(), tree)); 483 } 484 return super.scan(tree, p); 485 } 486 }.scan(Trees.instance(t).getPath(e.getTypeElement()), null); 487 } 488 } 489 }); 490 491 t.call(); 492 } 493 } 494 495 void testCircular() throws IOException { 496 JavacTool c = JavacTool.create(); 497 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 498 class MyFileObject extends SimpleJavaFileObject { 499 MyFileObject() { 500 super(URI.create("myfo:///Test.java"), SOURCE); 501 } 502 @Override 503 public String getCharContent(boolean ignoreEncodingErrors) { 504 return "class Test extends Test {" + 505 " {\n" + 506 " int i;\n" + 507 " }\n" + 508 "}"; 509 } 510 } 511 Context ctx = new Context(); 512 TestAnalyzer.preRegister(ctx); 513 JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, 514 List.of(new MyFileObject()), ctx); 515 CompilationUnitTree cut = t.parse().iterator().next(); 516 t.analyze(); 517 518 new TreePathScanner<Void, Void>() { 519 @Override 520 public Void visitBlock(BlockTree node, Void p) { 521 Trees.instance(t).getScope(getCurrentPath()); 522 return super.visitBlock(node, p); 523 } 524 }.scan(cut, null); 525 } 526 } 527 528 void testRecord() throws IOException { 529 JavacTool c = JavacTool.create(); 530 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 531 class MyFileObject extends SimpleJavaFileObject { 532 MyFileObject() { 533 super(URI.create("myfo:///Test.java"), SOURCE); 534 } 535 @Override 536 public String getCharContent(boolean ignoreEncodingErrors) { 537 return "record Test<T>(int mark) {}"; 538 } 539 } 540 Context ctx = new Context(); 541 TestAnalyzer.preRegister(ctx); 542 List<String> options = List.of("--enable-preview", 543 "-source", System.getProperty("java.specification.version")); 544 JavacTask t = (JavacTask) c.getTask(null, fm, null, options, null, 545 List.of(new MyFileObject()), ctx); 546 CompilationUnitTree cut = t.parse().iterator().next(); 547 t.analyze(); 548 549 List<String> actual = new ArrayList<>(); 550 551 new TreePathScanner<Void, Void>() { 552 @Override 553 public Void visitClass(ClassTree node, Void p) { 554 Scope scope = Trees.instance(t).getScope(getCurrentPath()); 555 actual.addAll(dumpScope(scope)); 556 return super.visitClass(node, p); 557 } 558 }.scan(cut, null); 559 560 List<String> expected = List.of( 561 "super:java.lang.Record", 562 "this:Test<T>", 563 "T:T" 564 ); 565 566 if (!expected.equals(actual)) { 567 throw new AssertionError("Unexpected Scope content: " + actual); 568 } 569 } 570 } 571 572 void testLocalRecordAnnotation() throws IOException { 573 JavacTool c = JavacTool.create(); 574 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 575 class Variant { 576 final String code; 577 final List<List<String>> expectedScopeContent; 578 public Variant(String code, List<List<String>> expectedScopeContent) { 579 this.code = code; 580 this.expectedScopeContent = expectedScopeContent; 581 } 582 } 583 Variant[] variants = new Variant[] { 584 new Variant(""" 585 class Test { 586 void t() { 587 record R(@Annotation int i) { 588 void stop () {} 589 } 590 } 591 } 592 @interface Annotation {} 593 """, 594 List.of( 595 List.of("super:java.lang.Object", "this:Test"), 596 List.of("super:java.lang.Object", "this:Test") 597 )), 598 new Variant(""" 599 record Test(@Annotation int i) {} 600 @interface Annotation {} 601 """, 602 List.of( 603 List.of("i:int", "super:java.lang.Record", "this:Test"), 604 List.of("super:java.lang.Record", "this:Test") 605 )) 606 }; 607 for (Variant currentVariant : variants) { 608 class MyFileObject extends SimpleJavaFileObject { 609 MyFileObject() { 610 super(URI.create("myfo:///Test.java"), SOURCE); 611 } 612 @Override 613 public String getCharContent(boolean ignoreEncodingErrors) { 614 return currentVariant.code; 615 } 616 } 617 Context ctx = new Context(); 618 TestAnalyzer.preRegister(ctx); 619 List<String> options = List.of("--enable-preview", 620 "-source", System.getProperty("java.specification.version")); 621 JavacTask t = (JavacTask) c.getTask(null, fm, null, options, null, 622 List.of(new MyFileObject()), ctx); 623 CompilationUnitTree cut = t.parse().iterator().next(); 624 t.analyze(); 625 626 List<List<String>> actual = new ArrayList<>(); 627 628 new TreePathScanner<Void, Void>() { 629 @Override 630 public Void visitAnnotation(AnnotationTree node, Void p) { 631 Scope scope = Trees.instance(t).getScope(getCurrentPath()); 632 actual.add(dumpScope(scope)); 633 return super.visitAnnotation(node, p); 634 } 635 }.scan(cut, null); 636 637 if (!currentVariant.expectedScopeContent.equals(actual)) { 638 throw new AssertionError("Unexpected Scope content: " + actual); 639 } 640 } 641 } 642 } 643 644 void testRuleCases() throws IOException { 645 JavacTool c = JavacTool.create(); 646 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 647 String code = """ 648 class Test { 649 void t(int i) { 650 long local; 651 System.err.println(switch (i) { 652 case 0 -> { 653 String var; 654 int scopeHere; 655 yield ""; 656 } 657 default -> { 658 String var; 659 int scopeHere; 660 yield ""; 661 } 662 }); 663 switch (i) { 664 case 0 -> { 665 String var; 666 int scopeHere; 667 } 668 default -> { 669 String var; 670 int scopeHere; 671 } 672 }; 673 switch (i) { 674 case 0: { 675 int checkTree; 676 } 677 } 678 } 679 } 680 """; 681 class MyFileObject extends SimpleJavaFileObject { 682 MyFileObject() { 683 super(URI.create("myfo:///Test.java"), SOURCE); 684 } 685 @Override 686 public String getCharContent(boolean ignoreEncodingErrors) { 687 return code; 688 } 689 } 690 Context ctx = new Context(); 691 TestAnalyzer.preRegister(ctx); 692 List<String> options = List.of("--enable-preview", 693 "-source", System.getProperty("java.specification.version")); 694 JavacTask t = (JavacTask) c.getTask(null, fm, null, options, null, 695 List.of(new MyFileObject()), ctx); 696 CompilationUnitTree cut = t.parse().iterator().next(); 697 t.analyze(); 698 699 List<List<String>> actual = new ArrayList<>(); 700 701 new TreePathScanner<Void, Void>() { 702 @Override 703 public Void visitVariable(VariableTree node, Void p) { 704 if (node.getName().contentEquals("scopeHere")) { 705 Scope scope = Trees.instance(t).getScope(getCurrentPath()); 706 actual.add(dumpScope(scope)); 707 } else if (node.getName().contentEquals("checkTree")) { 708 Scope scope = Trees.instance(t).getScope(getCurrentPath()); 709 JCTree body = 710 ((JCCase) ((JavacScope) scope).getEnv().next.next.tree).body; 711 if (body != null) { 712 throw new AssertionError("Unexpected body tree: " + body); 713 } 714 } 715 return super.visitVariable(node, p); 716 } 717 }.scan(cut, null); 718 719 List<List<String>> expected = 720 Collections.nCopies(4, 721 List.of("scopeHere:int", 722 "var:java.lang.String", 723 "local:long", 724 "i:int", 725 "super:java.lang.Object", 726 "this:Test" 727 )); 728 729 if (!expected.equals(actual)) { 730 throw new AssertionError("Unexpected Scope content: " + actual); 731 } 732 } 733 } 734 735 private List<String> dumpScope(Scope scope) { 736 List<String> content = new ArrayList<>(); 737 while (scope.getEnclosingClass() != null) { 738 for (Element el : scope.getLocalElements()) { 739 content.add(el.getSimpleName() + ":" +el.asType().toString()); 740 } 741 scope = scope.getEnclosingScope(); 742 } 743 return content; 744 } 745 }