mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
make TestStubHierarchyAction actually check something + minor renames/moves
This commit is contained in:
@@ -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));
|
||||
|
||||
+80
-37
@@ -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() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user