Eclipse JDT ASTParser - resolving in lambdas

330 views Asked by At

I'm using the Eclipse JDT AST to parse Java source code, with the aim of identifying all uses of a given method. I am using eclipse.jdt.core 3.21.0.

While the tool resolves method bindings correctly most of the time, I'm running into a problem where it won't resolve them if the method is used inside a lambda, if the lambda is of a type unknown to the parser.

For example, in the following code the first invocation of Guava's Objects.equal will resolve, but the second will not:

LocalDate date = new LocalDate();    
Objects.equal(date, null);
Optional.of(date).filter(d -> Objects.equal(d, null));

In this example, I know the method invocation I am looking for, and I can provide the classfiles for that method (Guava) to the ASTParser. But because the tool needs to analyse a very large code base, and not all of the dependencies are known, I can't pass classfiles for all types in the codebase, so types like the org.joda.time.LocalDate are unknown. This isn't a problem for invocations like the first, but ones like the second won't resolve, and I'm struggling to figure out why.

I've wrapped up my this example and my ASTParser creation here:

import java.util.Hashtable;

import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;

public class ResolveTest {

  String source = "import java.util.Optional;\r\n" + 
      "\r\n" + 
      "import org.joda.time.LocalDate;\r\n" + 
      "\r\n" + 
      "import com.google.common.base.Objects;\r\n" + 
      "\r\n" + 
      "public class Source {\r\n" + 
      "\r\n" + 
      "  public void test() {\r\n" + 
      "    LocalDate date = new LocalDate();\r\n" + 
      "    \r\n" + 
      "    Objects.equal(date, null);\r\n" + 
      "    Optional.of(date).filter(d -> Objects.equal(d, null));\r\n" + 
      "  }\r\n" + 
      "}";

  public static void main(String[] args) {
    ResolveTest resolveTest = new ResolveTest();
    resolveTest.test();
  }

  private void test() {
    String userHome = System.getProperty("user.home");
    String guavaClassPath = userHome + "\\.m2\\repository\\com\\google\\guava\\guava\\27.0-jre\\guava-27.0-jre.jar";

    ASTParser parser = createParser(source, new String[] { "" }, new String[] { guavaClassPath });

    CompilationUnit compilationUnit = (CompilationUnit) parser.createAST(null);
    compilationUnit.accept(new ASTVisitor() {
      @Override
      public boolean visit(MethodInvocation node) {
        if (node.getName().toString().equals("equal")) {
          IMethodBinding resolvedMethodBinding = node.resolveMethodBinding();
          System.out.println("Found method [" + node.toString() + "], Resolved [" + resolvedMethodBinding + "]");
        }
        return super.visit(node);
      }
    });
  }

  public static ASTParser createParser(String fileSource, String[] sources, String[] classPath) {
    ASTParser parser = ASTParser.newParser(AST.JLS8);
    parser.setResolveBindings(true);
    parser.setBindingsRecovery(true);
    parser.setStatementsRecovery(true);
    parser.setKind(ASTParser.K_COMPILATION_UNIT);
    parser.setSource(fileSource.toCharArray());
    parser.setUnitName("Source.java");
    Hashtable<String, String> javaCoreOptions = JavaCore.getOptions();
    JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, javaCoreOptions);
    parser.setCompilerOptions(javaCoreOptions);

    String[] encodings = new String[] { "UTF-8" };
    parser.setEnvironment(classPath, sources, encodings, true);
    return parser;
  }
}

The output is:

Found method [Objects.equal(date,null)], Resolved [public static boolean equal(@Nullable Object, @Nullable Object) ]
Found method [Objects.equal(d,null)], Resolved [null]
0

There are 0 answers