8223443: Calling Trees.getScope early changes names of local/anonymous classes
Ensure Trees.getScope does not affect the rest of the compilation. Reviewed-by: mcimadamore
This commit is contained in:
parent
2b7170b33c
commit
d24296edf4
src/jdk.compiler/share/classes/com/sun/tools/javac
test/langtools/tools/javac/api
@ -28,13 +28,12 @@ package com.sun.tools.javac.api;
|
||||
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 javax.tools.StandardLocation;
|
||||
|
||||
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.DocTreePath;
|
||||
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;
|
||||
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.JCTree.JCVariableDecl;
|
||||
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 @@ public class JavacTrees extends DocTrees {
|
||||
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 @@ public class JavacTrees extends DocTrees {
|
||||
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 @@ public class JavacTrees extends DocTrees {
|
||||
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 @@ public class JavacTrees extends DocTrees {
|
||||
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 @@ public class JavacTrees extends DocTrees {
|
||||
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) {
|
||||
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 {
|
||||
return attr.attribExprToTree(expr, env, tree);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 @@ public class JavacTrees extends DocTrees {
|
||||
**/
|
||||
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 @@ public class JavacTrees extends DocTrees {
|
||||
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) {
|
||||
|
@ -407,7 +407,7 @@ public class Check {
|
||||
* 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 @@ public class Check {
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
@ -41,7 +41,6 @@ import com.sun.tools.javac.util.*;
|
||||
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 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
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 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
}
|
||||
}
|
||||
//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 {
|
||||
|
@ -610,4 +610,29 @@ public class Enter extends JCTree.Visitor {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
291
test/langtools/tools/javac/api/TestGetScopeBinaryNames.java
Normal file
291
test/langtools/tools/javac/api/TestGetScopeBinaryNames.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
131
test/langtools/tools/javac/api/TestGetScopeErrors.java
Normal file
131
test/langtools/tools/javac/api/TestGetScopeErrors.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user