2011-08-15 24 views
2

我目前正在研究一个使用ASTVisitor创建基本调用树的学术项目。使用ASTVisitor在Eclipse中解决重载方法JDT

为此,需要将方法的调用与其声明相关联。

编辑:问题在很大程度上得到了解决:所描述的错误仅出现在JUnit-Tests中,但不在独立插件中。它似乎与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

你还有问题吗? –

+0

问题解决了,我发布了我的解决方案作为答案。 – mtsz

+0

你可以接受你自己的答案。 :) –

回答

1

由于错误的行为只发生在测试用例中,AST的创建似乎没有正确设置。

我发现了一个测试设置,其中的工作原理: 对于每一个测试,我读CompilationUnit从文件系统中的文本和编程创建一个新的项目,从下面的描述here导出的方法。 正如您在原始问题的ASTParser配置中看到的那样,使用parser.setProject(IJavaProject)传递一个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库。在你的情况下,可能需要增加类路径。无论如何,这解决了这个问题。

相关问题