package com.jetbrains.dart.analysisServer; import com.intellij.ide.hierarchy.HierarchyBrowserBaseEx; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestBase; import com.jetbrains.lang.dart.ide.hierarchy.call.DartCallHierarchyTreeStructure; import com.jetbrains.lang.dart.ide.hierarchy.call.DartCalleeTreeStructure; import com.jetbrains.lang.dart.ide.hierarchy.call.DartCallerTreeStructure; import com.jetbrains.lang.dart.ide.index.DartClassIndex; import com.jetbrains.lang.dart.ide.index.DartSymbolIndex; import com.jetbrains.lang.dart.psi.*; import com.jetbrains.lang.dart.util.DartTestUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static com.jetbrains.lang.dart.DartTokenTypes.CALL_EXPRESSION; public class DartCallHierarchyTest extends HierarchyViewTestBase { @Override public void setUp() throws Exception { super.setUp(); DartTestUtils.configureDartSdk(myModule, getTestRootDisposable(), true); // Some tests do this here but resolution does not work if the server is initialized prior to copying the files. //DartAnalysisServerService.getInstance().serverReadyForRequest(getProject()); } @Override protected String getBasePath() { return "analysisServer/callHierarchy/" + getTestName(false); } @Override protected String getTestDataPath() { return DartTestUtils.BASE_TEST_DATA_PATH; } @Override protected VirtualFile configureByFiles(@Nullable final File rawProjectRoot, @NotNull final VirtualFile... vFiles) throws IOException { VirtualFile root = super.configureByFiles(rawProjectRoot, vFiles); return DartTestUtils.configureNavigation(this, root, vFiles); } private void doCallHierarchyTest(final String className, final String methodName, final boolean caller, final String... fileNames) throws Exception { doHierarchyTest(() -> { final Project project = getProject(); if (className != null) { final List<DartComponentName> dartComponentNames = DartClassIndex.getItemsByName(className, project, GlobalSearchScope.projectScope(project)); for (DartComponentName name : dartComponentNames) { DartClass dartClass = PsiTreeUtil.getParentOfType(name, DartClass.class); if (dartClass != null && className.equals(dartClass.getName())) { PsiElement member = dartClass.findMemberByName(methodName); if (member == null) { member = findReference(dartClass, methodName); } if (member == null) { fail("Method not found"); } if (caller) { return new DartCallerTreeStructure(project, member, HierarchyBrowserBaseEx.SCOPE_PROJECT); } else { return new DartCalleeTreeStructure(project, member, HierarchyBrowserBaseEx.SCOPE_PROJECT); } } } } else { final List<DartComponentName> dartComponentNames = DartSymbolIndex.getItemsByName(methodName, project, GlobalSearchScope.projectScope(project)); for (DartComponentName name : dartComponentNames) { PsiElement parent = PsiTreeUtil.getParentOfType(name, DartFunctionDeclarationWithBodyOrNative.class); if (parent != null) { if (caller) { return new DartCallerTreeStructure(project, parent, HierarchyBrowserBaseEx.SCOPE_PROJECT); } else { return new DartCalleeTreeStructure(project, parent, HierarchyBrowserBaseEx.SCOPE_PROJECT); } } else { fail("Function not found"); } } } return null; }, fileNames); } public static PsiElement findReference(PsiElement dartClass, String referenceName) { // Find any reference to the named function or method in the test code. PsiElement[] result = new PsiElement[1]; try { dartClass.acceptChildren(new DartRecursiveVisitor() { @Override public void visitReferenceExpression(@NotNull DartReferenceExpression reference) { if (referenceName.equals(reference.getText())) { if (reference.getNextSibling() == null) { PsiElement parent = reference.getParent(); if (parent != null) { IElementType type = parent.getNode().getElementType(); if (type == CALL_EXPRESSION) { result[0] = reference; throw new ExitVisitor(); } parent = parent.getParent(); if (parent != null) { type = parent.getNode().getElementType(); if (type == CALL_EXPRESSION) { List<PsiElement> results = new ArrayList<>(); DartCallHierarchyTreeStructure.collectDeclarations(reference.resolve(), results); if (!results.isEmpty()) { result[0] = results.get(0); throw new ExitVisitor(); } } } } } } super.visitReferenceExpression(reference); } }); } catch (ExitVisitor ex) { return result[0]; // not null } return null; } public void testMethodCallers() throws Exception { doCallHierarchyTest("B", "b", true, "B.dart", "A.dart"); } public void testMethodCallees() throws Exception { doCallHierarchyTest("B", "b", false, "B.dart", "C.dart"); } public void testMethodRefCallers() throws Exception { doCallHierarchyTest("B", "c", true, "B.dart", "A.dart", "C.dart"); } public void testConstructorCallers() throws Exception { doCallHierarchyTest("B", "b", true, "B.dart", "A.dart"); } public void testConstructorCallees() throws Exception { doCallHierarchyTest("B", "b", false, "B.dart", "A.dart", "C.dart"); } public void testFunctionCallers() throws Exception { doCallHierarchyTest(null, "b", true, "B.dart", "A.dart"); } public void testFunctionCallees() throws Exception { doCallHierarchyTest(null, "a", false, "A.dart", "C.dart", "B.dart"); } public void testFunctionRefCallers() throws Exception { doCallHierarchyTest(null, "c", true, "B.dart", "A.dart", "C.dart"); } public void testMultiCallers() throws Exception { doCallHierarchyTest("A", "c", true, "A.dart", "B.dart", "C.dart"); } public void testMultiCallees() throws Exception { doCallHierarchyTest("A", "a", false, "A.dart", "B.dart", "C.dart"); } public void testLocalFnInMethod() throws Exception { doCallHierarchyTest(null, "baz", true, "C.dart"); } public void testLocalFnInFunction() throws Exception { doCallHierarchyTest(null, "baz", true, "C.dart"); } public void testNamedConstructorCallers() throws Exception { doCallHierarchyTest("X", "z7", true, "A.dart"); } public void testGetterSetterCallers() throws Exception { doCallHierarchyTest("X", "it", true, "A.dart"); } public void testFactoryConstructorCallers() throws Exception { doCallHierarchyTest("X", "zz", true, "A.dart"); } private static class ExitVisitor extends Error { } }