我正在尝试将IntelliJ SDK用作独立的java解析器,并且在大多数情况下它可以正常工作,但无法解析泛型方法的返回类型。
当我在IntelliJ中的下一个示例中调试resolveMethod
verify(mock).simpleMethod()
时:
public class ResolutionTest {
private interface IMethods {
String simpleMethod();
}
private IMethods mock;
public static <T> T verify(T m) {
return m;
}
public void test() {
verify(mock).simpleMethod();
}
}
我看到verify(mock)
的返回类型为IMethods
,simpleMethod
也正确解析。但是在我的解析器中,返回类型verify(mock)
的{{1}}和T
解析失败了。我想我没有注册一些服务或扩展,但我无法弄清楚哪一个。
我的解析器:
simpleMethod
输出:
import com.intellij.codeInsight.ContainerProvider;
import com.intellij.codeInsight.runner.JavaMainMethodProvider;
import com.intellij.core.CoreApplicationEnvironment;
import com.intellij.core.CoreJavaFileManager;
import com.intellij.core.JavaCoreApplicationEnvironment;
import com.intellij.core.JavaCoreProjectEnvironment;
import com.intellij.mock.MockProject;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.extensions.ExtensionsArea;
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.augment.TypeAnnotationModifier;
import com.intellij.psi.compiled.ClassFileDecompilers;
import com.intellij.psi.impl.JavaClassSupersImpl;
import com.intellij.psi.impl.PsiElementFinderImpl;
import com.intellij.psi.impl.PsiNameHelperImpl;
import com.intellij.psi.impl.PsiTreeChangePreprocessor;
import com.intellij.psi.impl.file.impl.JavaFileManager;
import com.intellij.psi.meta.MetaDataContributor;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.BinaryFileStubBuilders;
import com.intellij.psi.util.JavaClassSupers;
import java.io.File;
public class Main {
static class Analyzer extends PsiElementVisitor {
static final Disposable disposable = () -> {
};
private static class ProjectEnvironment extends JavaCoreProjectEnvironment {
public ProjectEnvironment(Disposable parentDisposable, CoreApplicationEnvironment applicationEnvironment) {
super(parentDisposable, applicationEnvironment);
}
@Override
protected void registerJavaPsiFacade() {
JavaFileManager javaFileManager = getProject().getComponent(JavaFileManager.class);
CoreJavaFileManager coreJavaFileManager = (CoreJavaFileManager) javaFileManager;
ServiceManager.getService(getProject(), CoreJavaFileManager.class);
getProject().registerService(CoreJavaFileManager.class, coreJavaFileManager);
getProject().registerService(PsiNameHelper.class, PsiNameHelperImpl.getInstance());
PsiElementFinder finder = new PsiElementFinderImpl(getProject(), coreJavaFileManager);
ExtensionsArea area = Extensions.getArea(getProject());
area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(finder);
super.registerJavaPsiFacade();
}
@Override
protected void preregisterServices() {
super.preregisterServices();
ExtensionsArea area = Extensions.getArea(getProject());
CoreApplicationEnvironment.registerExtensionPoint(area, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor.class);
CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP_NAME, PsiElementFinder.class);
}
}
private static class ApplicationEnvironment extends JavaCoreApplicationEnvironment {
public ApplicationEnvironment(Disposable parentDisposable) {
super(parentDisposable);
myApplication.registerService(JavaClassSupers.class, new JavaClassSupersImpl());
}
}
final ApplicationEnvironment applicationEnvironment;
final ProjectEnvironment projectEnvironment;
public Analyzer() {
ExtensionsArea rootArea = Extensions.getRootArea();
CoreApplicationEnvironment.registerExtensionPoint(rootArea, BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint.class);
CoreApplicationEnvironment.registerExtensionPoint(rootArea, FileContextProvider.EP_NAME, FileContextProvider.class);
CoreApplicationEnvironment.registerExtensionPoint(rootArea, MetaDataContributor.EP_NAME, MetaDataContributor.class);
CoreApplicationEnvironment.registerExtensionPoint(rootArea, PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class);
CoreApplicationEnvironment.registerExtensionPoint(rootArea, JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class);
CoreApplicationEnvironment.registerExtensionPoint(rootArea, ContainerProvider.EP_NAME, ContainerProvider.class);
CoreApplicationEnvironment.registerExtensionPoint(rootArea, ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler.class);
CoreApplicationEnvironment.registerExtensionPoint(rootArea, TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier.class);
applicationEnvironment = new ApplicationEnvironment(disposable);
projectEnvironment = new ProjectEnvironment(disposable, applicationEnvironment);
}
public void add(final String[] args) throws Exception {
for (String arg : args) {
final VirtualFile root = applicationEnvironment.getLocalFileSystem().findFileByIoFile(new File(arg));
projectEnvironment.addSourcesToClasspath(root);
}
}
public void run() {
MockProject project = projectEnvironment.getProject();
PsiClass cls = project.getComponent(JavaFileManager.class)
.findClass("ResolutionTest", GlobalSearchScope.projectScope(project));
if (cls != null) {
PsiMethod[] methods = cls.findMethodsByName("test", false);
if (methods.length == 1) {
PsiMethod method = methods[0];
for (PsiStatement s : method.getBody().getStatements()) {
System.out.println(s.getNode().getText());
process(s);
}
}
}
}
private void process(PsiMethodCallExpression expression) {
PsiExpression qualifierExpression = expression.getMethodExpression().getQualifierExpression();
if (qualifierExpression instanceof PsiMethodCallExpression) {
process((PsiMethodCallExpression) qualifierExpression);
} else if (qualifierExpression instanceof PsiReference) {
System.out.println("Resolving reference " + qualifierExpression.getText());
PsiElement targetElement = ((PsiReference) qualifierExpression).resolve();
if (targetElement == null) {
System.out.println("Resolution failed");
} else if (targetElement instanceof PsiClass) {
System.out.println("Class " + ((PsiClass) targetElement).getName());
} else if (targetElement instanceof PsiVariable) {
System.out.println("Variable " + ((PsiVariable) targetElement).getTypeElement().getText());
}
}
System.out.println("Resolving method " + expression.getMethodExpression().getText());
PsiMethod method = expression.resolveMethod();
if (method == null) {
System.out.println("Resolution failed");
} else {
PsiClass clazz = method.getContainingClass();
System.out.println(clazz.getName() + "." + method.getName());
}
}
private void process(PsiExpression e) {
if (e instanceof PsiMethodCallExpression) {
process((PsiMethodCallExpression) e);
}
}
private void process(PsiStatement s) {
if (s instanceof PsiExpressionStatement) {
process(((PsiExpressionStatement) s).getExpression());
}
}
}
public static void main(String[] args) {
try {
Analyzer analyzer = new Analyzer();
analyzer.add(args);
analyzer.run();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}
答案 0 :(得分:6)
为了让这个示例正常工作,我必须通过rt.jar
添加projectEnvironment.addJarToClassPath(file);
- 遗憾的是我仍然在mockito中遇到2个方法解析失败而且我无法创建重现问题的小样本。仍然有关rt.jar
的信息对某人有用,所以我将其添加为答案。
问题的功能:
@Test
public void any_should_be_actual_alias_to_anyObject() {
mock.simpleMethod((Object) null);
verify(mock).simpleMethod(any());
verify(mock).simpleMethod(anyObject());
}
我目前对一个问题的理解:any()返回是通用的,而simpleMethod有多个重载,解析器无法选择合适的一个,但是想法本身能够选择合适的变体。
P.S。将java语言级别设置为6后(如在mockito源中) - 不再有失败。