From 6d79ed7498f6fbf189bd50dcfbb29d9655d7df4e Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 9 Jun 2016 12:03:41 +0200 Subject: [PATCH] make TestStubHierarchyAction actually check something + minor renames/moves --- .../stubsHierarchy/impl/ClassAnchorUtil.java | 79 ------------ .../impl/SingleClassHierarchy.java | 13 +- .../stubsHierarchy/impl/SmartClassAnchor.java | 55 ++++++++ .../impl/test/LogSubtypesAction.java | 7 +- .../impl/test/TestStubHierarchyAction.java | 117 ++++++++++++------ 5 files changed, 145 insertions(+), 126 deletions(-) delete mode 100644 java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/ClassAnchorUtil.java diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/ClassAnchorUtil.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/ClassAnchorUtil.java deleted file mode 100644 index 716aae3cd55d..000000000000 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/ClassAnchorUtil.java +++ /dev/null @@ -1,79 +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.psi.stubsHierarchy.impl; - -import com.intellij.lang.ASTNode; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.psi.*; -import com.intellij.psi.impl.source.PsiFileImpl; -import com.intellij.psi.impl.source.PsiFileWithStubSupport; -import com.intellij.psi.stubs.StubBase; -import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.stubs.StubTree; - -import java.util.List; - -public class ClassAnchorUtil { - - public static PsiClass retrieve(Project project, SmartClassAnchor anchor) { - PsiFile psiFile = PsiManager.getInstance(project).findFile(anchor.retrieveFile()); - assert psiFile != null; - return (PsiClass)restoreFromStubIndex((PsiFileWithStubSupport)psiFile, anchor.myStubId); - } - - public static PsiClass retrieveInReadAction(final Project project, final SmartClassAnchor anchor) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiClass compute() { - return retrieve(project, anchor); - } - }); - } - - private static PsiElement restoreFromStubIndex(PsiFileWithStubSupport fileImpl, int index) { - StubTree tree = fileImpl.getStubTree(); - - boolean foreign = tree == null; - if (foreign) { - if (fileImpl instanceof PsiFileImpl) { - tree = ((PsiFileImpl)fileImpl).calcStubTree(); - } - else { - return null; - } - } - - List> list = tree.getPlainList(); - if (index >= list.size()) { - return null; - } - StubElement stub = list.get(index); - - if (foreign) { - final PsiElement cachedPsi = ((StubBase)stub).getCachedPsi(); - if (cachedPsi != null) return cachedPsi; - - final ASTNode ast = fileImpl.findTreeForStub(tree, stub); - if (ast != null) { - return ast.getPsi(); - } - return null; - } - return stub.getPsi(); - } -} diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java index 1fcb0a2af886..e44b3c544479 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java @@ -20,6 +20,7 @@ import com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol; import com.intellij.util.indexing.FileBasedIndex; import gnu.trove.TIntHashSet; import gnu.trove.TIntStack; +import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -37,13 +38,13 @@ public class SingleClassHierarchy { this.myClassAnchorsByFileIds = mkByFileId(this.myClassAnchors); } - // under read actions - public SmartClassAnchor[] getSubtypes(PsiClass psiClass) { + public SmartClassAnchor[] getDirectSubtypes(PsiClass psiClass) { int fileId = Math.abs(FileBasedIndex.getFileId(psiClass.getContainingFile().getVirtualFile())); SmartClassAnchor anchor = forPsiClass(fileId, psiClass); - if (anchor == null) { - return SmartClassAnchor.EMPTY_ARRAY; - } + return anchor == null ? SmartClassAnchor.EMPTY_ARRAY : getDirectSubtypes(anchor); + } + + public SmartClassAnchor[] getDirectSubtypes(@NotNull SmartClassAnchor anchor) { int symbolId = anchor.myId; int start = subtypeStart(symbolId); int end = subtypeEnd(symbolId); @@ -184,7 +185,7 @@ public class SingleClassHierarchy { SmartClassAnchor candidate = myClassAnchors[id]; if (candidate.myFileId != fileId) return null; - if (psiClass.isEquivalentTo(ClassAnchorUtil.retrieveInReadAction(psiClass.getProject(), candidate))) + if (psiClass.isEquivalentTo(candidate.retrieveClass(psiClass.getProject()))) return candidate; id++; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SmartClassAnchor.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SmartClassAnchor.java index c92f93531587..daad6ce14a5f 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SmartClassAnchor.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SmartClassAnchor.java @@ -15,9 +15,23 @@ */ package com.intellij.psi.stubsHierarchy.impl; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.source.PsiFileImpl; +import com.intellij.psi.impl.source.PsiFileWithStubSupport; +import com.intellij.psi.stubs.StubBase; +import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.stubs.StubTree; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; public class SmartClassAnchor { public static final SmartClassAnchor[] EMPTY_ARRAY = new SmartClassAnchor[0]; @@ -39,6 +53,47 @@ public class SmartClassAnchor { return file; } + @Nullable + public PsiClass retrieveClass(@NotNull Project project) { + PsiFile psiFile = PsiManager.getInstance(project).findFile(retrieveFile()); + assert psiFile != null; + return (PsiClass)restoreFromStubIndex((PsiFileWithStubSupport)psiFile, myStubId); + } + + private static PsiElement restoreFromStubIndex(PsiFileWithStubSupport fileImpl, int index) { + StubTree tree = fileImpl.getStubTree(); + + boolean foreign = tree == null; + if (foreign) { + if (fileImpl instanceof PsiFileImpl) { + tree = ((PsiFileImpl)fileImpl).calcStubTree(); + } + else { + return null; + } + } + + List> list = tree.getPlainList(); + if (index >= list.size()) { + return null; + } + StubElement stub = list.get(index); + + if (foreign) { + final PsiElement cachedPsi = ((StubBase)stub).getCachedPsi(); + if (cachedPsi != null) return cachedPsi; + + final ASTNode ast = fileImpl.findTreeForStub(tree, stub); + return ast != null ? ast.getPsi() : null; + } + return stub.getPsi(); + } + + @Override + public int hashCode() { + return myId; + } + @Override public String toString() { return myStubId + " in " + retrieveFile().getPath(); diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/LogSubtypesAction.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/LogSubtypesAction.java index ba5c2632f9ba..36923dcbdf46 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/LogSubtypesAction.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/LogSubtypesAction.java @@ -23,9 +23,8 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.psi.stubsHierarchy.impl.ClassAnchorUtil; -import com.intellij.psi.stubsHierarchy.impl.SmartClassAnchor; import com.intellij.psi.stubsHierarchy.impl.HierarchyService; +import com.intellij.psi.stubsHierarchy.impl.SmartClassAnchor; import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -45,11 +44,11 @@ public class LogSubtypesAction extends InheritanceAction { HierarchyService hierarchyService = HierarchyService.instance(project); if (hierarchyService != null) { LOG.info("Subtypes of " + psiClass + " retrieve started"); - SmartClassAnchor[] classAnchors = hierarchyService.getSingleClassHierarchy().getSubtypes(psiClass); + SmartClassAnchor[] classAnchors = hierarchyService.getSingleClassHierarchy().getDirectSubtypes(psiClass); String[] subtypes = new String[classAnchors.length]; LOG.info("Subtypes of " + psiClass + " retrieve PSI started, count=" + classAnchors.length); for (int i = 0; i < classAnchors.length; i++) { - PsiClass subClass = ClassAnchorUtil.retrieve(project, classAnchors[i]); + PsiClass subClass = classAnchors[i].retrieveClass(project); subtypes[i] = subClass.toString() + "(" + subClass.getQualifiedName() + ", " + subClass.getContainingFile().getVirtualFile().getPresentableUrl() +")"; } LOG.info("Subtypes of " + psiClass + "(" + psiClass.getQualifiedName() + "): " + Arrays.toString(subtypes)); diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java index 7a29b2432ee2..63f2b7604161 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java @@ -17,14 +17,23 @@ package com.intellij.psi.stubsHierarchy.impl.test; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.stubsHierarchy.impl.*; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.PsiClass; +import com.intellij.psi.stubsHierarchy.impl.HierarchyService; +import com.intellij.psi.stubsHierarchy.impl.SingleClassHierarchy; +import com.intellij.psi.stubsHierarchy.impl.SmartClassAnchor; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; // Building hierarchy only for source files public class TestStubHierarchyAction extends InheritanceAction { @@ -33,66 +42,100 @@ public class TestStubHierarchyAction extends InheritanceAction { public void actionPerformed(AnActionEvent e) { final Project project = e.getData(CommonDataKeys.PROJECT); if (project != null) { - ProgressManager progress = ProgressManager.getInstance(); - progress.runProcessWithProgressSynchronously(new TestHierarchy(project), "Testing Hierarchy", true, project); + ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ReadAction.run(new TestHierarchy(project)::run), + "Testing Hierarchy", true, project); } } private static class TestHierarchy implements Runnable { - private final SingleClassHierarchy symbols; - Project myProject; + private final Project myProject; TestHierarchy(Project project) { myProject = project; - symbols = HierarchyService.instance(myProject).getSingleClassHierarchy(); } @Override public void run() { LOG.info("TestStubHierarchyAction started"); - final ProgressManager progressManager = ProgressManager.getInstance(); - final ProgressIndicator indicator = progressManager.getProgressIndicator(); + ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + HierarchyService service = HierarchyService.instance(myProject); + service.clearHierarchy(); + + indicator.setText("Building hierarchy"); + SingleClassHierarchy hierarchy = service.getSingleClassHierarchy(); + MultiMap supers = calcSupersMap(hierarchy); + + indicator.setText("Checking"); + compareSupers(indicator, supers, hierarchy); - indicator.setText("Getting keys"); - final SmartClassAnchor[] classes = symbols.myClassAnchors; - int size = symbols.myClassAnchors.length; - for (int i = 0; i < size; i++) { - final int finalI = i; - ApplicationManager.getApplication().runReadAction(() -> test(classes[finalI])); - indicator.setFraction(i * 1.0 / (double)size); - } LOG.info("TestStubHierarchyAction finished"); } - private void test(SmartClassAnchor aClass) { - PsiClass psiClass = ClassAnchorUtil.retrieve(myProject, aClass); - if (psiClass == null) { - LOG.info("error testing: could not retrieve file for anchor: " + aClass); - return; - } - - test(aClass, psiClass, psiClass.getSuperClass()); - for (PsiClass inter : psiClass.getInterfaces()) { - test(aClass, psiClass, inter); + private void compareSupers(ProgressIndicator indicator, MultiMap supers, SingleClassHierarchy hierarchy) { + SmartClassAnchor[] anchors = hierarchy.myClassAnchors; + for (int i = 0; i < anchors.length; i++) { + indicator.setFraction(i * 1.0 / anchors.length); + compareSupers(anchors[i], supers.get(anchors[i])); } } - private void test(SmartClassAnchor anchor, PsiClass psiClass, PsiClass fromPsi) { - if (fromPsi == null) { + private void compareSupers(final SmartClassAnchor anchor, final Collection superAnchors) { + PsiClass subClass = checkRetrieveClass(anchor); + List stubSuperList = ContainerUtil.map(superAnchors, this::checkRetrieveClass); + if (subClass == null || stubSuperList.contains(null)) { return; } - String psiName = fromPsi.getQualifiedName(); - if ("java.lang.Object".equals(psiName) || "groovy.lang.GroovyObject".equals(psiName) || "groovy.lang.GroovyObjectSupport".equals(psiName)) { - return; - } - // TODO - test using subtypes - LOG.info("error testing " + classInfo(psiClass) + ": missing " + classInfo(fromPsi)); + Set psiSupers = new HashSet<>(ContainerUtil.filter(getPsiSupers(subClass), psiClass -> !isImplicit(psiClass.getQualifiedName()))); + Set stubSupers = new HashSet<>(ContainerUtil.filter(stubSuperList, psiClass -> !isImplicit(psiClass.getQualifiedName()))); + + if (!stubSupers.containsAll(psiSupers)) { + psiSupers.removeAll(stubSupers); + LOG.info("Inconsistent hierarchy for " + classInfo(subClass) + + "\n missing " + psiSupers.size() + ": " + StringUtil.join(psiSupers, TestHierarchy::classInfo, ", ") + ); + } + } + + @NotNull + private static List getPsiSupers(PsiClass subClass) { + List psiSupers = new ArrayList<>(); + PsiClass superClass = subClass.getSuperClass(); + ContainerUtil.addIfNotNull(psiSupers, superClass); + Collections.addAll(psiSupers, subClass.getInterfaces()); + return psiSupers; + } + + private static boolean isImplicit(@Nullable String qname) { + return CommonClassNames.JAVA_LANG_OBJECT.equals(qname) || + "groovy.lang.GroovyObject".equals(qname) || "groovy.lang.GroovyObjectSupport".equals(qname); + } + + @Nullable + private PsiClass checkRetrieveClass(SmartClassAnchor anchor) { + PsiClass psiClass = anchor.retrieveClass(myProject); + if (psiClass == null) { + LOG.info("Cannot restore " + anchor); + } + return psiClass; + } + + @NotNull + MultiMap calcSupersMap(SingleClassHierarchy hierarchy) { + MultiMap supers = MultiMap.create(); + for (SmartClassAnchor aClass : hierarchy.myClassAnchors) { + for (SmartClassAnchor subtype : hierarchy.getDirectSubtypes(aClass)) { + supers.putValue(subtype, aClass); + } + } + return supers; } @NotNull private static String classInfo(PsiClass psiClass) { - return psiClass + "[" + psiClass.getQualifiedName() + "]" + " (" + psiClass.getContainingFile().getVirtualFile().getPresentableUrl() + ")"; + String name = psiClass.getQualifiedName(); + if (name == null) name = psiClass.toString(); + return name + " (" + psiClass.getContainingFile().getVirtualFile().getPresentableUrl() + ")"; } }