问题
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:aonly) - 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