1 /* 2 * Copyright (c) 2018, 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 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.List; 38 39 import javax.lang.model.element.Element; 40 import javax.tools.JavaCompiler; 41 import javax.tools.SimpleJavaFileObject; 42 import javax.tools.StandardJavaFileManager; 43 import javax.tools.ToolProvider; 44 45 import com.sun.source.tree.CompilationUnitTree; 46 import com.sun.source.tree.LambdaExpressionTree; 47 import com.sun.source.tree.Scope; 48 import com.sun.source.tree.VariableTree; 49 import com.sun.source.util.JavacTask; 50 import com.sun.source.util.TreePath; 51 import com.sun.source.util.TreePathScanner; 52 import com.sun.source.util.Trees; 53 54 import com.sun.tools.javac.api.JavacTool; 55 import com.sun.tools.javac.comp.Analyzer; 56 import com.sun.tools.javac.comp.AttrContext; 57 import com.sun.tools.javac.comp.Env; 58 import com.sun.tools.javac.tree.JCTree.JCStatement; 59 import com.sun.tools.javac.util.Context; 60 import com.sun.tools.javac.util.Context.Factory; 61 62 import static javax.tools.JavaFileObject.Kind.SOURCE; 63 64 public class TestGetScopeResult { 65 public static void main(String... args) throws IOException { 66 new TestGetScopeResult().run(); 67 new TestGetScopeResult().testAnalyzerDisabled(); 68 } 69 70 public void run() throws IOException { 71 String[] simpleLambda = { 72 "s:java.lang.String", 73 "i:Test.I", 74 "super:java.lang.Object", 75 "this:Test" 76 }; 77 doTest("class Test { void test() { I i = s -> { }; } interface I { public void test(String s); } }", 78 simpleLambda); 79 doTest("class Test { void test() { I i = s -> { }; } interface I { public int test(String s); } }", 80 simpleLambda); 81 doTest("class Test { void test() { I i = s -> { }; } interface I { public String test(String s); } }", 82 simpleLambda); 83 doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public void test(String s); } }", 84 simpleLambda); 85 doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public int test(String s); } }", 86 simpleLambda); 87 doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public String test(String s); } }", 88 simpleLambda); 89 String[] dualLambda = { 90 "s:java.lang.String", 91 "i:Test.I1", 92 "super:java.lang.Object", 93 "this:Test", 94 "s:java.lang.CharSequence", 95 "i:Test.I1", 96 "super:java.lang.Object", 97 "this:Test" 98 }; 99 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); } }", 100 dualLambda); 101 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); } }", 102 dualLambda); 103 String[] brokenType = { 104 "s:<any>", 105 "u:Undefined", 106 "super:java.lang.Object", 107 "this:Test" 108 }; 109 doTest("class Test { void test() { Undefined u = s -> { }; } }", 110 brokenType); 111 String[] multipleCandidates1 = { 112 "s:<any>", 113 "super:java.lang.Object", 114 "this:Test" 115 }; 116 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); } }", 117 multipleCandidates1); 118 String[] multipleCandidates2 = { 119 "s:java.lang.String", 120 "super:java.lang.Object", 121 "this:Test" 122 }; 123 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); } }", 124 multipleCandidates2); 125 126 String[] implicitExplicitConflict1 = { 127 ":t", 128 "s:java.lang.String", 129 "super:java.lang.Object", 130 "this:Test" 131 }; 132 133 doTest("class Test { void test() { cand((var s, t) -> \"\"); } void cand(I i) { } interface I { public String test(String s); } }", 134 implicitExplicitConflict1); 135 136 String[] implicitExplicitConflict2 = { 137 "s:<any>", 138 ":t", 139 "super:java.lang.Object", 140 "this:Test" 141 }; 142 143 doTest("class Test { void test() { cand((t, var s) -> \"\"); } void cand(I i) { } interface I { public String test(String s); } }", 144 implicitExplicitConflict2); 145 } 146 147 public void doTest(String code, String... expected) throws IOException { 148 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 149 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 150 class MyFileObject extends SimpleJavaFileObject { 151 MyFileObject() { 152 super(URI.create("myfo:///Test.java"), SOURCE); 153 } 154 @Override 155 public String getCharContent(boolean ignoreEncodingErrors) { 156 return code; 157 } 158 } 159 JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, List.of(new MyFileObject())); 160 CompilationUnitTree cut = t.parse().iterator().next(); 161 t.analyze(); 162 163 List<String> actual = new ArrayList<>(); 164 165 new TreePathScanner<Void, Void>() { 166 @Override 167 public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { 168 Scope scope = Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getBody())); 169 while (scope.getEnclosingClass() != null) { 170 for (Element el : scope.getLocalElements()) { 171 actual.add(el.getSimpleName() + ":" +el.asType().toString()); 172 } 173 scope = scope.getEnclosingScope(); 174 } 175 return super.visitLambdaExpression(node, p); 176 } 177 }.scan(cut, null); 178 179 List<String> expectedList = List.of(expected); 180 181 if (!expectedList.equals(actual)) { 182 throw new IllegalStateException("Unexpected scope content: " + actual); 183 } 184 } 185 } 186 187 void testAnalyzerDisabled() throws IOException { 188 JavacTool c = JavacTool.create(); 189 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 190 class MyFileObject extends SimpleJavaFileObject { 191 MyFileObject() { 192 super(URI.create("myfo:///Test.java"), SOURCE); 193 } 194 @Override 195 public String getCharContent(boolean ignoreEncodingErrors) { 196 return "class Test {" + 197 " void test() { cand(() -> { System.err.println(); }); }" + 198 " Runnable r = new Runnable() { public void test() { System.err.println(); } };" + 199 " void cand(Runnable r) { }" + 200 "}"; 201 } 202 } 203 Context ctx = new Context(); 204 TestAnalyzer.preRegister(ctx); 205 JavacTask t = (JavacTask) c.getTask(null, fm, null, List.of("-XDfind=lambda"), null, 206 List.of(new MyFileObject()), ctx); 207 CompilationUnitTree cut = t.parse().iterator().next(); 208 t.analyze(); 209 210 TestAnalyzer analyzer = (TestAnalyzer) TestAnalyzer.instance(ctx); 211 212 if (!analyzer.analyzeCalled) { 213 throw new IllegalStateException("Analyzer didn't run!"); 214 } 215 216 new TreePathScanner<Void, Void>() { 217 @Override 218 public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { 219 analyzer.analyzeCalled = false; 220 Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getBody())); 221 if (analyzer.analyzeCalled) { 222 throw new IllegalStateException("Analyzer was run during getScope!"); 223 } 224 return super.visitLambdaExpression(node, p); 225 } 226 227 @Override 228 public Void visitVariable(VariableTree node, Void p) { 229 if (node.getInitializer() != null) { 230 analyzer.analyzeCalled = false; 231 TreePath tp = new TreePath(getCurrentPath(), node.getInitializer()); 232 Trees.instance(t).getScope(tp); 233 if (analyzer.analyzeCalled) { 234 throw new IllegalStateException("Analyzer was run during getScope!"); 235 } 236 } 237 return super.visitVariable(node, p); 238 } 239 }.scan(cut, null); 240 } 241 } 242 243 private static final class TestAnalyzer extends Analyzer { 244 245 public static void preRegister(Context context) { 246 context.put(analyzerKey, (Factory<Analyzer>) ctx -> new TestAnalyzer(ctx)); 247 } 248 249 private boolean analyzeCalled; 250 251 public TestAnalyzer(Context context) { 252 super(context); 253 } 254 255 @Override 256 protected void analyze(JCStatement statement, Env<AttrContext> env) { 257 analyzeCalled = true; 258 super.analyze(statement, env); 259 } 260 } 261 } 262