java: speedup resolving of statically-imported members by honoring name hint (IDEA-247726)

GitOrigin-RevId: 8cbdf1fca1afc4023fc95f1ed5e6da5fcc589000
This commit is contained in:
Peter Gromov
2020-08-07 17:00:45 +00:00
committed by intellij-monorepo-bot
parent 781de9a390
commit 553cf412e7
4 changed files with 83 additions and 24 deletions
@@ -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<PsiMember> 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;
}
}
@@ -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;
@@ -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..<fieldCount).collect { "String field$it = \"x\";\n" } +
"}")
myFixture.configureByText("a.java", "import static Constants.*; class C { { field<caret>x } }")
PlatformTestUtil.startPerformanceTest(name, 10_000, {
assert myFixture.completeBasic().length > 100
}).setup {
lookup?.hideLookup(true)
myFixture.type("\bd")
psiManager.dropPsiCaches()
assert !lookup
}.assertTiming()
}
}
@@ -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<PsiJavaCodeReferenceElement> 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));
}