独立IntelliJ解析器中的符号解析

时间:2016-11-10 10:16:21

标签: java intellij-idea intellij-plugin

我正在尝试将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)的返回类型为IMethodssimpleMethod也正确解析。但是在我的解析器中,返回类型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);
        }
    }
}

1 个答案:

答案 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源中) - 不再有失败。