【问题标题】:Symbols resolution in standalone IntelliJ parser独立 IntelliJ 解析器中的符号解析
【发布时间】:2016-11-10 10:16:20
【问题描述】:

我正在尝试将 IntelliJ SDK 用作独立的 java 解析器,它在大多数情况下都能正常工作,但无法解析泛型方法的返回类型。

当我在 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();
    }

}

我看到verify(mock) 的返回类型为IMethodssimpleMethod 也正确解析。但是在我的解析器返回类型verify(mock)TsimpleMethod 分辨率因此而失败。我想我没有注册一些服务或扩展,但我不知道是哪一个。

我的解析器:

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

【问题讨论】:

    标签: java intellij-idea intellij-plugin


    【解决方案1】:

    为了使该示例正常工作,我必须通过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() 返回是通用的,simpleMethod 有多个重载,解析器无法选择合适的重载,但想法本身能够选择合适的变体。

    附:在将 java 语言级别设置为 6 后(如在 mockito 源中) - 没有更多的失败。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-12
      • 2020-04-01
      • 1970-01-01
      • 2015-02-12
      • 2015-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多