独立 IntelliJ 解析器中的符号解析
Symbols resolution in standalone IntelliJ parser
我正在尝试将 IntelliJ SDK 用作独立的 java 解析器,它在大多数情况下工作正常,但无法解析 return 类型的通用方法。
当我在 IntelliJ 的下一个示例中为 verify(mock).simpleMethod()
调试 resolveMethod
时:
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();
}
}
我看到 return 类型的 verify(mock)
作为 IMethods
和 simpleMethod
也正确解析。但是在我的解析器中 return 类型的 verify(mock)
是 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);
}
}
}
并输出:
verify(mock).simpleMethod();
Resolving method verify
ResolutionTest.verify
Resolving method verify(mock).simpleMethod
Resolution failed
为了使这个示例工作,我必须通过 projectEnvironment.addJarToClassPath(file);
添加 rt.jar
- 不幸的是,我仍然在 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() return 是通用的,simpleMethod 有多个重载,解析器无法选择合适的一个,但想法本身能够 select 合适的变体。
P.S。将 java 语言级别设置为 6 后(如在 mockito 源中)——不再有失败。
我正在尝试将 IntelliJ SDK 用作独立的 java 解析器,它在大多数情况下工作正常,但无法解析 return 类型的通用方法。
当我在 IntelliJ 的下一个示例中为 verify(mock).simpleMethod()
调试 resolveMethod
时:
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();
}
}
我看到 return 类型的 verify(mock)
作为 IMethods
和 simpleMethod
也正确解析。但是在我的解析器中 return 类型的 verify(mock)
是 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);
}
}
}
并输出:
verify(mock).simpleMethod();
Resolving method verify
ResolutionTest.verify
Resolving method verify(mock).simpleMethod
Resolution failed
为了使这个示例工作,我必须通过 projectEnvironment.addJarToClassPath(file);
添加 rt.jar
- 不幸的是,我仍然在 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() return 是通用的,simpleMethod 有多个重载,解析器无法选择合适的一个,但想法本身能够 select 合适的变体。
P.S。将 java 语言级别设置为 6 后(如在 mockito 源中)——不再有失败。