Using Eclipse JDT to find all identifiers visible within a specific node

心不动则不痛 提交于 2021-02-07 21:55:44

问题


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; or import 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!