В настоящее время я работаю над академическим проектом, в котором для создания базового calltree используется ASTVisitor.
Для этого необходимо связать вызов метода с его объявлением.
EDIT : Проблема решена в значительной степени: описанная ошибка появляется только в 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);
}
EDIT : Описанная установка не работает в JUnit-Tests, но работает как отдельный плагин. Причина этого должна быть в методе getJavaProject, где создается временный проект.