Character Resolution in IntelliJ Standalone Parser

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 
+6
source share
1 answer

For this sample to work, I had to add rt.jar via projectEnvironment.addJarToClassPath(file); - unfortunately, I still get 2 method permission failures in mockito , and I cannot create a small sample that reproduces the problem. The information about rt.jar may be useful to someone, so I am adding it as an answer.

Function with problems:

 @Test public void any_should_be_actual_alias_to_anyObject() { mock.simpleMethod((Object) null); verify(mock).simpleMethod(any()); verify(mock).simpleMethod(anyObject()); } 

My current understanding of the problem: any () return is common and simpleMethod has several overloads and the resolver cannot choose the right one, but the idea itself can choose the right one.

PS After the java level of the language reaches 6 (as in the mockito sources), there are no more crashes.

+6
source

Source: https://habr.com/ru/post/1012213/


All Articles