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:
Jan Lahoda 2019-07-09 09:20:04 +02:00
parent 2b7170b33c
commit d24296edf4
6 changed files with 587 additions and 41 deletions
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);
}
}
}

@ -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;
}
}
}

@ -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);
}
}
}