Устранение перегруженных методов с помощью ASTVisitor в Eclipse JDT - PullRequest
2 голосов
/ 16 августа 2011

В настоящее время я работаю над академическим проектом, в котором для создания базового 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, где создается временный проект.

1 Ответ

1 голос
/ 17 сентября 2011

Поскольку ошибочное поведение происходит только в тестовых случаях, создание AST, похоже, не настроено должным образом.

Я нашел настройку тестирования, которая работает: для каждого теста я читаю CompilationUnit как текст из файловой системы и создаю новый проект программно, следуя подходу, основанному на описании здесь ,Как вы видите в конфигурации ASTParser в исходном вопросе, IJavaProject -экземпляр передается с использованием parser.setProject(IJavaProject), удерживая среду для правильного разрешения вызовов.

Этот экземпляр должен быть настроен с использованием classpath:

/**
 * @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 по умолчанию.В вашем случае может потребоваться увеличить путь к классам.В любом случае это решает проблему.

...