I am trying to use the IntelliJ SDK as a standalone java parser, and it works fine in most cases, but cannot resolve the return type of common methods.
When I debug resolveMethod
for verify(mock).simpleMethod()
in the following example inside IntelliJ:
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(); } }
I see the verify(mock)
return type as IMethods
and simpleMethod
also correctly resolved. But because of this, in my expression for the parser, the verify(mock)
is T
and simpleMethod
. I assume that I will not register any service or extension, but I cannot understand which one.
My parser:
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); } } }
And the conclusion:
verify(mock).simpleMethod(); Resolving method verify ResolutionTest.verify Resolving method verify(mock).simpleMethod Resolution failed