make TestStubHierarchyAction actually check something + minor renames/moves

This commit is contained in:
peter
2016-06-09 13:48:10 +02:00
parent e63f5dad49
commit 6d79ed7498
5 changed files with 145 additions and 126 deletions
@@ -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<PsiClass>() {
@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<StubElement<?>> 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();
}
}
@@ -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++;
}
@@ -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<StubElement<?>> 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();
@@ -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));
@@ -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<SmartClassAnchor, SmartClassAnchor> 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<SmartClassAnchor, SmartClassAnchor> 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<SmartClassAnchor> superAnchors) {
PsiClass subClass = checkRetrieveClass(anchor);
List<PsiClass> 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<PsiClass> psiSupers = new HashSet<>(ContainerUtil.filter(getPsiSupers(subClass), psiClass -> !isImplicit(psiClass.getQualifiedName())));
Set<PsiClass> 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<PsiClass> getPsiSupers(PsiClass subClass) {
List<PsiClass> 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<SmartClassAnchor, SmartClassAnchor> calcSupersMap(SingleClassHierarchy hierarchy) {
MultiMap<SmartClassAnchor, SmartClassAnchor> 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() + ")";
}
}