From 553cf412e7e08904dbdbd3b735496f47b6caa8f8 Mon Sep 17 00:00:00 2001 From: Peter Gromov Date: Fri, 7 Aug 2020 18:59:51 +0200 Subject: [PATCH] java: speedup resolving of statically-imported members by honoring name hint (IDEA-247726) GitOrigin-RevId: 8cbdf1fca1afc4023fc95f1ed5e6da5fcc589000 --- .../intellij/psi/impl/PsiClassImplUtil.java | 39 ++++++++++++++++--- .../psi/impl/source/PsiJavaFileBaseImpl.java | 22 ++--------- .../completion/NormalCompletionTest.groovy | 17 ++++++++ .../psi/resolve/ResolvePerformanceTest.java | 29 ++++++++++++++ 4 files changed, 83 insertions(+), 24 deletions(-) diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java index 65fb3b1baa6f..3d48e56e98d9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java @@ -123,6 +123,37 @@ public final class PsiClassImplUtil { return byMap.isEmpty() ? null : (PsiClass)byMap.get(0); } + public static boolean processAllMembersWithoutSubstitutors(@NotNull PsiClass psiClass, @NotNull PsiScopeProcessor processor, @NotNull ResolveState state) { + ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); + + NameHint nameHint = processor.getHint(NameHint.KEY); + String name = nameHint == null ? null : nameHint.getName(state); + + if ((classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) && + !processMembers(state, processor, getMap(psiClass, MemberType.METHOD).get(name == null ? ALL : name))) { + return false; + } + if ((classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) && + !processMembers(state, processor, getMap(psiClass, MemberType.FIELD).get(name == null ? ALL : name))) { + return false; + } + if ((classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) && + !processMembers(state, processor, getMap(psiClass, MemberType.CLASS).get(name == null ? ALL : name))) { + return false; + } + return true; + } + + private static boolean processMembers(ResolveState state, PsiScopeProcessor processor, PsiMember @Nullable[] members) { + if (members == null) return true; + for (PsiMember member : members) { + if (!processor.execute(member, state)) { + return false; + } + } + return true; + } + @NotNull private static List findByMap(@NotNull PsiClass aClass, String name, boolean checkBases, @NotNull MemberType type) { if (name == null) return Collections.emptyList(); @@ -630,9 +661,7 @@ public final class PsiClassImplUtil { ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) { - for (PsiField field : aClass.getFields()) { - if (!processor.execute(field, state)) return false; - } + if (!processMembers(state, processor, aClass.getFields())) return false; } PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject()); @@ -654,9 +683,7 @@ public final class PsiClassImplUtil { } if (!(last instanceof PsiReferenceList) && !(last instanceof PsiModifierList)) { - for (PsiClass inner : aClass.getInnerClasses()) { - if (!processor.execute(inner, state)) return false; - } + if (!processMembers(state, processor, aClass.getInnerClasses())) return false; } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java index d42fced5eebf..3662882e05f4 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java @@ -15,6 +15,7 @@ import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.JavaPsiImplementationHelper; +import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiFileEx; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; @@ -354,7 +355,8 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava } private boolean processOnDemandPackages(PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement place) { - boolean shouldProcessClasses = shouldProcess(processor, CLASS); + ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); + boolean shouldProcessClasses = classHint == null || classHint.shouldProcess(CLASS); if (shouldProcessClasses) { if (!processCurrentPackage(processor, state, place)) return false; if (!processOnDemandTypeImports(processor, state, place)) return false; @@ -375,11 +377,6 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava return getImportList() != null ? getImportList().getImportStatements() : PsiImportStatement.EMPTY_ARRAY; } - private static boolean shouldProcess(PsiScopeProcessor processor, ElementClassHint.DeclarationKind kind) { - ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); - return classHint == null || classHint.shouldProcess(kind); - } - private boolean processCurrentPackage(PsiScopeProcessor processor, ResolveState state, PsiElement place) { processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null); PsiPackage aPackage = JavaPsiFacade.getInstance(myManager.getProject()).findPackage(getPackageName()); @@ -405,18 +402,7 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava final PsiClass targetElement = importStaticStatement.resolveTargetClass(); if (targetElement != null) { processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement); - if (shouldProcess(processor, METHOD) && !processMembers(state, processor, targetElement.getAllMethods())) return false; - if (shouldProcess(processor, FIELD) && !processMembers(state, processor, targetElement.getAllFields())) return false; - if (shouldProcess(processor, CLASS) && !processMembers(state, processor, targetElement.getAllInnerClasses())) return false; - } - } - return true; - } - - private static boolean processMembers(ResolveState state, PsiScopeProcessor processor, PsiMember[] members) { - for (PsiMember member : members) { - if (!processor.execute(member, state)) { - return false; + if (!PsiClassImplUtil.processAllMembersWithoutSubstitutors(targetElement, processor, state)) return false; } } return true; diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy index ba712ea4eb41..e14ee12ebec4 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy @@ -2197,4 +2197,21 @@ class Abc { assert !lookup }.assertTiming() } + + void "test performance with many matching statically-imported declarations"() { + def fieldCount = 7000 + + myFixture.addClass("interface Constants {" + + (0..x } }") + PlatformTestUtil.startPerformanceTest(name, 10_000, { + assert myFixture.completeBasic().length > 100 + }).setup { + lookup?.hideLookup(true) + myFixture.type("\bd") + psiManager.dropPsiCaches() + assert !lookup + }.assertTiming() + } } diff --git a/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolvePerformanceTest.java b/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolvePerformanceTest.java index 9e6c514d517f..d12940fed52a 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolvePerformanceTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolvePerformanceTest.java @@ -15,6 +15,7 @@ import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.testFramework.JavaResolveTestCase; import com.intellij.testFramework.PlatformTestUtil; import com.intellij.util.containers.ContainerUtil; +import one.util.streamex.IntStreamEx; import java.io.File; import java.util.ArrayList; @@ -108,6 +109,34 @@ public class ResolvePerformanceTest extends JavaResolveTestCase { .attempts(1).assertTiming(); } + public void testResolveOfManyStaticallyImportedFields() throws Exception { + int fieldCount = 7000; + + createFile(myModule, "Constants.java", + "interface Constants { " + + IntStreamEx.range(0, fieldCount).mapToObj(i -> "String field" + i + ";").joining("\n") + + "}"); + + PsiFile file = createFile(myModule, "a.java", + "import static Constants.*;\n" + + "class Usage { \n" + + "void foo(String s) {}\n" + + "{" + + IntStreamEx.range(0, fieldCount).mapToObj(i -> "foo(field" + i + ");").joining("\n") + + "}}"); + + List refs = SyntaxTraverser.psiTraverser(file).filter(PsiJavaCodeReferenceElement.class).toList(); + + PlatformTestUtil.startPerformanceTest(getTestName(false), 1_000, () -> { + for (PsiJavaCodeReferenceElement ref : refs) { + assertNotNull(ref.resolve()); + } + }) + .setup(getPsiManager()::dropPsiCaches) + .assertTiming(); + + } + private void ensureIndexUpToDate() { getJavaFacade().findClass(CommonClassNames.JAVA_UTIL_LIST, GlobalSearchScope.allScope(myProject)); }