我目前正在开展一个使用ASTVisitor创建基本calltree的学术项目。
为此,需要将方法的调用与其声明相关联。
编辑:问题在很大程度上得到了解决:所描述的错误仅出现在JUnit-Tests中,但在standalone-plugin中却没有出现。它似乎与ASTVisitors的单元测试工作流程有关。我将在一段时间内进一步调查原因并在此处发布答案。
下面的代码显示了一个最小的访问者,它将调用和相关的声明打印到控制台:
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;
/**
* Visits all method invocations and prints the declaration of the caller to
* console.
*/
public class InvocationLoggerASTVisitor extends ASTVisitor {
@Override
public boolean visit(MethodInvocation methodInvocation) {
if (methodInvocation.resolveMethodBinding() != null) {
IMethodBinding declarationOfInvokedMethod = methodInvocation
.resolveMethodBinding().getMethodDeclaration();
System.out.println(String.format(
"invocation of \"%s\" is resolved to declaration \"%s\"",
methodInvocation, declarationOfInvokedMethod));
}
return super.visit(methodInvocation);
}
}
访问者适用于某些测试场景。 但奇怪的是,当将它应用于包含重载方法的编译单元时,某些调用会与错误的声明相关联。让我们访问以下代码:
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class OverloadedMethodsAndRawTypes implements ICallGraphTestSource {
void f(HashSet objects) {
f(new ArrayDeque(objects));
}
void f(ArrayDeque objects) {
f(new ArrayList(objects));
}
void f(ArrayList objects) {
f(new HashSet(objects));
}
}
访问此编译单元时,控制台输出为:
invocation of "f(new ArrayDeque(objects))" is resolved to declaration "void f(HashSet) "
invocation of "f(new ArrayList(objects))" is resolved to declaration "void f(HashSet )"
invocation of "f(new HashSet(list))" is resolved to declaration "void f(HashSet) "
我希望这个输出:
invocation of "f(new ArrayDeque(objects))" is resolved to declaration "void f(ArrayDeque) "
invocation of "f(new ArrayList(objects))" is resolved to declaration "void f(ArrayList )"
invocation of "f(new HashSet(list))" is resolved to declaration "void f(HashSet) "
我注意到,重载方法的调用总是解析为与调用的方法名称匹配的第一个发生的声明,这对我来说似乎不对。
为了证明,该方法重载是责备,我附加相同的代码没有方法重载:
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class RawTypes implements ICallGraphTestSource {
void f_set(HashSet objects) {
f_deque(new ArrayDeque(objects));
}
void f_deque(ArrayDeque objects) {
f_list(new ArrayList(objects));
}
void f_list(ArrayList objects) {
f_set(new HashSet(objects));
}
}
访问时,会产生正确的输出:
invocation of "f_deque(new ArrayDeque(objects))" is resolved to declaration "void f_deque(ArrayDeque) "
invocation of "f_list(new ArrayList(objects))" is resolved to declaration "void f_list(ArrayList) "
invocation of "f_set(new HashSet(list))" is resolved to declaration "void f_set(HashSet) "
我的ASTParser配置如下:
public static ASTNode getAST(ICompilationUnit compilationUnit) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(compilationUnit);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
parser.setProject(getJavaProject());
return parser.createAST(null);
}
编辑:所描述的设置在JUnit-Tests中不起作用,但它确实作为独立插件工作。必须在getJavaProject-Method中创建临时项目的原因。
答案 0 :(得分:1)
由于错误行为仅发生在测试用例中,因此AST的创建似乎没有正确设置。
我找到了一个有效的测试设置:
对于每个测试,我从文件系统中读取CompilationUnit
作为文本,并按照从描述here派生的方法以编程方式创建新项目。
正如您在原始问题中的ASTParser配置中看到的那样,使用IJavaProject
传递parser.setProject(IJavaProject)
- 实例,并保存一个环境以正确解析调用。
此实例必须配置类路径:
/**
* @param project
* a project with JavaNature
* @return a java project with classpath set to the default JRELibrary
* @throws JavaModelException
*/
private static IJavaProject createJavaProject(IProject project)
throws JavaModelException {
IJavaProject javaProject = JavaCore.create(project);
javaProject.setRawClasspath(PreferenceConstants.getDefaultJRELibrary(),
null);
return javaProject;
}
在我的情况下,我只需要默认的JRE库。在您的情况下,可能需要增加类路径。无论如何,这解决了这个问题。