search java direct inheritors using javac indices (initial):
authorDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Mon, 10 Oct 2016 13:09:38 +0000 (16:09 +0300)
committerDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Mon, 10 Oct 2016 19:16:59 +0000 (22:16 +0300)
* use only one search adapter
* resolve anonymous child classes (e.g A$2$B)
* lazy iterate over candidates

java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java
java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java
java/java-indexing-impl/src/com/intellij/compiler/ClassResolvingCompilerSearchAdapter.java [moved from java/java-indexing-impl/src/com/intellij/compiler/CompilerDirectInheritorSearchAdapter.java with 90% similarity]
java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java
java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java
java/java-indexing-impl/src/com/intellij/compiler/JavaCompilerDirectInheritorSearchAdapter.java [deleted file]
java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java
java/java-tests/testData/compiler/bytecodeReferences/testHierarchy/Baz.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java

index fb2dd4fd7a2d6fd8434f3eb2332378578f1c392c..b5be62418ee6b255ee1935a22ae095ce48369451 100644 (file)
@@ -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
-  <T extends PsiNamedElement> Couple<Map<VirtualFile, T[]>> getDirectInheritors(@NotNull CompilerElement element,
-                                                                                @NotNull PsiNamedElement psiElement,
-                                                                                @NotNull CompilerDirectInheritorSearchAdapter<T> adapter,
+  @Nullable
+  <T extends PsiNamedElement> Couple<Map<VirtualFile, T[]>> getDirectInheritors(@NotNull PsiNamedElement psiElement,
+                                                                                @Nullable CompilerReferenceServiceImpl.CompilerElementInfo searchElementInfo,
+                                                                                @NotNull ClassResolvingCompilerSearchAdapter<T> inheritorSearchAdapter,
                                                                                 @NotNull GlobalSearchScope searchScope,
                                                                                 @NotNull GlobalSearchScope dirtyScope,
                                                                                 @NotNull Project project,
-                                                                                FileType... fileTypes) {
-    final LightUsage aClass = asLightUsage(element);
-    Collection<CompilerBackwardReferenceIndex.LightDefinition> candidates = myIndex.getBackwardHierarchyMap().get(aClass);
-    if (candidates == null) return Couple.of(Collections.emptyMap(), Collections.emptyMap());
+                                                                                FileType fileType) {
+    if (searchElementInfo == null) return null;
 
-    final Set<FileType> fileTypeSet = ContainerUtil.set(fileTypes);
+    CompilerBackwardReferenceIndex.LightDefinition[] candidates =
+      Stream.of(searchElementInfo.searchElements)
+      .map(this::asLightUsage)
+      .map(myIndex.getBackwardHierarchyMap()::get)
+      .filter(Objects::nonNull)
+      .flatMap(Collection::stream)
+      .toArray(CompilerBackwardReferenceIndex.LightDefinition[]::new);
 
-    final Set<Class<? extends LightUsage>> suitableClasses = new THashSet<>();
+    if (candidates == null) return Couple.of(Collections.emptyMap(), Collections.emptyMap());
+
+    Set<Class<? extends LightUsage>> 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<VirtualFile, SmartList<String>> perFileCandidates = candidates
-      .stream()
+    Map<VirtualFile, SmartList<String>> 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<VirtualFile, T[]> inheritors = new THashMap<>(perFileCandidates.size());
+    Map<VirtualFile, T[]> inheritors = new THashMap<>(candidatesPerFile.size());
     Map<VirtualFile, T[]> 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 {
index 376c6331e46defb289fcc3706e3547b9b3189439..6a7b673328cb8281cce4dea3960e53693dabaffc 100644 (file)
@@ -200,42 +200,47 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
   @Nullable
   @Override
   public <T extends PsiNamedElement> CompilerDirectInheritorInfo<T> getDirectInheritors(@NotNull PsiNamedElement aClass,
-                                                                                        @NotNull GlobalSearchScope useScope,
-                                                                                        @NotNull GlobalSearchScope searchScope,
-                                                                                        @NotNull CompilerSearchAdapter compilerSearchAdapter,
-                                                                                        @NotNull CompilerDirectInheritorSearchAdapter<T> 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<T> inheritorSearchAdapter,
+                                                                                         @NotNull FileType searchFileType) {
+    if (!isServiceEnabled() ||
+        InjectedLanguageManager.getInstance(myProject).isInjectedFragment(aClass.getContainingFile()) ||
+        !myProjectFileIndex.isInSourceContent(aClass.getContainingFile().getVirtualFile())) return null;
 
     Couple<Map<VirtualFile, T[]>> directInheritorsAndCandidates =
-      CachedValuesManager.getCachedValue(aClass, () -> CachedValueProvider.Result.create(myReader.getDirectInheritors(compilerElement,
-                                                                                                                      aClass,
-                                                                                                                      inheritorSearchAdapter,
-                                                                                                                      useScope,
-                                                                                                                      dirtyScope,
-                                                                                                                      myProject,
-                                                                                                                      searchFileTypes),
-                                                                                         PsiModificationTracker.MODIFICATION_COUNT,
-                                                                                         this));
-
-    return new CompilerDirectInheritorInfo<>(selectClassesInScope(directInheritorsAndCandidates.getFirst(), searchScope),
-                                             selectClassesInScope(directInheritorsAndCandidates.getSecond(), searchScope),
-                                             dirtyScope);
+      CachedValuesManager.getCachedValue(aClass, () -> CachedValueProvider.Result.create(calculateDirectInheritors(aClass,
+                                                                                                                   inheritorSearchAdapter,
+                                                                                                                   useScope,
+                                                                                                                   searchFileType),
+                                                                                       PsiModificationTracker.MODIFICATION_COUNT,
+                                                                                       this));
+
+    if (directInheritorsAndCandidates == null) return null;
+    return new CompilerDirectInheritorInfoImpl<>(directInheritorsAndCandidates, myDirtyModulesHolder.getDirtyScope(), searchScope);
   }
 
   private boolean isServiceEnabled() {
     return myReader != null && isEnabled();
   }
 
+  private <T extends PsiNamedElement> Couple<Map<VirtualFile, T[]>> calculateDirectInheritors(@NotNull PsiNamedElement aClass,
+                                                                                              @NotNull ClassResolvingCompilerSearchAdapter<T> 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 <T extends PsiNamedElement> List<T> selectClassesInScope(Map<VirtualFile, T[]> 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<VirtualFile> 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<T extends PsiNamedElement> implements CompilerDirectInheritorInfo<T> {
+    private final GlobalSearchScope myDirtyScope;
+    private final GlobalSearchScope mySearchScope;
+    private Couple<Map<VirtualFile, T[]>> myCandidatePerFile;
+
+    private CompilerDirectInheritorInfoImpl(Couple<Map<VirtualFile, T[]>> candidatePerFile,
+                                            GlobalSearchScope dirtyScope,
+                                            GlobalSearchScope searchScope) {
+      myCandidatePerFile = candidatePerFile;
+      myDirtyScope = dirtyScope;
+      mySearchScope = searchScope;
+    }
+
+    @Override
+    @NotNull
+    public Stream<T> getDirectInheritors() {
+      return selectClassesInScope(myCandidatePerFile.getFirst(), mySearchScope);
+    }
+
+    @Override
+    @NotNull
+    public Stream<T> getDirectInheritorCandidates() {
+      return selectClassesInScope(myCandidatePerFile.getSecond(), mySearchScope);
+    }
+
+    @Override
+    @NotNull
+    public GlobalSearchScope getDirtyScope() {
+      return myDirtyScope;
+    }
+
+    private static <T extends PsiNamedElement> Stream<T> selectClassesInScope(Map<VirtualFile, T[]> classesPerFile, GlobalSearchScope searchScope) {
+      return classesPerFile.entrySet().stream().filter(e -> searchScope.contains(e.getKey())).flatMap(e -> Stream.of(e.getValue()));
+    }
+  }
 }
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 b6dc1e91438d7a6d80aa40c16ebf156ceb5995e5..3ddd559cc799deac68eda8a9336cf711a2705cc5 100644 (file)
@@ -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<T extends PsiNamedElement> {
+public interface ClassResolvingCompilerSearchAdapter<T extends PsiNamedElement> extends CompilerSearchAdapter {
 
   /**
    * @param classInternalNames - collection compiler internal name of classes (e.g. org.some.Main$1 for java anonymous class)
index e0861146f8dd3ed9d01fa83791df33e5a02800b4..8d5869ad5c716d85b8f1175ca2de098f4164136e 100644 (file)
@@ -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 <T extends PsiNamedElement> CompilerDirectInheritorInfo<T> getDirectInheritors(@NotNull PsiNamedElement aClass,
                                                                                                  @NotNull GlobalSearchScope useScope,
                                                                                                  @NotNull GlobalSearchScope searchScope,
-                                                                                                 @NotNull CompilerSearchAdapter compilerSearchAdapter,
-                                                                                                 @NotNull CompilerDirectInheritorSearchAdapter<T> inheritorSearchAdapter,
-                                                                                                 @NotNull FileType... searchFileTypes);
+                                                                                                 @NotNull ClassResolvingCompilerSearchAdapter<T> inheritorSearchAdapter,
+                                                                                                 @NotNull FileType searchFileType);
 
 
   public static boolean isEnabled() {
     return IS_ENABLED_KEY.asBoolean();
   }
 
-  public static class CompilerDirectInheritorInfo<T extends PsiNamedElement> {
-    private final Collection<T> myDirectInheritors;
-    private final Collection<T> myDirectInheritorCandidates;
-    private final GlobalSearchScope myDirtyScope;
-
-    CompilerDirectInheritorInfo(Collection<T> directInheritors,
-                                Collection<T> directInheritorCandidates,
-                                GlobalSearchScope dirtyScope) {
-      myDirectInheritors = directInheritors;
-      myDirectInheritorCandidates = directInheritorCandidates;
-      myDirtyScope = dirtyScope;
-    }
-
+  public interface CompilerDirectInheritorInfo<T extends PsiNamedElement> {
     @NotNull
-    public Collection<T> getDirectInheritors() {
-      return myDirectInheritors;
-    }
+    Stream<T> getDirectInheritors();
 
     @NotNull
-    public Collection<T> getDirectInheritorCandidates() {
-      return myDirectInheritorCandidates;
-    }
+    Stream<T> getDirectInheritorCandidates();
 
     @NotNull
-    public GlobalSearchScope getDirtyScope() {
-      return myDirtyScope;
-    }
+    GlobalSearchScope getDirtyScope();
   }
 }
index 21b3c42c8bdd8aac4a0110a6c585519b35378e7f..a168b63996c74ee49fab737c23bd77d472a2e35c 100644 (file)
  */
 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<PsiClass> {
   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<String> classInternalNames,
+                                          @NotNull PsiNamedElement superClass,
+                                          @NotNull VirtualFile containingFile,
+                                          @NotNull Project project) {
+    Collection<InternalClassMatcher> 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<PsiClass> retrieveMatchedClasses(VirtualFile file, Project project, Collection<InternalClassMatcher> matchers) {
+    final List<PsiClass> 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 <T> boolean match(BiFunction<InternalClassMatcher, T, Boolean> matchingRule, T classObj, Collection<InternalClassMatcher> 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<InternalClassMatcher> createClassMatcher(@NotNull Collection<String> internalNames, @NotNull PsiNamedElement baseClass) {
+    boolean matcherBySuperNameAdded = false;
+    final List<InternalClassMatcher> 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 (file)
index ce07fd4..0000000
+++ /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<PsiClass> {
-  public static final JavaCompilerDirectInheritorSearchAdapter INSTANCE = new JavaCompilerDirectInheritorSearchAdapter();
-
-  @NotNull
-  @Override
-  public PsiClass[] getCandidatesFromFile(@NotNull Collection<String> 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<PsiClass> findClassByStub(VirtualFile file, String name, boolean isAnonymous, Project project) {
-    final List<PsiClass> 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;
-  }
-}
index 2352b4cb93353c4f3df16d35740b1742dd8fd981..9d106f05750d24cf26a68620421a105e6f145ed3 100644 (file)
@@ -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<PsiClass, Dir
       });
     }
 
-    Collection<PsiClass> 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<PsiClass, Dir
                                                      (GlobalSearchScope)useScope,
                                                      (GlobalSearchScope)scope,
                                                      JavaBaseCompilerSearchAdapter.INSTANCE,
-                                                     JavaCompilerDirectInheritorSearchAdapter.INSTANCE,
                                                      JavaFileType.INSTANCE);
       if (compilerDirectInheritorInfo != null) {
-        for (PsiClass aClass : compilerDirectInheritorInfo.getDirectInheritors()) {
-          consumer.process(aClass);
+        Iterator<PsiClass> 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<PsiClass> 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<PsiClass, Dir
   // The list starts with non-anonymous classes, ends with anonymous sub classes
   // Classes grouped by their FQN. (Because among the same-named subclasses we should return only the same-jar ones, or all of them if there were none)
   @NotNull
-  private static PsiClass[] getOrCalculateDirectSubClasses(@NotNull Project project,
-                                                           @NotNull PsiClass baseClass,
-                                                           @NotNull SearchScope useScope,
-                                                           Collection<PsiClass> anonymousCandidates) {
+  private static PsiClass[] getOrCalculateDirectSubClasses(@NotNull Project project, @NotNull PsiClass baseClass, @NotNull SearchScope useScope) {
     ConcurrentMap<PsiClass, PsiClass[]> map = HighlightingCaches.getInstance(project).DIRECT_SUB_CLASSES;
     PsiClass[] cache = map.get(baseClass);
     if (cache != null) {
@@ -180,7 +184,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
     if (StringUtil.isEmpty(baseClassName)) {
       return PsiClass.EMPTY_ARRAY;
     }
-    cache = calculateDirectSubClasses(project, baseClass, baseClassName, useScope, anonymousCandidates);
+    cache = calculateDirectSubClasses(project, baseClass, baseClassName, useScope);
     // for non-physical elements ignore the cache completely because non-physical elements created so often/unpredictably so I can't figure out when to clear caches in this case
     if (ApplicationManager.getApplication().runReadAction((Computable<Boolean>)baseClass::isPhysical)) {
       cache = ConcurrencyUtil.cacheOrGet(map, baseClass, cache);
@@ -203,8 +207,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
   private static PsiClass[] calculateDirectSubClasses(@NotNull Project project,
                                                       @NotNull PsiClass baseClass,
                                                       @NotNull String baseClassName,
-                                                      @NotNull SearchScope useScope,
-                                                      @NotNull Collection<PsiClass> 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<PsiClass, Dir
 
     Collection<PsiAnonymousClass> 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<PsiClass, Dir
   private static VirtualFile getJarFile(@NotNull PsiClass aClass) {
     return ApplicationManager.getApplication().runReadAction((Computable<VirtualFile>)() -> PsiUtil.getJarFile(aClass));
   }
+
+  private static Stream<PsiClass> filterOutAnonymousIfNeed(Stream<PsiClass> 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 (file)
index 0000000..18844f6
--- /dev/null
@@ -0,0 +1,15 @@
+public class Baz {
+
+  void m() {
+    new Runnable() {
+      public void run() {
+
+        class FooImpl2 extends Foo {
+
+        }
+
+      }
+    };
+  }
+
+}
\ No newline at end of file
index 94f513a39050a13c61e33fc6ac839675357e8790..64bb0a11ed36ce01bdadc8eb4b716c077e3410c1 100644 (file)
@@ -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<PsiClass> directInheritorInfo = getHierarchyUnderForElementCaret();
 
-    Collection<PsiClass> inheritors = directInheritorInfo.getDirectInheritors();
+    Collection<PsiClass> 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<PsiClass> candidates = directInheritorInfo.getDirectInheritorCandidates();
+    Collection<PsiClass> 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);
 
   }