Symbols resolution in standalone IntelliJ parser

此生再无相见时 提交于 2019-12-23 09:42:31

问题


I'm trying to use IntelliJ SDK as standalone java parser and it works fine in most cases, but failing to resolve return type of generic methods.

When I debugging resolveMethod for verify(mock).simpleMethod() in next sample inside of 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 return type of verify(mock) as IMethods and simpleMethod also resolved correctly. But in my parser return type of verify(mock) is T and simpleMethod resolution failing because of that. I guess I'm not register some service or extension but I cannot figure out 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 output:

verify(mock).simpleMethod();
Resolving method verify
ResolutionTest.verify
Resolving method verify(mock).simpleMethod
Resolution failed

回答1:


In order to make this sample to work I had to add rt.jar via projectEnvironment.addJarToClassPath(file); - unfortunately I'm still getting 2 method resolution failures in mockito and I'm unable to create small sample which reproduces an issue. Still information about rt.jar could be useful to someone so I'm adding it as an answer.

Function with issues:

@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 an issue: any() return is generic and simpleMethod has multiple overloads and resolver could not pick proper one, but idea itself is able to select proper variant.

P.S. After setting java language level to 6 (as in mockito sources) – there are no more failures.



来源:https://stackoverflow.com/questions/40525028/symbols-resolution-in-standalone-intellij-parser

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!