From 9f842c25a6007e527036fa004f8af5d6f1ee92a0 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 10 Oct 2016 16:09:38 +0300 Subject: [PATCH] search java direct inheritors using javac indices (initial): * use only one search adapter * resolve anonymous child classes (e.g A$2$B) * lazy iterate over candidates --- .../compiler/CompilerReferenceReader.java | 42 ++--- .../CompilerReferenceServiceImpl.java | 149 ++++++++++++------ ... ClassResolvingCompilerSearchAdapter.java} | 3 +- .../compiler/CompilerReferenceService.java | 35 +--- .../JavaBaseCompilerSearchAdapter.java | 148 ++++++++++++++++- ...aCompilerDirectInheritorSearchAdapter.java | 116 -------------- .../search/JavaDirectInheritorsSearcher.java | 41 ++--- .../bytecodeReferences/testHierarchy/Baz.java | 15 ++ .../compiler/CompilerReferencesTest.java | 9 +- 9 files changed, 318 insertions(+), 240 deletions(-) rename java/java-indexing-impl/src/com/intellij/compiler/{CompilerDirectInheritorSearchAdapter.java => ClassResolvingCompilerSearchAdapter.java} (90%) delete mode 100644 java/java-indexing-impl/src/com/intellij/compiler/JavaCompilerDirectInheritorSearchAdapter.java create mode 100644 java/java-tests/testData/compiler/bytecodeReferences/testHierarchy/Baz.java diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java index fb2dd4fd7a2d..b5be62418ee6 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java @@ -42,7 +42,7 @@ import org.jetbrains.jps.backwardRefs.LightUsage; import java.io.File; import java.io.IOException; import java.util.*; -import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.util.stream.Collectors.*; @@ -75,31 +75,37 @@ class CompilerReferenceReader { return set; } - @NotNull - Couple> getDirectInheritors(@NotNull CompilerElement element, - @NotNull PsiNamedElement psiElement, - @NotNull CompilerDirectInheritorSearchAdapter adapter, + @Nullable + Couple> getDirectInheritors(@NotNull PsiNamedElement psiElement, + @Nullable CompilerReferenceServiceImpl.CompilerElementInfo searchElementInfo, + @NotNull ClassResolvingCompilerSearchAdapter inheritorSearchAdapter, @NotNull GlobalSearchScope searchScope, @NotNull GlobalSearchScope dirtyScope, @NotNull Project project, - FileType... fileTypes) { - final LightUsage aClass = asLightUsage(element); - Collection candidates = myIndex.getBackwardHierarchyMap().get(aClass); + FileType fileType) { + if (searchElementInfo == null) return null; + + CompilerBackwardReferenceIndex.LightDefinition[] candidates = + Stream.of(searchElementInfo.searchElements) + .map(this::asLightUsage) + .map(myIndex.getBackwardHierarchyMap()::get) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .toArray(CompilerBackwardReferenceIndex.LightDefinition[]::new); + if (candidates == null) return Couple.of(Collections.emptyMap(), Collections.emptyMap()); - final Set fileTypeSet = ContainerUtil.set(fileTypes); - - final Set> suitableClasses = new THashSet<>(); + Set> suitableClasses = new THashSet<>(); for (LanguageLightUsageConverter converter : LanguageLightUsageConverter.INSTANCES) { - if (fileTypeSet.contains(converter.getFileSourceType())) { + if (fileType == converter.getFileSourceType()) { suitableClasses.addAll(converter.getLanguageLightUsageClasses()); + break; } } final GlobalSearchScope effectiveSearchScope = GlobalSearchScope.notScope(dirtyScope).intersectWith(searchScope); - Map> perFileCandidates = candidates - .stream() + Map> candidatesPerFile = Stream.of(candidates) .filter(def -> suitableClasses.contains(def.getUsage().getClass())) .map(definition -> { final VirtualFile file = findFile(definition.getFileId()); @@ -108,13 +114,13 @@ class CompilerReferenceReader { .filter(Objects::nonNull) .collect(groupingBy(DecodedInheritorCandidate::getDeclarationFile, mapping(DecodedInheritorCandidate::getQName, toCollection(SmartList::new)))); - if (perFileCandidates.isEmpty()) return Couple.of(Collections.emptyMap(), Collections.emptyMap()); + if (candidatesPerFile.isEmpty()) return Couple.of(Collections.emptyMap(), Collections.emptyMap()); - Map inheritors = new THashMap<>(perFileCandidates.size()); + Map inheritors = new THashMap<>(candidatesPerFile.size()); Map inheritorCandidates = new THashMap<>(); - perFileCandidates.forEach((file, directInheritors) -> { - final T[] currInheritors = adapter.getCandidatesFromFile(directInheritors, psiElement, file, project); + candidatesPerFile.forEach((file, directInheritors) -> { + final T[] currInheritors = inheritorSearchAdapter.getCandidatesFromFile(directInheritors, psiElement, file, project); if (currInheritors.length == directInheritors.size()) { inheritors.put(file, currInheritors); } else { diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java index 376c6331e46d..6a7b673328cb 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java @@ -200,42 +200,47 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple @Nullable @Override public CompilerDirectInheritorInfo getDirectInheritors(@NotNull PsiNamedElement aClass, - @NotNull GlobalSearchScope useScope, - @NotNull GlobalSearchScope searchScope, - @NotNull CompilerSearchAdapter compilerSearchAdapter, - @NotNull CompilerDirectInheritorSearchAdapter inheritorSearchAdapter, - @NotNull FileType... searchFileTypes) { - if (!isServiceEnabled() || InjectedLanguageManager.getInstance(myProject).isInjectedFragment(aClass.getContainingFile())) return null; - - //TODO should be available to search inheritors of lib classes - final VirtualFile file = aClass.getContainingFile().getVirtualFile(); - if (!myProjectFileIndex.isInSourceContent(file)) return null; - - final CompilerElement compilerElement = compilerSearchAdapter.asCompilerElement(aClass); - if (compilerElement == null) return null; - - final GlobalSearchScope dirtyScope = myDirtyModulesHolder.getDirtyScope(); + @NotNull GlobalSearchScope useScope, + @NotNull GlobalSearchScope searchScope, + @NotNull ClassResolvingCompilerSearchAdapter inheritorSearchAdapter, + @NotNull FileType searchFileType) { + if (!isServiceEnabled() || + InjectedLanguageManager.getInstance(myProject).isInjectedFragment(aClass.getContainingFile()) || + !myProjectFileIndex.isInSourceContent(aClass.getContainingFile().getVirtualFile())) return null; Couple> directInheritorsAndCandidates = - CachedValuesManager.getCachedValue(aClass, () -> CachedValueProvider.Result.create(myReader.getDirectInheritors(compilerElement, - aClass, - inheritorSearchAdapter, - useScope, - dirtyScope, - myProject, - searchFileTypes), - PsiModificationTracker.MODIFICATION_COUNT, - this)); + CachedValuesManager.getCachedValue(aClass, () -> CachedValueProvider.Result.create(calculateDirectInheritors(aClass, + inheritorSearchAdapter, + useScope, + searchFileType), + PsiModificationTracker.MODIFICATION_COUNT, + this)); - return new CompilerDirectInheritorInfo<>(selectClassesInScope(directInheritorsAndCandidates.getFirst(), searchScope), - selectClassesInScope(directInheritorsAndCandidates.getSecond(), searchScope), - dirtyScope); + if (directInheritorsAndCandidates == null) return null; + return new CompilerDirectInheritorInfoImpl<>(directInheritorsAndCandidates, myDirtyModulesHolder.getDirtyScope(), searchScope); } private boolean isServiceEnabled() { return myReader != null && isEnabled(); } + private Couple> calculateDirectInheritors(@NotNull PsiNamedElement aClass, + @NotNull ClassResolvingCompilerSearchAdapter searchAdapter, + @NotNull GlobalSearchScope useScope, + FileType searchFileType) { + final CompilerElementInfo searchElementInfo = asCompilerElements(aClass, searchAdapter); + synchronized (myLock) { + if (myReader == null) return null; + return myReader.getDirectInheritors(aClass, + searchElementInfo, + searchAdapter, + useScope, + myDirtyModulesHolder.getDirtyScope(), + myProject, + searchFileType); + } + } + @Nullable private GlobalSearchScope calculateScopeWithoutReferences(@NotNull PsiElement element, CompilerSearchAdapter adapter) { TIntHashSet referentFileIds = getReferentFileIds(element, adapter); @@ -247,6 +252,23 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple @Nullable private TIntHashSet getReferentFileIds(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) { + final CompilerElementInfo compilerElementInfo = asCompilerElements(element, adapter); + if (compilerElementInfo == null) return null; + + synchronized (myLock) { + if (myReader == null) return null; + TIntHashSet referentFileIds = new TIntHashSet(); + for (CompilerElement compilerElement : compilerElementInfo.searchElements) { + final TIntHashSet referents = myReader.findReferentFileIds(compilerElement, adapter, compilerElementInfo.place == ElementPlace.SRC); + if (referents == null) return null; + referentFileIds.addAll(referents.toArray()); + } + return referentFileIds; + } + } + + @Nullable + private CompilerElementInfo asCompilerElements(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) { final PsiFile file = element.getContainingFile(); if (file == null) return null; final VirtualFile vFile = file.getVirtualFile(); @@ -260,25 +282,13 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple if (myDirtyModulesHolder.contains(vFile)) { return null; } - CompilerElement[] compilerElements; if (place == ElementPlace.SRC) { final CompilerElement compilerElement = adapter.asCompilerElement(element); - compilerElements = compilerElement == null ? CompilerElement.EMPTY_ARRAY : new CompilerElement[]{compilerElement}; + return compilerElement == null ? null : new CompilerElementInfo(place, compilerElement); } else { - compilerElements = adapter.libraryElementAsCompilerElements(element); - } - if (compilerElements.length == 0) return null; - - synchronized (myLock) { - if (myReader == null) return null; - TIntHashSet referentFileIds = new TIntHashSet(); - for (CompilerElement compilerElement : compilerElements) { - final TIntHashSet referents = myReader.findReferentFileIds(compilerElement, adapter, place == ElementPlace.SRC); - if (referents == null) return null; - referentFileIds.addAll(referents.toArray()); - } - return referentFileIds; + final CompilerElement[] elements = adapter.libraryElementAsCompilerElements(element); + return elements.length == 0 ? null : new CompilerElementInfo(place, elements); } } @@ -299,15 +309,6 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple } } - private static List selectClassesInScope(Map classesPerFile, GlobalSearchScope searchScope) { - return classesPerFile - .entrySet() - .stream() - .filter(e -> searchScope.contains(e.getKey())) - .flatMap(e -> Stream.of(e.getValue())) - .collect(Collectors.toList()); - } - @TestOnly @Nullable public Set getReferentFiles(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) { @@ -430,4 +431,50 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple } } } + + static class CompilerElementInfo { + final ElementPlace place; + final CompilerElement[] searchElements; + + private CompilerElementInfo(ElementPlace place, CompilerElement... searchElements) { + this.searchElements = searchElements; + this.place = place; + } + } + + private static class CompilerDirectInheritorInfoImpl implements CompilerDirectInheritorInfo { + private final GlobalSearchScope myDirtyScope; + private final GlobalSearchScope mySearchScope; + private Couple> myCandidatePerFile; + + private CompilerDirectInheritorInfoImpl(Couple> candidatePerFile, + GlobalSearchScope dirtyScope, + GlobalSearchScope searchScope) { + myCandidatePerFile = candidatePerFile; + myDirtyScope = dirtyScope; + mySearchScope = searchScope; + } + + @Override + @NotNull + public Stream getDirectInheritors() { + return selectClassesInScope(myCandidatePerFile.getFirst(), mySearchScope); + } + + @Override + @NotNull + public Stream getDirectInheritorCandidates() { + return selectClassesInScope(myCandidatePerFile.getSecond(), mySearchScope); + } + + @Override + @NotNull + public GlobalSearchScope getDirtyScope() { + return myDirtyScope; + } + + private static Stream selectClassesInScope(Map classesPerFile, GlobalSearchScope searchScope) { + return classesPerFile.entrySet().stream().filter(e -> searchScope.contains(e.getKey())).flatMap(e -> Stream.of(e.getValue())); + } + } } diff --git a/java/java-indexing-impl/src/com/intellij/compiler/CompilerDirectInheritorSearchAdapter.java b/java/java-indexing-impl/src/com/intellij/compiler/ClassResolvingCompilerSearchAdapter.java similarity index 90% rename from java/java-indexing-impl/src/com/intellij/compiler/CompilerDirectInheritorSearchAdapter.java rename to java/java-indexing-impl/src/com/intellij/compiler/ClassResolvingCompilerSearchAdapter.java index b6dc1e91438d..3ddd559cc799 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/CompilerDirectInheritorSearchAdapter.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/ClassResolvingCompilerSearchAdapter.java @@ -18,12 +18,11 @@ package com.intellij.compiler; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiNamedElement; -import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; import java.util.Collection; -public interface CompilerDirectInheritorSearchAdapter { +public interface ClassResolvingCompilerSearchAdapter extends CompilerSearchAdapter { /** * @param classInternalNames - collection compiler internal name of classes (e.g. org.some.Main$1 for java anonymous class) diff --git a/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java b/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java index e0861146f8dd..8d5869ad5c71 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java @@ -26,7 +26,7 @@ import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; +import java.util.stream.Stream; public abstract class CompilerReferenceService extends AbstractProjectComponent { public static final RegistryValue IS_ENABLED_KEY = Registry.get("bytecode.ref.index"); @@ -46,41 +46,22 @@ public abstract class CompilerReferenceService extends AbstractProjectComponent public abstract CompilerDirectInheritorInfo getDirectInheritors(@NotNull PsiNamedElement aClass, @NotNull GlobalSearchScope useScope, @NotNull GlobalSearchScope searchScope, - @NotNull CompilerSearchAdapter compilerSearchAdapter, - @NotNull CompilerDirectInheritorSearchAdapter inheritorSearchAdapter, - @NotNull FileType... searchFileTypes); + @NotNull ClassResolvingCompilerSearchAdapter inheritorSearchAdapter, + @NotNull FileType searchFileType); public static boolean isEnabled() { return IS_ENABLED_KEY.asBoolean(); } - public static class CompilerDirectInheritorInfo { - private final Collection myDirectInheritors; - private final Collection myDirectInheritorCandidates; - private final GlobalSearchScope myDirtyScope; - - CompilerDirectInheritorInfo(Collection directInheritors, - Collection directInheritorCandidates, - GlobalSearchScope dirtyScope) { - myDirectInheritors = directInheritors; - myDirectInheritorCandidates = directInheritorCandidates; - myDirtyScope = dirtyScope; - } + public interface CompilerDirectInheritorInfo { + @NotNull + Stream getDirectInheritors(); @NotNull - public Collection getDirectInheritors() { - return myDirectInheritors; - } + Stream getDirectInheritorCandidates(); @NotNull - public Collection getDirectInheritorCandidates() { - return myDirectInheritorCandidates; - } - - @NotNull - public GlobalSearchScope getDirtyScope() { - return myDirtyScope; - } + GlobalSearchScope getDirtyScope(); } } diff --git a/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java b/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java index 21b3c42c8bdd..a168b63996c7 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java @@ -15,19 +15,28 @@ */ package com.intellij.compiler; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.impl.LibraryScopeCache; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.impl.java.stubs.PsiClassStub; +import com.intellij.psi.impl.source.PsiFileWithStubSupport; import com.intellij.psi.search.searches.ClassInheritorsSearch; +import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.stubs.StubTree; import com.intellij.psi.util.ClassUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.function.BiFunction; import java.util.function.Function; -public class JavaBaseCompilerSearchAdapter implements CompilerSearchAdapter { +public class JavaBaseCompilerSearchAdapter implements ClassResolvingCompilerSearchAdapter { public static final JavaBaseCompilerSearchAdapter INSTANCE = new JavaBaseCompilerSearchAdapter(); @Override @@ -109,6 +118,16 @@ public class JavaBaseCompilerSearchAdapter implements CompilerSearchAdapter { return CompilerElement.EMPTY_ARRAY; } + @NotNull + @Override + public PsiClass[] getCandidatesFromFile(@NotNull Collection classInternalNames, + @NotNull PsiNamedElement superClass, + @NotNull VirtualFile containingFile, + @NotNull Project project) { + Collection matchers = createClassMatcher(classInternalNames, superClass); + return retrieveMatchedClasses(containingFile, project, matchers).toArray(PsiClass.EMPTY_ARRAY); + } + private static boolean mayBeVisibleOutsideOwnerFile(@NotNull PsiElement element) { if (!(element instanceof PsiModifierListOwner)) return true; if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.PRIVATE)) return false; @@ -122,4 +141,127 @@ public class JavaBaseCompilerSearchAdapter implements CompilerSearchAdapter { .forEach(processor); } } + + private static Collection retrieveMatchedClasses(VirtualFile file, Project project, Collection matchers) { + final List result = new ArrayList<>(matchers.size()); + PsiFileWithStubSupport psiFile = ObjectUtils.notNull((PsiFileWithStubSupport)PsiManager.getInstance(project).findFile(file)); + StubTree tree = psiFile.getStubTree(); + if (tree != null) { + for (StubElement element : tree.getPlainListFromAllRoots()) { + if (element instanceof PsiClassStub && match((m, e) -> m.matches(e), (PsiClassStub)element, matchers)) { + result.add((PsiClass)element.getPsi()); + } + } + } else { + PsiTreeUtil.processElements(psiFile, e -> { + if (e instanceof PsiClass && match((m, c) -> m.matches((PsiClass)c), e, matchers)) { + result.add((PsiClass)e); + } + return true; + }); + } + return result; + } + + private static boolean match(BiFunction matchingRule, T classObj, Collection matchers) { + for (InternalClassMatcher matcher : matchers) { + if (matchingRule.apply(matcher, classObj)) { + //qualified name is unique among file's classes + if (matcher instanceof InternalClassMatcher.ByQualifiedName) { + matchers.remove(matcher); + } + return true; + } + } + return false; + } + + private static Collection createClassMatcher(@NotNull Collection internalNames, @NotNull PsiNamedElement baseClass) { + boolean matcherBySuperNameAdded = false; + final List matchers = new ArrayList<>(internalNames.size()); + for (String internalName : internalNames) { + int curLast = internalName.length() - 1; + while (true) { + int lastIndex = internalName.lastIndexOf('$', curLast); + if (lastIndex > -1 && lastIndex < internalName.length() - 1) { + final boolean anonymousSign = Character.isDigit(internalName.charAt(lastIndex + 1)); + if (anonymousSign) { + if (curLast == internalName.length() - 1) { + if (matcherBySuperNameAdded) { + break; + } + matcherBySuperNameAdded = true; + matchers.add(new InternalClassMatcher.BySuperName(baseClass.getName())); + break; + } + else { + matchers.add(new InternalClassMatcher.ByName(StringUtil.getShortName(internalName, '$'))); + break; + } + } + } + else { + matchers.add(new InternalClassMatcher.ByQualifiedName(StringUtil.replace(internalName, "$", "."))); + break; + } + curLast = lastIndex - 1; + } + } + return matchers; + } + + private interface InternalClassMatcher { + boolean matches(PsiClass psiClass); + + boolean matches(PsiClassStub stub); + + class BySuperName implements InternalClassMatcher { + private final String mySuperName; + + public BySuperName(String name) {mySuperName = name;} + + @Override + public boolean matches(PsiClass psiClass) { + return psiClass instanceof PsiAnonymousClass && + mySuperName.equals(StringUtil.getShortName(((PsiAnonymousClass)psiClass).getBaseClassReference().getText())); + } + + @Override + public boolean matches(PsiClassStub stub) { + return stub.isAnonymous() && mySuperName.equals(PsiNameHelper.getShortClassName(stub.getBaseClassReferenceText())); + } + } + + class ByName implements InternalClassMatcher { + private final String myName; + + public ByName(String name) {myName = name;} + + @Override + public boolean matches(PsiClass psiClass) { + return myName.equals(psiClass.getName()); + } + + @Override + public boolean matches(PsiClassStub stub) { + return myName.equals(stub.getName()); + } + } + + class ByQualifiedName implements InternalClassMatcher { + private final String myQName; + + public ByQualifiedName(String name) {myQName = name;} + + @Override + public boolean matches(PsiClass psiClass) { + return myQName.equals(psiClass.getQualifiedName()); + } + + @Override + public boolean matches(PsiClassStub stub) { + return myQName.equals(stub.getQualifiedName()); + } + } + } } diff --git a/java/java-indexing-impl/src/com/intellij/compiler/JavaCompilerDirectInheritorSearchAdapter.java b/java/java-indexing-impl/src/com/intellij/compiler/JavaCompilerDirectInheritorSearchAdapter.java deleted file mode 100644 index ce07fd4640dc..000000000000 --- a/java/java-indexing-impl/src/com/intellij/compiler/JavaCompilerDirectInheritorSearchAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.compiler; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; -import com.intellij.psi.impl.java.stubs.PsiClassStub; -import com.intellij.psi.impl.source.PsiFileWithStubSupport; -import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.stubs.StubTree; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.ObjectUtils; -import com.intellij.util.SmartList; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -public class JavaCompilerDirectInheritorSearchAdapter implements CompilerDirectInheritorSearchAdapter { - public static final JavaCompilerDirectInheritorSearchAdapter INSTANCE = new JavaCompilerDirectInheritorSearchAdapter(); - - @NotNull - @Override - public PsiClass[] getCandidatesFromFile(@NotNull Collection classInternalNames, - @NotNull PsiNamedElement superClass, - @NotNull VirtualFile containingFile, - @NotNull Project project) { - final PsiClass[] result = new PsiClass[classInternalNames.size()]; - int i = 0; - boolean anonymousClassesAdded = false; - for (String classInternalName : classInternalNames) { - String name; - - boolean isAnonymous = isAnonymousClass(classInternalName); - if (isAnonymous) { - if (anonymousClassesAdded) { - continue; - } - anonymousClassesAdded = true; - name = ObjectUtils.notNull(superClass.getName()); - } - else { - name = StringUtil.replace(classInternalName, "$", "."); - } - for (PsiClass c : findClassByStub(containingFile, name, isAnonymous, project)) { - result[i++] = c; - } - } - return result; - } - - private static boolean isAnonymousClass(@NotNull String name) { - int lastIndex = name.lastIndexOf('$'); - return lastIndex != -1 && lastIndex < name.length() - 1 && Character.isDigit(name.charAt(lastIndex + 1)); - } - - private static Collection findClassByStub(VirtualFile file, String name, boolean isAnonymous, Project project) { - final List result = new SmartList<>(); - PsiFileWithStubSupport psiFile = ObjectUtils.notNull((PsiFileWithStubSupport)PsiManager.getInstance(project).findFile(file)); - StubTree tree = psiFile.getStubTree(); - if (tree != null) { - for (StubElement element : tree.getPlainListFromAllRoots()) { - if (element instanceof PsiClassStub) { - if (isAnonymous) { - String baseClassRef = ((PsiClassStub)element).getBaseClassReferenceText(); - if (baseClassRef != null && ((PsiClassStub)element).isAnonymous() && name.equals(PsiNameHelper.getShortClassName(baseClassRef))) { - result.add((PsiClass)element.getPsi()); - } - } else { - if (!((PsiClassStub)element).isAnonymous() && name.equals(((PsiClassStub)element).getQualifiedName())) { - result.add((PsiClass)element.getPsi()); - } - } - } - } - } else { - PsiTreeUtil.processElements(psiFile, e -> { - if (e instanceof PsiAnonymousClass) { - if (isAnonymous) { - String baseClassRefText = ((PsiAnonymousClass)e).getBaseClassReference().getText(); - if (name.equals(PsiNameHelper.getShortClassName(baseClassRefText))) { - result.add((PsiClass)e); - } - } - return true; - } - else if (e instanceof PsiClass) { - if (!isAnonymous) { - if (name.equals(((PsiClass)e).getQualifiedName())) { - result.add((PsiClass)e); - } - } - } - return true; - }); - } - return result; - } -} diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java index 2352b4cb9335..9d106f05750d 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java @@ -17,7 +17,6 @@ package com.intellij.psi.impl.search; import com.intellij.compiler.CompilerReferenceService; import com.intellij.compiler.JavaBaseCompilerSearchAdapter; -import com.intellij.compiler.JavaCompilerDirectInheritorSearchAdapter; import com.intellij.concurrency.JobLauncher; import com.intellij.ide.highlighter.JavaFileType; import com.intellij.openapi.application.ApplicationManager; @@ -49,6 +48,7 @@ import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.concurrent.ConcurrentMap; +import java.util.stream.Stream; /** * @author max @@ -73,7 +73,6 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor anonymousCandidatesFromCompilerSearchIndex = Collections.emptyList(); SearchScope scope = parameters.getScope(); if (useScope instanceof GlobalSearchScope && scope instanceof GlobalSearchScope) { PsiClass searchClass = baseClass; @@ -87,20 +86,28 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor inheritors = filterOutAnonymousIfNeed(compilerDirectInheritorInfo.getDirectInheritorCandidates(), parameters).iterator(); + while (inheritors.hasNext()) { + ProgressManager.checkCanceled(); + PsiClass next = inheritors.next(); + if (!consumer.process(next)) return false; } - // here candidates is only anonymous classes whose containing file has more than one anonymous inheritor of baseClass - anonymousCandidatesFromCompilerSearchIndex = compilerDirectInheritorInfo.getDirectInheritorCandidates(); + + Iterator candidates = filterOutAnonymousIfNeed(compilerDirectInheritorInfo.getDirectInheritorCandidates(), parameters).iterator(); + while (candidates.hasNext()) { + ProgressManager.checkCanceled(); + PsiClass next = candidates.next(); + if (next.isInheritor(baseClass, false) && !consumer.process(next)) return false; + } + scope = ((GlobalSearchScope)scope).intersectWith(compilerDirectInheritorInfo.getDirtyScope()); useScope = ((GlobalSearchScope)useScope).intersectWith(compilerDirectInheritorInfo.getDirtyScope()); } } - PsiClass[] cache = getOrCalculateDirectSubClasses(project, baseClass, useScope, anonymousCandidatesFromCompilerSearchIndex); + PsiClass[] cache = getOrCalculateDirectSubClasses(project, baseClass, useScope); if (cache.length == 0) { return true; @@ -166,10 +173,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor anonymousCandidates) { + private static PsiClass[] getOrCalculateDirectSubClasses(@NotNull Project project, @NotNull PsiClass baseClass, @NotNull SearchScope useScope) { ConcurrentMap map = HighlightingCaches.getInstance(project).DIRECT_SUB_CLASSES; PsiClass[] cache = map.get(baseClass); if (cache != null) { @@ -180,7 +184,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor)baseClass::isPhysical)) { cache = ConcurrencyUtil.cacheOrGet(map, baseClass, cache); @@ -203,8 +207,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor anonymousCandidatesFromCompilerIndicesSearch) { + @NotNull SearchScope useScope) { DumbService dumbService = DumbService.getInstance(project); GlobalSearchScope globalUseScope = dumbService.runReadActionInSmartMode( () -> StubHierarchyInheritorSearcher.restrictScope(GlobalSearchScopeUtil.toGlobalSearchScope(useScope, project))); @@ -259,9 +262,6 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor anonymousCandidates = dumbService.runReadActionInSmartMode(() -> JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(baseClassName, project, globalUseScope)); - for (PsiClass candidate : anonymousCandidatesFromCompilerIndicesSearch) { - anonymousCandidates.add((PsiAnonymousClass)candidate); - } processConcurrentlyIfTooMany(anonymousCandidates, candidate-> { @@ -296,4 +296,9 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor)() -> PsiUtil.getJarFile(aClass)); } + + private static Stream filterOutAnonymousIfNeed(Stream classStream, + DirectClassInheritorsSearch.SearchParameters parameters) { + return parameters.includeAnonymous() ? classStream : classStream.filter(c -> !(c instanceof PsiAnonymousClass)); + } } diff --git a/java/java-tests/testData/compiler/bytecodeReferences/testHierarchy/Baz.java b/java/java-tests/testData/compiler/bytecodeReferences/testHierarchy/Baz.java new file mode 100644 index 000000000000..18844f6ce9bd --- /dev/null +++ b/java/java-tests/testData/compiler/bytecodeReferences/testHierarchy/Baz.java @@ -0,0 +1,15 @@ +public class Baz { + + void m() { + new Runnable() { + public void run() { + + class FooImpl2 extends Foo { + + } + + } + }; + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java index 94f513a39050..64bb0a11ed36 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java @@ -83,21 +83,21 @@ public class CompilerReferencesTest extends AbstractCompilerAwareTest { } public void testHierarchy() { - myFixture.configureByFiles(getName() + "/Foo.java", getName() + "/FooImpl.java", getName() + "/Bar.java"); + myFixture.configureByFiles(getName() + "/Foo.java", getName() + "/FooImpl.java", getName() + "/Bar.java", getName() + "/Baz.java"); rebuildProject(); CompilerReferenceService.CompilerDirectInheritorInfo directInheritorInfo = getHierarchyUnderForElementCaret(); - Collection inheritors = directInheritorInfo.getDirectInheritors(); + Collection inheritors = directInheritorInfo.getDirectInheritors().collect(Collectors.toList()); assertSize(4, inheritors); for (PsiClass inheritor : inheritors) { if (inheritor instanceof PsiAnonymousClass) { assertOneOf(inheritor.getTextOffset(), 58, 42, 94); } else { - assertEquals("FooImpl", inheritor.getQualifiedName()); + assertOneOf(inheritor.getQualifiedName(), "FooImpl", "FooImpl2"); } } - Collection candidates = directInheritorInfo.getDirectInheritorCandidates(); + Collection candidates = directInheritorInfo.getDirectInheritorCandidates().collect(Collectors.toList()); assertEmpty(candidates); } @@ -110,7 +110,6 @@ public class CompilerReferencesTest extends AbstractCompilerAwareTest { assertInstanceOf(classAtCaret.getUseScope(), GlobalSearchScope.class), assertInstanceOf(classAtCaret.getUseScope(), GlobalSearchScope.class), JavaBaseCompilerSearchAdapter.INSTANCE, - JavaCompilerDirectInheritorSearchAdapter.INSTANCE, StdFileTypes.JAVA); }