8223443: Calling Trees.getScope early changes names of local/anonymous classes
Summary: Ensure Trees.getScope does not affect the rest of the compilation.
Reviewed-by: mcimadamore
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Mon Jul 08 16:20:40 2019 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Tue Jul 09 09:20:04 2019 +0200
@@ -28,13 +28,12 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.BreakIterator;
-import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
@@ -60,9 +59,8 @@
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
-import com.sun.source.doctree.EndElementTree;
-import com.sun.source.doctree.StartElementTree;
import com.sun.source.tree.CatchTree;
+import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Scope;
import com.sun.source.tree.Tree;
@@ -71,13 +69,11 @@
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import com.sun.source.util.JavacTask;
-import com.sun.source.util.SimpleDocTreeVisitor;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Scope.NamedImportScope;
import com.sun.tools.javac.code.Scope.StarImportScope;
import com.sun.tools.javac.code.Scope.WriteableScope;
-import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
@@ -94,11 +90,13 @@
import com.sun.tools.javac.code.Types.TypeRelation;
import com.sun.tools.javac.comp.Attr;
import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Check;
import com.sun.tools.javac.comp.Enter;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.comp.MemberEnter;
import com.sun.tools.javac.comp.Modules;
import com.sun.tools.javac.comp.Resolve;
+import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.file.BaseFileManager;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.parser.DocCommentParser;
@@ -133,6 +131,7 @@
import com.sun.tools.javac.tree.TreeCopier;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Abort;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context;
@@ -171,6 +170,7 @@
private Log log;
private MemberEnter memberEnter;
private Attr attr;
+ private Check chk;
private TreeMaker treeMaker;
private JavacElements elements;
private JavacTaskImpl javacTaskImpl;
@@ -218,6 +218,7 @@
private void init(Context context) {
modules = Modules.instance(context);
attr = Attr.instance(context);
+ chk = Check.instance(context);
enter = Enter.instance(context);
elements = JavacElements.instance(context);
log = Log.instance(context);
@@ -915,13 +916,13 @@
try {
Assert.check(method.body == tree);
method.body = copier.copy((JCBlock)tree, (JCTree) path.getLeaf());
- env = attribStatToTree(method.body, env, copier.leafCopy);
+ env = attribStatToTree(method.body, env, copier.leafCopy, copier.copiedClasses);
} finally {
method.body = (JCBlock) tree;
}
} else {
JCBlock body = copier.copy((JCBlock)tree, (JCTree) path.getLeaf());
- env = attribStatToTree(body, env, copier.leafCopy);
+ env = attribStatToTree(body, env, copier.leafCopy, copier.copiedClasses);
}
return env;
}
@@ -930,7 +931,7 @@
if (field != null && field.getInitializer() == tree) {
env = memberEnter.getInitEnv(field, env);
JCExpression expr = copier.copy((JCExpression)tree, (JCTree) path.getLeaf());
- env = attribExprToTree(expr, env, copier.leafCopy);
+ env = attribExprToTree(expr, env, copier.leafCopy, copier.copiedClasses);
return env;
}
}
@@ -938,24 +939,137 @@
return (field != null) ? memberEnter.getInitEnv(field, env) : env;
}
- private Env<AttrContext> attribStatToTree(JCTree stat, Env<AttrContext>env, JCTree tree) {
+ private Env<AttrContext> attribStatToTree(JCTree stat, Env<AttrContext>env,
+ JCTree tree, Map<JCClassDecl, JCClassDecl> copiedClasses) {
JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
+ Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
try {
- return attr.attribStatToTree(stat, env, tree);
+ Env<AttrContext> result = attr.attribStatToTree(stat, env, tree);
+
+ enter.unenter(env.toplevel, stat);
+ fixLocalClassNames(copiedClasses, env);
+ return result;
} finally {
+ log.popDiagnosticHandler(diagHandler);
+ log.useSource(prev);
+ }
+ }
+
+ private Env<AttrContext> attribExprToTree(JCExpression expr, Env<AttrContext>env,
+ JCTree tree, Map<JCClassDecl, JCClassDecl> copiedClasses) {
+ JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
+ Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
+ try {
+ Env<AttrContext> result = attr.attribExprToTree(expr, env, tree);
+
+ enter.unenter(env.toplevel, expr);
+ fixLocalClassNames(copiedClasses, env);
+ return result;
+ } finally {
+ log.popDiagnosticHandler(diagHandler);
log.useSource(prev);
}
}
- private Env<AttrContext> attribExprToTree(JCExpression expr, Env<AttrContext>env, JCTree tree) {
- JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
- try {
- return attr.attribExprToTree(expr, env, tree);
- } finally {
- log.useSource(prev);
+ /* Change the flatnames of the local and anonymous classes in the Scope to
+ * the names they would have if the whole file was attributed normally.
+ */
+ private void fixLocalClassNames(Map<JCClassDecl, JCClassDecl> copiedClasses,
+ Env<AttrContext> lastEnv) {
+ Map<JCClassDecl, Name> flatnameForClass = null;
+
+ for (Entry<JCClassDecl, JCClassDecl> e : copiedClasses.entrySet()) {
+ if (e.getKey().sym != null) {
+ Name origName;
+ if (e.getValue().sym != null) {
+ //if the source tree was already attributed, use the flatname
+ //from the source tree's Symbol:
+ origName = e.getValue().sym.flatname;
+ } else {
+ //otherwise, compute the flatnames (for source trees) as
+ //if the full source code would be attributed:
+ if (flatnameForClass == null) {
+ flatnameForClass = prepareFlatnameForClass(lastEnv);
+ }
+ origName = flatnameForClass.get(e.getValue());
+ }
+ if (origName != null) {
+ e.getKey().sym.flatname = origName;
+ }
+ }
}
}
+ /* This method computes and assigns flatnames to trees, as if they would be
+ * normally assigned during attribution of the full source code.
+ */
+ private Map<JCTree.JCClassDecl, Name> prepareFlatnameForClass(Env<AttrContext> env) {
+ Map<JCClassDecl, Name> flatNameForClass = new HashMap<>();
+ Symbol enclClass = env.enclClass.sym;
+
+ if (enclClass != null && (enclClass.flags_field & Flags.UNATTRIBUTED) != 0) {
+ ListBuffer<ClassSymbol> toClear = new ListBuffer<>();
+ new TreeScanner() {
+ Symbol owner;
+ boolean localContext;
+ @Override
+ public void visitClassDef(JCClassDecl tree) {
+ //compute the name (and ClassSymbol) which would be used
+ //for this class for full attribution
+ Symbol prevOwner = owner;
+ try {
+ ClassSymbol c;
+ if (tree.sym != null) {
+ //already entered:
+ c = tree.sym;
+ } else {
+ c = syms.defineClass(tree.name, owner);
+ if (owner.kind != TYP) {
+ //for local classes, assign the flatname
+ c.flatname = chk.localClassName(c);
+ chk.putCompiled(c);
+ toClear.add(c);
+ }
+ flatNameForClass.put(tree, c.flatname);
+ }
+ owner = c;
+ super.visitClassDef(tree);
+ } finally {
+ owner = prevOwner;
+ }
+ }
+
+ @Override
+ public void visitBlock(JCBlock tree) {
+ Symbol prevOwner = owner;
+ try {
+ owner = new MethodSymbol(0, names.empty, Type.noType, owner);
+ super.visitBlock(tree);
+ } finally {
+ owner = prevOwner;
+ }
+ }
+ @Override
+ public void visitVarDef(JCVariableDecl tree) {
+ Symbol prevOwner = owner;
+ try {
+ owner = new MethodSymbol(0, names.empty, Type.noType, owner);
+ super.visitVarDef(tree);
+ } finally {
+ owner = prevOwner;
+ }
+ }
+ }.scan(env.enclClass);
+ //revert changes done by the visitor:
+ toClear.stream().forEach(c -> {
+ chk.clearLocalClassNameIndexes(c);
+ chk.removeCompiled(c);
+ });
+ }
+
+ return flatNameForClass;
+ }
+
static JavaFileObject asJavaFileObject(FileObject fileObject) {
JavaFileObject jfo = null;
@@ -1065,6 +1179,7 @@
**/
protected static class Copier extends TreeCopier<JCTree> {
JCTree leafCopy = null;
+ private Map<JCClassDecl, JCClassDecl> copiedClasses = new HashMap<>();
protected Copier(TreeMaker M) {
super(M);
@@ -1077,6 +1192,14 @@
leafCopy = t2;
return t2;
}
+
+ @Override
+ public JCTree visitClass(ClassTree node, JCTree p) {
+ JCTree nue = super.visitClass(node, p);
+ copiedClasses.put((JCClassDecl) nue, (JCClassDecl) node);
+ return nue;
+ }
+
}
protected Copier createCopier(TreeMaker maker) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Mon Jul 08 16:20:40 2019 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Tue Jul 09 09:20:04 2019 +0200
@@ -407,7 +407,7 @@
* enclClass is the flat name of the enclosing class,
* classname is the simple name of the local class
*/
- Name localClassName(ClassSymbol c) {
+ public Name localClassName(ClassSymbol c) {
Name enclFlatname = c.owner.enclClass().flatname;
String enclFlatnameStr = enclFlatname.toString();
Pair<Name, Name> key = new Pair<>(enclFlatname, c.name);
@@ -422,7 +422,7 @@
}
}
- void clearLocalClassNameIndexes(ClassSymbol c) {
+ public void clearLocalClassNameIndexes(ClassSymbol c) {
if (c.owner != null && c.owner.kind != NIL) {
localClassNameIndexes.remove(new Pair<>(
c.owner.enclClass().flatname, c.name));
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Mon Jul 08 16:20:40 2019 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Tue Jul 09 09:20:04 2019 +0200
@@ -41,7 +41,6 @@
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.GraphUtils.DependencyKind;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
-import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.comp.Attr.ResultInfo;
import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
@@ -501,7 +500,7 @@
attr.attribTree(newTree, speculativeEnv, resultInfo);
return newTree;
} finally {
- new UnenterScanner(env.toplevel.modle).scan(newTree);
+ enter.unenter(env.toplevel, newTree);
log.popDiagnosticHandler(deferredDiagnosticHandler);
if (localCache != null) {
localCache.leave();
@@ -509,29 +508,6 @@
}
}
//where
-
- class UnenterScanner extends TreeScanner {
- private final ModuleSymbol msym;
-
- public UnenterScanner(ModuleSymbol msym) {
- this.msym = msym;
- }
-
- @Override
- public void visitClassDef(JCClassDecl tree) {
- ClassSymbol csym = tree.sym;
- //if something went wrong during method applicability check
- //it is possible that nested expressions inside argument expression
- //are left unchecked - in such cases there's nothing to clean up.
- if (csym == null) return;
- typeEnvs.remove(csym);
- chk.removeCompiled(csym);
- chk.clearLocalClassNameIndexes(csym);
- syms.removeClass(msym, csym.flatname);
- super.visitClassDef(tree);
- }
- }
-
static class DeferredAttrDiagHandler extends Log.DeferredDiagnosticHandler {
static class PosScanner extends TreeScanner {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java Mon Jul 08 16:20:40 2019 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java Tue Jul 09 09:20:04 2019 +0200
@@ -610,4 +610,29 @@
public void newRound() {
typeEnvs.clear();
}
+
+ public void unenter(JCCompilationUnit topLevel, JCTree tree) {
+ new UnenterScanner(topLevel.modle).scan(tree);
+ }
+ class UnenterScanner extends TreeScanner {
+ private final ModuleSymbol msym;
+
+ public UnenterScanner(ModuleSymbol msym) {
+ this.msym = msym;
+ }
+
+ @Override
+ public void visitClassDef(JCClassDecl tree) {
+ ClassSymbol csym = tree.sym;
+ //if something went wrong during method applicability check
+ //it is possible that nested expressions inside argument expression
+ //are left unchecked - in such cases there's nothing to clean up.
+ if (csym == null) return;
+ typeEnvs.remove(csym);
+ chk.removeCompiled(csym);
+ chk.clearLocalClassNameIndexes(csym);
+ syms.removeClass(msym, csym.flatname);
+ super.visitClassDef(tree);
+ }
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/api/TestGetScopeBinaryNames.java Tue Jul 09 09:20:04 2019 +0200
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8223443
+ * @summary Verify binary names are not changed and are correct
+ * when using Trees.getScope
+ * @modules jdk.compiler
+ */
+
+import com.sun.source.tree.ClassTree;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.tools.JavaCompiler;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Scope;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+
+import static javax.tools.JavaFileObject.Kind.SOURCE;
+
+public class TestGetScopeBinaryNames {
+ public static void main(String... args) throws IOException {
+ new TestGetScopeBinaryNames().run();
+ }
+
+ public void run() throws IOException {
+ class EnclosingDesc {
+ final String code;
+ final boolean supportsLocal;
+ public EnclosingDesc(String code, boolean supportsLocal) {
+ this.code = code;
+ this.supportsLocal = supportsLocal;
+ }
+ }
+ List<EnclosingDesc> enclosingEnvs = List.of(
+ new EnclosingDesc("class Test {" +
+ " void test() {" +
+ " $" +
+ " }" +
+ "}",
+ true),
+ new EnclosingDesc("class Test {" +
+ " {" +
+ " $" +
+ " }" +
+ "}",
+ true),
+ new EnclosingDesc("class Test {" +
+ " static {" +
+ " $" +
+ " }" +
+ "}",
+ true),
+ new EnclosingDesc("class Test {" +
+ " Object I = $" +
+ "}",
+ true)
+ );
+ class LocalDesc {
+ final String localCode;
+ final boolean isLocalClass;
+ public LocalDesc(String localCode, boolean isLocalClass) {
+ this.localCode = localCode;
+ this.isLocalClass = isLocalClass;
+ }
+ }
+ List<LocalDesc> locals = List.of(
+ new LocalDesc("new A() {" +
+ " class AI extends B {" +
+ " class AII extends C {" +
+ " private void t() {" +
+ " new D() { class DI extends E {} };" +
+ " }" +
+ " }" +
+ " private void t() { new F() {}; }" +
+ " }" +
+ " private void t() { new G() {}; }" +
+ "};",
+ false),
+ new LocalDesc("class AA extends A {" +
+ " class AI extends B {" +
+ " class AII extends C {" +
+ " private void t() {" +
+ " new D() { class DI extends E {} };" +
+ " }" +
+ " }" +
+ " private void t() { new F() {}; }" +
+ " }" +
+ " private void t() { new G() {}; }" +
+ "}",
+ false)
+ );
+ String markerClasses = "class A {} class B {} class C {}" +
+ "class D {} class E {} class F {}" +
+ "class G {}";
+ for (EnclosingDesc enclosing : enclosingEnvs) {
+ for (LocalDesc local : locals) {
+ if (!local.isLocalClass || enclosing.supportsLocal) {
+ doTest(enclosing.code.replace("$", local.localCode) +
+ markerClasses);
+ }
+ }
+ }
+ }
+
+ void doTest(String code, String... expected) throws IOException {
+ Map<String, String> name2BinaryName = new HashMap<>();
+ Map<String, String> name2QualifiedName = new HashMap<>();
+
+ computeNames(code, name2BinaryName, name2QualifiedName);
+
+ JavaCompiler c = ToolProvider.getSystemJavaCompiler();
+ JavacTask t = (JavacTask) c.getTask(null, null, null, null, null,
+ List.of(new MyFileObject(code)));
+ CompilationUnitTree cut = t.parse().iterator().next();
+ Trees trees = Trees.instance(t);
+
+ t.addTaskListener(new TaskListener() {
+ @Override
+ public void finished(TaskEvent e) {
+ if (e.getKind() == TaskEvent.Kind.ENTER) {
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void scan(Tree tree, Void p) {
+ if (tree != null &&
+ !isInExtendsClause(getCurrentPath(), tree)) {
+ TreePath path =
+ new TreePath(getCurrentPath(), tree);
+ Scope scope = trees.getScope(path);
+ checkScope(t.getElements(), scope,
+ name2BinaryName, name2QualifiedName);
+ }
+ return super.scan(tree, p);
+ }
+ }.scan(cut, null);
+ }
+ }
+ });
+
+ t.analyze();
+
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void visitClass(ClassTree node, Void p) {
+ TypeElement type =
+ (TypeElement) trees.getElement(getCurrentPath());
+ checkClass(t.getElements(), type,
+ name2BinaryName, name2QualifiedName);
+ return super.visitClass(node, p);
+ }
+ }.scan(cut, null);
+
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void scan(Tree tree, Void p) {
+ if (tree != null &&
+ !isInExtendsClause(getCurrentPath(), tree)) {
+ TreePath path =
+ new TreePath(getCurrentPath(), tree);
+ Scope scope = trees.getScope(path);
+ checkScope(t.getElements(), scope,
+ name2BinaryName, name2QualifiedName);
+ }
+ return super.scan(tree, p);
+ }
+ }.scan(cut, null);
+ }
+
+ void computeNames(String code,
+ Map<String, String> name2BinaryName,
+ Map<String, String> name2QualifiedName) throws IOException {
+ JavaCompiler c = ToolProvider.getSystemJavaCompiler();
+ JavacTask t = (JavacTask) c.getTask(null, null, null, null, null,
+ List.of(new MyFileObject(code)));
+ CompilationUnitTree cut = t.parse().iterator().next();
+
+ t.analyze();
+
+ new TreePathScanner<Void, Void>() {
+ Trees trees = Trees.instance(t);
+ Elements els = t.getElements();
+ @Override
+ public Void visitClass(ClassTree node, Void p) {
+ TypeElement type =
+ (TypeElement) trees.getElement(getCurrentPath());
+ String key = type.getSuperclass().toString();
+
+ name2BinaryName.put(key, els.getBinaryName(type).toString());
+ name2QualifiedName.put(key, type.getQualifiedName().toString());
+ return super.visitClass(node, p);
+ }
+ }.scan(cut, null);
+ }
+
+ boolean isInExtendsClause(TreePath clazz, Tree toCheck) {
+ return clazz != null &&
+ clazz.getLeaf().getKind() == Kind.CLASS &&
+ ((ClassTree) clazz.getLeaf()).getExtendsClause() == toCheck;
+ }
+
+ void checkClass(Elements els, TypeElement type,
+ Map<String, String> name2BinaryName,
+ Map<String, String> name2QualifiedName) {
+ if (type.getNestingKind() == NestingKind.TOP_LEVEL ||
+ type.getNestingKind() == NestingKind.MEMBER) {
+ return ;
+ }
+
+ String binaryName = name2BinaryName.get(type.getSuperclass().toString());
+
+ if (!els.getBinaryName(type).contentEquals(binaryName)) {
+ throw new AssertionError("Unexpected: " + els.getBinaryName(type));
+ }
+
+ String qualifiedName = name2QualifiedName.get(type.getSuperclass().toString());
+
+ if (qualifiedName != null) {
+ if (!type.getQualifiedName().contentEquals(qualifiedName)) {
+ throw new AssertionError("Unexpected: " + type.getQualifiedName() +
+ ", expected: " + qualifiedName);
+ }
+ }
+ }
+
+ void checkScope(Elements els, Scope scope,
+ Map<String, String> name2BinaryName,
+ Map<String, String> name2QualifiedName) {
+ while (scope != null) {
+ for (Element el : scope.getLocalElements()) {
+ if (el.getKind().isClass()) {
+ checkClass(els, (TypeElement) el,
+ name2BinaryName, name2QualifiedName);
+ }
+ }
+ scope = scope.getEnclosingScope();
+ }
+ }
+
+ class MyFileObject extends SimpleJavaFileObject {
+ private final String code;
+
+ MyFileObject(String code) {
+ super(URI.create("myfo:///Test.java"), SOURCE);
+ this.code = code;
+ }
+ @Override
+ public String getCharContent(boolean ignoreEncodingErrors) {
+ return code;
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/api/TestGetScopeErrors.java Tue Jul 09 09:20:04 2019 +0200
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8223443
+ * @summary Verify errors are not reported when computing Scopes.
+ * @modules jdk.compiler
+ */
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+import static javax.tools.JavaFileObject.Kind.SOURCE;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+
+public class TestGetScopeErrors {
+ public static void main(String... args) throws IOException {
+ new TestGetScopeErrors().run();
+ }
+
+ void run() throws IOException {
+ JavaCompiler c = ToolProvider.getSystemJavaCompiler();
+ String code =
+ "public class Test {" +
+ " private Object obj = new Object() {" +
+ " private Unresolvable u;" +
+ " };" +
+ " void test() {" +
+ " new Object() {" +
+ " private Unresolvable u;" +
+ " };" +
+ " }" +
+ "}";
+ class MyFileObject extends SimpleJavaFileObject {
+ MyFileObject() {
+ super(URI.create("myfo:///Test.java"), SOURCE);
+ }
+ @Override
+ public String getCharContent(boolean ignoreEncodingErrors) {
+ return code;
+ }
+ }
+ AtomicBoolean enterDone = new AtomicBoolean();
+ List<String> errors = new ArrayList<>();
+ DiagnosticListener<JavaFileObject> noErrors = d -> {
+ if (!enterDone.get() && d.getKind() == Diagnostic.Kind.ERROR) {
+ throw new AssertionError(d.toString());
+ }
+ errors.add(d.getSource().getName() + ":" +
+ d.getPosition() + ":" +
+ d.getCode());
+ };
+ JavacTask t =
+ (JavacTask) c.getTask(null, null, noErrors,
+ Arrays.asList("-XDrawDiagnostics"),
+ null, List.of(new MyFileObject()));
+ CompilationUnitTree cut = t.parse().iterator().next();
+ Trees trees = Trees.instance(t);
+ t.addTaskListener(new TaskListener() {
+ @Override
+ public void finished(TaskEvent e) {
+ if (e.getKind() == TaskEvent.Kind.ENTER) {
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void scan(Tree tree, Void p) {
+ if (tree != null) {
+ TreePath path =
+ new TreePath(getCurrentPath(), tree);
+ trees.getScope(path);
+ }
+ return super.scan(tree, p);
+ }
+ }.scan(cut, null);
+ enterDone.set(true);
+ }
+ }
+ });
+
+ t.analyze();
+
+ List<String> expectedErrors = List.of(
+ "/Test.java:74:compiler.err.cant.resolve",
+ "/Test.java:154:compiler.err.cant.resolve"
+ );
+
+ if (!expectedErrors.equals(errors)) {
+ throw new IllegalStateException("Unexpected errors: " + errors);
+ }
+ }
+
+}