8223443: Calling Trees.getScope early changes names of local/anonymous classes
authorjlahoda
Tue, 09 Jul 2019 09:20:04 +0200
changeset 55622 0b470386f5f7
parent 55621 ea3b1a8fd4bb
child 55623 c589ba4b823c
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
src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java
test/langtools/tools/javac/api/TestGetScopeBinaryNames.java
test/langtools/tools/javac/api/TestGetScopeErrors.java
--- 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);
+        }
+    }
+
+}