问题
I'm working on a plug-in to Eclipse JDT that parses Java files and offers automatic corrections to them. I'm doing so using Eclipse's API for analyzing the AST.
I'm trying to write a method that calculates the environment of a method - a list of all the identifiers that are visible within the scope of the method. Another way to look at this is the list of identifiers that can be auto-completed from a specific point in Eclipse.
For example:
import ...
public class MyClass {
private static final int a = 3;
private boolean b;
float someMethod(String s) {
int c = 3;
(X);
}
}
The environment in (X)
is composed of the identifiers a
, b
, c
and s
.
How can I calculate the environment of a method in Eclipse?
回答1:
You have to obtain the ICompilationUnit
from the class you want to parse.
Then you can make visitors
for the node types you need, as MethodDeclaration
.
public class MainMethodVisitor extends ASTVisitor {
List<MethodDeclaration> methods = new ArrayList<MethodDeclaration>();
@Override
public boolean visit(MethodDeclaration node) {
... //do stuff with the node
And call it with:
ICompilationUnit unit;
//...
CompilationUnit parse = parse(unit);
MainMethodVisitor visitor = new MainMethodVisitor();
parse.accept(visitor);
Having the method node, you can access to its paramenters with node.parameters()
to obtain s
. You'll have to find out what type of nodes are class's members variables (a
, b
).
Last thing to do is access to, i think, VariableDeclaration
inside the method node in order to obtain c
.
Thats all i have to help you. Hope it helps.
回答2:
Here is some working code that solves the given simple example, but needs to be extended for more complex code:
- Scopes, e. g.
int a = 1; { int b = 2; } (X)
(environment:a
only) - Inheritance
- Static imports with or without wildcards, e. g.
import static java.lang.Integer.MAX_VALUE;
orimport static java.lang.Integer.*;
- ...
The basic principle is to travel the AST and access cross connections via node.resolveBinding()
.
public class Environment {
public static void main(String[] args) {
String code = "public class MyClass {\n" +
" private static final int a = 3;\n" +
" private boolean b;\n" +
"\n" +
" float someMethod(String s) {\n" +
" int c = 3;\n" +
" // (X);\n" +
" }\n" +
"}";
for (IBinding binding : of(code, code.indexOf("(X)"))) {
System.out.println(binding.getName());
}
}
public static List<IBinding> of(String code, int offset) {
final List<IBinding> environment = new ArrayList<>();
createAst(code).accept(new ASTVisitor(true) {
public boolean visit(VariableDeclarationFragment node) {
if (offset < node.getStartPosition()) return false;
environment.add(node.resolveBinding());
return true;
}
public boolean visit(SingleVariableDeclaration node) {
if (offset < node.getStartPosition()) return false;
environment.add(node.resolveBinding());
return true;
}
});
return environment;
}
private static CompilationUnit createAst(String code) {
// parser
ASTParser parser = ASTParser.newParser(AST.JLS10);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
parser.setStatementsRecovery(true);
// options
final Hashtable<String, String> options = JavaCore.getOptions();
options.put("org.eclipse.jdt.core.compiler.source", "1.8");
parser.setCompilerOptions(options);
// sources and classpath
String[] sources = new String[] { /* source folders */ };
String[] classpath = new String[] { /* JARs */};
String[] encodings = new String[sources.length];
Arrays.fill(encodings, StandardCharsets.UTF_8.name());
parser.setEnvironment(classpath, sources, encodings, true);
parser.setUnitName("code");
parser.setSource(code.toCharArray());
// abstract syntax tree
return (CompilationUnit) parser.createAST(null);
}
}
Alternatively, the fields (including constants) can be collected at visit(TypeDeclaration node)
via node.getFields()
or the parameters of a method at visit(MethodDeclaration node)
via node.parameters()
earlier in the AST.
来源:https://stackoverflow.com/questions/32305545/using-eclipse-jdt-to-find-all-identifiers-visible-within-a-specific-node