From 10941ae732db81dfa5c60b7b2c942a9428ca05fc Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 15 Jun 2017 17:22:18 +0300 Subject: [PATCH] Call Hierarchy: Refactoring Do not rely on light methods to search usages as some elements don't have them (e.g. local functions) #KT-9288 Fixed #KT-14428 Fixed --- .../kotlin/idea/findUsages/findUsageUtils.kt | 15 + .../kotlin/idea/hierarchy/HierarchyUtils.kt | 17 -- .../calls/CalleeReferenceProcessor.java | 91 ++++++ .../calls/CalleeReferenceVisitorBase.java | 2 +- .../calls/KotlinCallHierarchyBrowser.java | 18 +- .../KotlinCallHierarchyNodeDescriptor.java | 59 ++-- .../calls/KotlinCallHierarchyProvider.java | 75 ----- .../calls/KotlinCallHierarchyProvider.kt | 40 +++ .../calls/KotlinCallReferenceProcessor.kt | 15 +- .../calls/KotlinCallTreeStructure.java | 176 ------------ .../KotlinCalleeMethodsTreeStructure.java | 140 ---------- .../calls/KotlinCalleeTreeStructure.kt | 84 ++++++ .../KotlinCallerMethodsTreeStructure.java | 259 ------------------ .../calls/KotlinCallerTreeStructure.kt | 147 ++++++++++ .../hierarchy/calls/callHierarchyUtils.kt | 86 ++++++ .../overrides/KotlinOverrideTreeStructure.kt | 4 +- .../changeSignature/ui/KotlinCallerChooser.kt | 7 +- .../idea/refactoring/dataContextUtils.kt | 6 +- .../CallInsideAnonymousFun_verification.xml | 5 + .../callers/callInsideAnonymousFun/main0.kt | 11 + .../CallInsideLambda_verification.xml | 5 + .../calls/callers/callInsideLambda/main0.kt | 11 + .../idea/hierarchy/AbstractHierarchyTest.java | 24 +- .../hierarchy/HierarchyTestGenerated.java | 12 + 24 files changed, 566 insertions(+), 743 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceProcessor.java delete mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.java create mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.kt delete mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallTreeStructure.java delete mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeMethodsTreeStructure.java create mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeTreeStructure.kt delete mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java create mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerTreeStructure.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/callHierarchyUtils.kt create mode 100644 idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/CallInsideAnonymousFun_verification.xml create mode 100644 idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/main0.kt create mode 100644 idea/testData/hierarchy/calls/callers/callInsideLambda/CallInsideLambda_verification.xml create mode 100644 idea/testData/hierarchy/calls/callers/callInsideLambda/main0.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt index 8901f492915..a2f7e5018fb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt @@ -36,3 +36,18 @@ fun KtDeclaration.processAllExactUsages( options() ) } + +fun KtDeclaration.processAllUsages( + options: FindUsagesOptions, + processor: (UsageInfo) -> Unit +) { + val findUsagesHandler = KotlinFindUsagesHandlerFactory(project).createFindUsagesHandler(this, false, false) + findUsagesHandler.processElementUsages( + this, + { + processor(it) + true + }, + options + ) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/HierarchyUtils.kt b/idea/src/org/jetbrains/kotlin/idea/hierarchy/HierarchyUtils.kt index 238d1522044..216fd929658 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/HierarchyUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/HierarchyUtils.kt @@ -20,24 +20,9 @@ import com.intellij.codeInsight.TargetElementUtil import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.project.Project -import com.intellij.psi.PsiClass import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod import org.jetbrains.kotlin.idea.util.ProjectRootsUtil -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf - -fun isCallHierarchyElement(e: PsiElement): Boolean { - return e is PsiMethod || - e is PsiClass || - e is KtFile || - e is KtNamedFunction || - e is KtSecondaryConstructor || - e is KtObjectDeclaration || - e is KtClass && !e.isInterface() || - e is KtProperty -} fun getCurrentElement(dataContext: DataContext, project: Project): PsiElement? { val editor = CommonDataKeys.EDITOR.getData(dataContext) @@ -49,5 +34,3 @@ fun getCurrentElement(dataContext: DataContext, project: Project): PsiElement? { return CommonDataKeys.PSI_ELEMENT.getData(dataContext) } - -fun getCallHierarchyElement(element: PsiElement) = element.parentsWithSelf.firstOrNull(::isCallHierarchyElement) \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceProcessor.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceProcessor.java new file mode 100644 index 00000000000..e2eb65dcae3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceProcessor.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010-2017 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 org.jetbrains.kotlin.idea.hierarchy.calls; + +import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.psi.*; +import com.intellij.psi.impl.light.LightMemberReference; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.idea.references.KtReference; +import org.jetbrains.kotlin.psi.KtElement; +import org.jetbrains.kotlin.psi.KtImportDirective; +import org.jetbrains.kotlin.psi.KtNamedDeclaration; +import org.jetbrains.kotlin.psi.KtProperty; + +public abstract class CalleeReferenceProcessor extends ReadActionProcessor { + private final boolean kotlinOnly; + + public CalleeReferenceProcessor(boolean only) { + kotlinOnly = only; + } + + @Override + public boolean processInReadAction(PsiReference ref) { + // copied from Java + if (!(ref instanceof PsiReferenceExpression || ref instanceof KtReference)) { + if (!(ref instanceof PsiElement)) { + return true; + } + + PsiElement parent = ((PsiElement) ref).getParent(); + if (parent instanceof PsiNewExpression) { + if (((PsiNewExpression) parent).getClassReference() != ref) { + return true; + } + } + else if (parent instanceof PsiAnonymousClass) { + if (((PsiAnonymousClass) parent).getBaseClassReference() != ref) { + return true; + } + } + else if (ref instanceof LightMemberReference) { + PsiElement refTarget = ref.resolve(); + // Accept implicit superclass constructor reference in Java code + if (!(refTarget instanceof PsiMethod && ((PsiMethod) refTarget).isConstructor())) return true; + } + else { + return true; + } + } + + PsiElement refElement = ref.getElement(); + if (PsiTreeUtil.getParentOfType(refElement, KtImportDirective.class, true) != null) return true; + + PsiElement element = (refElement instanceof KtElement) + ? CallHierarchyUtilsKt.getCallHierarchyElement(refElement) + : PsiTreeUtil.getParentOfType(refElement, PsiMethod.class, false); + + if (kotlinOnly && !(element instanceof KtNamedDeclaration)) return true; + + // If reference belongs to property initializer, show enclosing declaration instead + if (element instanceof KtProperty) { + KtProperty property = (KtProperty) element; + if (PsiTreeUtil.isAncestor(property.getInitializer(), refElement, false)) { + element = CallHierarchyUtilsKt.getCallHierarchyElement(element.getParent()); + } + } + + if (element != null) { + onAccept(ref, element); + } + + return true; + } + + protected abstract void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element); +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceVisitorBase.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceVisitorBase.java index 8c84085cb84..f89f194b9e8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceVisitorBase.java +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/CalleeReferenceVisitorBase.java @@ -36,7 +36,7 @@ public abstract class CalleeReferenceVisitorBase extends KtTreeVisitorVoid { this.deepTraversal = deepTraversal; } - protected abstract void processDeclaration(KtSimpleNameExpression reference, PsiElement declaration); + protected abstract void processDeclaration(@NotNull KtSimpleNameExpression reference, @NotNull PsiElement declaration); @Override public void visitKtElement(@NotNull KtElement element) { diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyBrowser.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyBrowser.java index e50abed5857..de90f5c3ae0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyBrowser.java +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyBrowser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -25,20 +25,20 @@ import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.ui.PopupHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.hierarchy.HierarchyUtilsKt; +import org.jetbrains.kotlin.psi.KtElement; import javax.swing.*; import java.util.Comparator; import java.util.Map; public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase { - public KotlinCallHierarchyBrowser(@NotNull Project project, @NotNull PsiElement element) { - super(project, element); + public KotlinCallHierarchyBrowser(@NotNull PsiElement element) { + super(element.getProject(), element); } @Override @@ -63,7 +63,7 @@ public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase { private static PsiElement getTargetElement(@NotNull HierarchyNodeDescriptor descriptor) { if (descriptor instanceof KotlinCallHierarchyNodeDescriptor) { - return ((KotlinCallHierarchyNodeDescriptor) descriptor).getTargetElement(); + return descriptor.getPsiElement(); } return null; } @@ -76,17 +76,19 @@ public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase { @Override protected boolean isApplicableElement(@NotNull PsiElement element) { if (element instanceof PsiClass) return false; // PsiClass is not allowed at the hierarchy root - return HierarchyUtilsKt.isCallHierarchyElement(element); + return CallHierarchyUtilsKt.isCallHierarchyElement(element); } @Override protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) { + if (!(psiElement instanceof KtElement)) return null; + if (typeName.equals(CALLER_TYPE)) { - return new KotlinCallerMethodsTreeStructure(myProject, psiElement, getCurrentScopeType()); + return new KotlinCallerTreeStructure((KtElement) psiElement, getCurrentScopeType()); } if (typeName.equals(CALLEE_TYPE)) { - return new KotlinCalleeMethodsTreeStructure(myProject, psiElement, getCurrentScopeType()); + return new KotlinCalleeTreeStructure((KtElement) psiElement, getCurrentScopeType()); } return null; diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java index dcdf342a140..f0bf6121ed6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -19,24 +19,22 @@ package org.jetbrains.kotlin.idea.hierarchy.calls; import com.intellij.icons.AllIcons; import com.intellij.ide.IdeBundle; import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; -import com.intellij.ide.hierarchy.JavaHierarchyUtil; import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor; import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ui.util.CompositeAppearance; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.Navigatable; -import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.LayeredIcon; -import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.descriptors.ClassDescriptor; +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor; +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; +import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.renderer.DescriptorRenderer; @@ -49,34 +47,29 @@ import java.util.HashSet; import java.util.Set; public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor implements Navigatable { - private int usageCount = 0; - private final Set references = new HashSet(); + private int usageCount = 1; + private final Set references = new HashSet<>(); private final CallHierarchyNodeDescriptor javaDelegate; - public KotlinCallHierarchyNodeDescriptor(@NotNull Project project, - HierarchyNodeDescriptor parentDescriptor, - @NotNull PsiElement element, + public KotlinCallHierarchyNodeDescriptor( + @Nullable HierarchyNodeDescriptor parentDescriptor, + @NotNull KtElement element, boolean isBase, boolean navigateToReference) { - super(project, parentDescriptor, element, isBase); + super(element.getProject(), parentDescriptor, element, isBase); this.javaDelegate = new CallHierarchyNodeDescriptor(myProject, null, element, isBase, navigateToReference); } - public final CallHierarchyNodeDescriptor getJavaDelegate() { - return javaDelegate; + public final void incrementUsageCount() { + usageCount++; + javaDelegate.incrementUsageCount(); } public final void addReference(PsiReference reference) { - if (references.add(reference)) { - usageCount++; - } + references.add(reference); javaDelegate.addReference(reference); } - public final PsiElement getTargetElement(){ - return getPsiElement(); - } - @Override public final boolean isValid(){ //noinspection ConstantConditions @@ -96,7 +89,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i boolean changes = super.update(); - PsiElement targetElement = getTargetElement(); + PsiElement targetElement = getPsiElement(); String elementText = renderElement(targetElement); if (elementText == null) { @@ -122,16 +115,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i mainTextAttributes = new TextAttributes(myColor, null, null, null, Font.PLAIN); } - String packageName = null; - if (targetElement instanceof KtElement) { - packageName = KtPsiUtil.getPackageName((KtElement) targetElement); - } - else { - PsiClass enclosingClass = PsiTreeUtil.getParentOfType(targetElement, PsiClass.class, false); - if (enclosingClass != null) { - packageName = JavaHierarchyUtil.getPackageName(enclosingClass); - } - } + String packageName = KtPsiUtil.getPackageName((KtElement) targetElement); myHighlightedText.getEnding().addText(elementText, mainTextAttributes); @@ -188,7 +172,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i } } } - else if (element instanceof KtNamedFunction || element instanceof KtSecondaryConstructor) { + else if (element instanceof KtNamedFunction || element instanceof KtConstructor) { elementText = renderNamedFunction((FunctionDescriptor) descriptor); } else if (element instanceof KtProperty) { @@ -219,12 +203,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i String name = descriptorForName.getName().asString(); String paramTypes = StringUtil.join( descriptor.getValueParameters(), - new Function() { - @Override - public String fun(ValueParameterDescriptor descriptor) { - return DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType()); - } - }, + descriptor1 -> DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor1.getType()), ", " ); return name + "(" + paramTypes + ")"; diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.java deleted file mode 100644 index dcebcaa366a..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.hierarchy.calls; - -import com.intellij.codeInsight.TargetElementUtilBase; -import com.intellij.ide.hierarchy.CallHierarchyBrowserBase; -import com.intellij.ide.hierarchy.HierarchyBrowser; -import com.intellij.ide.hierarchy.HierarchyProvider; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.idea.KotlinLanguage; -import org.jetbrains.kotlin.idea.hierarchy.HierarchyUtilsKt; -import org.jetbrains.kotlin.idea.util.ProjectRootsUtil; -import org.jetbrains.kotlin.psi.KtFile; - -public class KotlinCallHierarchyProvider implements HierarchyProvider { - @Override - public PsiElement getTarget(@NotNull DataContext dataContext) { - Project project = CommonDataKeys.PROJECT.getData(dataContext); - if (project == null) return null; - - PsiElement element = getCurrentElement(dataContext, project); - if (element == null) return null; - - element = HierarchyUtilsKt.getCallHierarchyElement(element); - if (element instanceof KtFile || element == null || element.getLanguage() != KotlinLanguage.INSTANCE) return null; - - return element; - } - - private static PsiElement getCurrentElement(DataContext dataContext, Project project) { - Editor editor = CommonDataKeys.EDITOR.getData(dataContext); - if (editor != null) { - PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); - if (file == null) return null; - - if (!ProjectRootsUtil.isInProjectOrLibSource(file)) return null; - - return TargetElementUtilBase.findTargetElement(editor, TargetElementUtilBase.getInstance().getAllAccepted()); - } - - return CommonDataKeys.PSI_ELEMENT.getData(dataContext); - } - - @NotNull - @Override - public HierarchyBrowser createHierarchyBrowser(PsiElement target) { - return new KotlinCallHierarchyBrowser(target.getProject(), target); - } - - @Override - public void browserActivated(@NotNull HierarchyBrowser hierarchyBrowser) { - ((KotlinCallHierarchyBrowser) hierarchyBrowser).changeView(CallHierarchyBrowserBase.CALLER_TYPE); - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.kt b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.kt new file mode 100644 index 00000000000..4b589b669a5 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyProvider.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2017 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 org.jetbrains.kotlin.idea.hierarchy.calls + +import com.intellij.ide.hierarchy.CallHierarchyBrowserBase +import com.intellij.ide.hierarchy.HierarchyBrowser +import com.intellij.ide.hierarchy.HierarchyProvider +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.refactoring.psiElement +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile + +class KotlinCallHierarchyProvider : HierarchyProvider { + override fun getTarget(dataContext: DataContext): KtElement? { + val element = dataContext.psiElement?.let { getCallHierarchyElement(it) } + if (element is KtFile) return null + return element + } + + override fun createHierarchyBrowser(target: PsiElement) = KotlinCallHierarchyBrowser(target) + + override fun browserActivated(hierarchyBrowser: HierarchyBrowser) { + (hierarchyBrowser as KotlinCallHierarchyBrowser).changeView(CallHierarchyBrowserBase.CALLER_TYPE) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallReferenceProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallReferenceProcessor.kt index 3e8fe61b086..0a09fcd17ab 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallReferenceProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallReferenceProcessor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -19,20 +19,15 @@ package org.jetbrains.kotlin.idea.hierarchy.calls import com.intellij.ide.hierarchy.HierarchyNodeDescriptor import com.intellij.ide.hierarchy.call.CallReferenceProcessor import com.intellij.ide.hierarchy.call.JavaCallHierarchyData +import com.intellij.ide.util.treeView.NodeDescriptor import com.intellij.psi.PsiElement import com.intellij.psi.PsiReference -import org.jetbrains.kotlin.idea.references.KtReference class KotlinCallReferenceProcessor : CallReferenceProcessor { override fun process(reference: PsiReference, data: JavaCallHierarchyData): Boolean { - if (reference !is KtReference) return true - val nodeDescriptor = data.nodeDescriptor as? HierarchyNodeDescriptor ?: return true + val nodeDescriptor = data.nodeDescriptor as? HierarchyNodeDescriptor ?: return false @Suppress("UNCHECKED_CAST") - return !KotlinCallerMethodsTreeStructure.defaultQueryProcessor( - if (nodeDescriptor is KotlinCallHierarchyNodeDescriptor) nodeDescriptor.javaDelegate else nodeDescriptor, - data.resultMap as MutableMap, - false, - true - ).process(reference) + KotlinCallerTreeStructure.processReference(reference, reference.element, nodeDescriptor, data.resultMap as MutableMap>, true) + return true } } diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallTreeStructure.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallTreeStructure.java deleted file mode 100644 index ead24c204fc..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallTreeStructure.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.hierarchy.calls; - -import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; -import com.intellij.ide.hierarchy.HierarchyTreeStructure; -import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiReference; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.HashMap; -import kotlin.collections.CollectionsKt; -import kotlin.jvm.functions.Function1; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.asJava.LightClassUtil; -import org.jetbrains.kotlin.asJava.LightClassUtilsKt; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; - -import java.util.Map; - -import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass; - -public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure { - protected final String scopeType; - - public KotlinCallTreeStructure(@NotNull Project project, PsiElement element, String scopeType) { - super(project, createNodeDescriptor(project, element, null, false, false)); - this.scopeType = scopeType; - } - - protected static KtElement getEnclosingElementForLocalDeclaration(PsiElement element) { - return element instanceof KtNamedDeclaration - ? KtPsiUtil.getEnclosingElementForLocalDeclaration((KtNamedDeclaration) element) - : null; - } - - @Nullable - private static HierarchyNodeDescriptor createNodeDescriptor( - Project project, PsiElement element, HierarchyNodeDescriptor parent, boolean navigateToReference, boolean wrapAsLightElements - ) { - PsiElement nodeElement = element; - if (wrapAsLightElements && element instanceof KtElement) { - nodeElement = CollectionsKt.firstOrNull(LightClassUtilsKt.toLightElements((KtElement) element)); - } - if (nodeElement == null) return null; - boolean root = (parent == null); - return nodeElement instanceof KtElement - ? new KotlinCallHierarchyNodeDescriptor(project, parent, nodeElement, root, navigateToReference) - : new CallHierarchyNodeDescriptor(project, parent, nodeElement, root, navigateToReference); - } - - protected static PsiElement getTargetElement(HierarchyNodeDescriptor descriptor) { - return descriptor instanceof CallHierarchyNodeDescriptor - ? ((CallHierarchyNodeDescriptor) descriptor).getEnclosingElement() - : ((KotlinCallHierarchyNodeDescriptor)descriptor).getTargetElement(); - } - - private static final Function1 IS_NON_LOCAL_DECLARATION = new Function1() { - @Override - public Boolean invoke(@javax.annotation.Nullable PsiElement input) { - return input instanceof PsiMethod - || ((input instanceof KtNamedFunction || input instanceof KtClassOrObject || input instanceof KtProperty) - && !KtPsiUtil.isLocal((KtNamedDeclaration) input)); - } - }; - - @Nullable - protected static PsiMethod getRepresentativePsiMethod(PsiElement element) { - while (true) { - element = PsiUtilsKt.getParentOfTypesAndPredicate(element, false, ArrayUtil.EMPTY_CLASS_ARRAY, IS_NON_LOCAL_DECLARATION); - if (element == null) return null; - - PsiMethod method = getRepresentativePsiMethodForNonLocalDeclaration(element); - if (method != null) return method; - - element = element.getParent(); - } - } - - private static PsiMethod getRepresentativePsiMethodForNonLocalDeclaration(PsiElement element) { - if (element instanceof PsiMethod) { - return (PsiMethod) element; - } - - if (element instanceof KtNamedFunction || element instanceof KtSecondaryConstructor) { - return LightClassUtil.INSTANCE.getLightClassMethod((KtFunction) element); - } - - if (element instanceof KtProperty) { - LightClassUtil.PropertyAccessorsPsiMethods propertyMethods = - LightClassUtil.INSTANCE.getLightClassPropertyMethods((KtProperty) element); - return (propertyMethods.getGetter() != null) ? propertyMethods.getGetter() : propertyMethods.getSetter(); - } - - if (element instanceof KtClassOrObject) { - PsiClass psiClass = toLightClass((KtClassOrObject) element); - if (psiClass == null) return null; - - PsiMethod[] constructors = psiClass.getConstructors(); - if (constructors.length > 0) return constructors[0]; - } - - return null; - } - - protected static CallHierarchyNodeDescriptor getJavaNodeDescriptor(HierarchyNodeDescriptor originalDescriptor) { - if (originalDescriptor instanceof CallHierarchyNodeDescriptor) return (CallHierarchyNodeDescriptor) originalDescriptor; - - assert originalDescriptor instanceof KotlinCallHierarchyNodeDescriptor; - return ((KotlinCallHierarchyNodeDescriptor) originalDescriptor).getJavaDelegate(); - } - - protected Object[] collectNodeDescriptors( - HierarchyNodeDescriptor descriptor, Map referencesToCalleeElements, PsiClass basePsiClass - ) { - HashMap declarationToDescriptorMap = new HashMap(); - for (Map.Entry refToCallee : referencesToCalleeElements.entrySet()) { - PsiReference ref = refToCallee.getKey(); - PsiElement callee = refToCallee.getValue(); - - if (basePsiClass != null && !isInScope(basePsiClass, callee, scopeType)) continue; - - addNodeDescriptorForElement(ref, callee, declarationToDescriptorMap, descriptor, false); - } - return declarationToDescriptorMap.values().toArray(new Object[declarationToDescriptorMap.size()]); - } - - protected static void addNodeDescriptorForElement( - PsiReference reference, - PsiElement element, - Map declarationToDescriptorMap, - HierarchyNodeDescriptor descriptor, - boolean wrapAsLightElements - ) { - HierarchyNodeDescriptor d = declarationToDescriptorMap.get(element); - if (d == null) { - d = createNodeDescriptor(element.getProject(), element, descriptor, true, wrapAsLightElements); - if (d == null) return; - declarationToDescriptorMap.put(element, d); - } - else if (d instanceof CallHierarchyNodeDescriptor) { - ((CallHierarchyNodeDescriptor) d).incrementUsageCount(); - } - - if (d instanceof CallHierarchyNodeDescriptor) { - ((CallHierarchyNodeDescriptor) d).addReference(reference); - } - else if (d instanceof KotlinCallHierarchyNodeDescriptor) { - ((KotlinCallHierarchyNodeDescriptor) d).addReference(reference); - } - } - - @Override - public boolean isAlwaysShowPlus() { - return true; - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeMethodsTreeStructure.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeMethodsTreeStructure.java deleted file mode 100644 index 305c0e60839..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeMethodsTreeStructure.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2010-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 org.jetbrains.kotlin.idea.hierarchy.calls; - -import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; -import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor; -import com.intellij.ide.hierarchy.call.CalleeMethodsTreeStructure; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiReference; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.asJava.elements.KtLightMethod; -import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; -import org.jetbrains.kotlin.idea.references.ReferenceUtilKt; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure { - private final CalleeMethodsTreeStructure javaTreeStructure; - private final PsiClass representativePsiClass; - - public KotlinCalleeMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) { - super(project, element, scopeType); - - PsiMethod representativePsiMethod = getRepresentativePsiMethod(element); - assert representativePsiMethod != null; - - this.representativePsiClass = representativePsiMethod.getContainingClass(); - this.javaTreeStructure = new CalleeMethodsTreeStructure(project, representativePsiMethod, scopeType); - } - - private static Map getReferencesToCalleeElements(@NotNull KtElement rootElement) { - List elementsToAnalyze = new ArrayList(); - if (rootElement instanceof KtNamedFunction) { - elementsToAnalyze.add(((KtNamedFunction) rootElement).getBodyExpression()); - } else if (rootElement instanceof KtProperty) { - for (KtPropertyAccessor accessor : ((KtProperty) rootElement).getAccessors()) { - KtExpression body = accessor.getBodyExpression(); - if (body != null) { - elementsToAnalyze.add(body); - } - } - } else { - KtClassOrObject classOrObject = (KtClassOrObject) rootElement; - for (KtSuperTypeListEntry specifier : classOrObject.getSuperTypeListEntries()) { - if (specifier instanceof KtCallElement) { - elementsToAnalyze.add(specifier); - } - } - - KtClassBody body = classOrObject.getBody(); - if (body != null) { - for (KtAnonymousInitializer initializer : body.getAnonymousInitializers()) { - KtExpression initializerBody = initializer.getBody(); - if (initializerBody != null) { - elementsToAnalyze.add(initializerBody); - } - } - for (KtProperty property : body.getProperties()) { - KtExpression initializer = property.getInitializer(); - if (initializer != null) { - elementsToAnalyze.add(initializer); - } - } - } - } - - final Map referencesToCalleeElements = new HashMap(); - for (KtElement element : elementsToAnalyze) { - element.accept( - new CalleeReferenceVisitorBase(ResolutionUtils.analyze(element, BodyResolveMode.FULL), false) { - @Override - protected void processDeclaration(KtSimpleNameExpression reference, PsiElement declaration) { - referencesToCalleeElements.put(ReferenceUtilKt.getMainReference(reference), declaration); - } - } - ); - } - - return referencesToCalleeElements; - } - - @NotNull - @Override - protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) { - PsiElement targetElement = getTargetElement(descriptor); - - // Kotlin class constructor invoked from Java code - if (targetElement instanceof PsiMethod) { - PsiMethod psiMethod = (PsiMethod) targetElement; - if (psiMethod.isConstructor()) { - PsiClass psiClass = psiMethod.getContainingClass(); - PsiElement navigationElement = psiClass != null ? psiClass.getNavigationElement() : null; - if (navigationElement instanceof KtClass) { - return buildChildrenByKotlinTarget(descriptor, (KtElement) navigationElement); - } - } - } - - // Kotlin function or property invoked from Java code - if (targetElement instanceof KtLightMethod) { - return buildChildrenByKotlinTarget(descriptor, ((KtLightMethod) targetElement).getKotlinOrigin()); - } - - if (targetElement instanceof KtElement) { - return buildChildrenByKotlinTarget(descriptor, (KtElement) targetElement); - } - - CallHierarchyNodeDescriptor javaDescriptor = descriptor instanceof CallHierarchyNodeDescriptor - ? (CallHierarchyNodeDescriptor) descriptor - : ((KotlinCallHierarchyNodeDescriptor)descriptor).getJavaDelegate(); - return javaTreeStructure.getChildElements(javaDescriptor); - } - - private Object[] buildChildrenByKotlinTarget(HierarchyNodeDescriptor descriptor, KtElement targetElement) { - Map referencesToCalleeElements = getReferencesToCalleeElements(targetElement); - return collectNodeDescriptors(descriptor, referencesToCalleeElements, representativePsiClass); - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeTreeStructure.kt b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeTreeStructure.kt new file mode 100644 index 00000000000..d153552c004 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCalleeTreeStructure.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2017 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 org.jetbrains.kotlin.idea.hierarchy.calls + +import com.intellij.ide.hierarchy.HierarchyNodeDescriptor +import com.intellij.ide.hierarchy.HierarchyTreeStructure +import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor +import com.intellij.ide.hierarchy.call.CalleeMethodsTreeStructure +import com.intellij.ide.util.treeView.NodeDescriptor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.util.ArrayUtil +import com.intellij.util.containers.HashMap +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest +import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject + +class KotlinCalleeTreeStructure( + element: KtElement, + private val scopeType: String +) : HierarchyTreeStructure(element.project, + KotlinCallHierarchyNodeDescriptor(null, element, true, false)) { + private fun KtElement.getCalleeSearchScope(): List { + return when (this) { + is KtNamedFunction, is KtFunctionLiteral, is KtPropertyAccessor -> listOf((this as KtDeclarationWithBody).bodyExpression) + is KtProperty -> accessors.map { it.bodyExpression } + is KtClassOrObject -> { + superTypeListEntries.filterIsInstance() + + getAnonymousInitializers().map { it.body } + + declarations.filterIsInstance().map { it.initializer } + } + else -> emptyList() + }.filterNotNull() + } + + override fun buildChildren(nodeDescriptor: HierarchyNodeDescriptor): Array { + if (nodeDescriptor is CallHierarchyNodeDescriptor) { + val psiMethod = nodeDescriptor.enclosingElement as? PsiMethod ?: return ArrayUtil.EMPTY_OBJECT_ARRAY + return CalleeMethodsTreeStructure(myProject, psiMethod, scopeType).getChildElements(nodeDescriptor) + } + + val element = nodeDescriptor.psiElement as? KtElement ?: return ArrayUtil.EMPTY_OBJECT_ARRAY + + val result = LinkedHashSet() + val baseClass = (element as? KtDeclaration)?.containingClassOrObject + val calleeToDescriptorMap = HashMap>() + + element.getCalleeSearchScope().forEach { + it.accept( + object : CalleeReferenceVisitorBase(it.analyze(), false) { + override fun processDeclaration(reference: KtSimpleNameExpression, declaration: PsiElement) { + if (!isInScope(baseClass, declaration, scopeType)) return + result += (getOrCreateNodeDescriptor(nodeDescriptor, declaration, null, false, calleeToDescriptorMap, false) ?: return) + } + } + ) + } + + for (it in HierarchySearchRequest(element, element.useScope).searchOverriders()) { + val overrider = it.unwrapped as? KtElement ?: continue + if (!isInScope(baseClass, overrider, scopeType)) continue + result += KotlinCallHierarchyNodeDescriptor(nodeDescriptor, overrider, false, false) + } + + return result.toArray() + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java deleted file mode 100644 index 51df812261b..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.hierarchy.calls; - -import com.google.common.collect.Maps; -import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; -import com.intellij.ide.hierarchy.call.CallerMethodsTreeStructure; -import com.intellij.openapi.application.ReadActionProcessor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.psi.*; -import com.intellij.psi.impl.light.LightMemberReference; -import com.intellij.psi.search.SearchScope; -import com.intellij.psi.search.searches.MethodReferencesSearch; -import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.FilteringProcessor; -import com.intellij.util.Processor; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.HashMap; -import gnu.trove.TObjectHashingStrategy; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.asJava.LightClassUtil; -import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; -import org.jetbrains.kotlin.idea.hierarchy.HierarchyUtilsKt; -import org.jetbrains.kotlin.idea.references.KtReference; -import org.jetbrains.kotlin.idea.references.ReferenceUtilKt; -import org.jetbrains.kotlin.idea.search.usagesSearch.UtilsKt; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; - -import java.util.*; - -public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure { - private final CallerMethodsTreeStructure javaTreeStructure; - private final PsiClass basePsiClass; - - public KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) { - super(project, element, scopeType); - - PsiMethod basePsiMethod = getRepresentativePsiMethod(element); - assert basePsiMethod != null : "Can't generate light method: " + element.getText(); - - basePsiClass = basePsiMethod.getContainingClass(); - javaTreeStructure = new CallerMethodsTreeStructure(project, basePsiMethod, scopeType); - } - - @NotNull - @Override - protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) { - final PsiElement element = getTargetElement(descriptor); - - KtElement codeBlockForLocalDeclaration = getEnclosingElementForLocalDeclaration(element); - if (codeBlockForLocalDeclaration != null) { - BindingContext bindingContext = ResolutionUtils.analyze((KtElement) element, BodyResolveMode.FULL); - - final Map referencesToElements = new HashMap(); - codeBlockForLocalDeclaration.accept(new CalleeReferenceVisitorBase(bindingContext, true) { - @Override - protected void processDeclaration(KtSimpleNameExpression reference, PsiElement declaration) { - if (!declaration.equals(element)) return; - - //noinspection unchecked - PsiElement container = PsiTreeUtil.getParentOfType( - reference, - KtNamedFunction.class, KtPropertyAccessor.class, KtClassOrObject.class - ); - if (container instanceof KtPropertyAccessor) { - container = PsiTreeUtil.getParentOfType(container, KtProperty.class); - } - - if (container != null) { - referencesToElements.put(ReferenceUtilKt.getMainReference(reference), container); - } - } - }); - return collectNodeDescriptors(descriptor, referencesToElements, null); - } - - SearchScope searchScope = getSearchScope(scopeType, basePsiClass); - Map methodToDescriptorMap = Maps.newHashMap(); - - Object[] javaCallers = null; - if (element instanceof PsiMethod) { - javaCallers = javaTreeStructure.getChildElements(getJavaNodeDescriptor(descriptor)); - processPsiMethodCallers( - Collections.singleton((PsiMethod) element), descriptor, methodToDescriptorMap, searchScope, true - ); - } - if (element instanceof KtNamedFunction || element instanceof KtSecondaryConstructor) { - Collection lightMethods = LightClassUtil.INSTANCE.getLightClassMethods((KtFunction) element); - processPsiMethodCallers(lightMethods, descriptor, methodToDescriptorMap, searchScope, false); - } - if (element instanceof KtProperty) { - LightClassUtil.PropertyAccessorsPsiMethods propertyMethods = - LightClassUtil.INSTANCE.getLightClassPropertyMethods((KtProperty) element); - processPsiMethodCallers(propertyMethods, descriptor, methodToDescriptorMap, searchScope, false); - } - if (element instanceof KtClassOrObject) { - KtPrimaryConstructor constructor = ((KtClassOrObject) element).getPrimaryConstructor(); - if (constructor != null) { - PsiMethod lightMethod = LightClassUtil.INSTANCE.getLightClassMethod(constructor); - processPsiMethodCallers(Collections.singleton(lightMethod), descriptor, methodToDescriptorMap, searchScope, false); - } - else { - processKtClassOrObjectCallers((KtClassOrObject) element, descriptor, methodToDescriptorMap, searchScope); - } - } - - Object[] callers = methodToDescriptorMap.values().toArray(new Object[methodToDescriptorMap.size()]); - return (javaCallers != null) ? ArrayUtil.mergeArrays(javaCallers, callers) : callers; - } - - private static void processPsiMethodCallers( - Iterable lightMethods, - HierarchyNodeDescriptor descriptor, - Map methodToDescriptorMap, - SearchScope searchScope, - boolean kotlinOnly - ) { - Set methodsToFind = new HashSet(); - for (PsiMethod lightMethod : lightMethods) { - if (lightMethod == null) continue; - - PsiMethod[] superMethods = lightMethod.findDeepestSuperMethods(); - methodsToFind.add(lightMethod); - ContainerUtil.addAll(methodsToFind, superMethods); - } - - if (methodsToFind.isEmpty()) return; - - Set references = ContainerUtil.newTroveSet( - new TObjectHashingStrategy() { - @Override - public int computeHashCode(PsiReference object) { - return object.getElement().hashCode(); - } - - @Override - public boolean equals(PsiReference o1, PsiReference o2) { - return o1.getElement().equals(o2.getElement()); - } - } - ); - for (PsiMethod superMethod: methodsToFind) { - ContainerUtil.addAll(references, MethodReferencesSearch.search(superMethod, searchScope, true)); - } - ContainerUtil.process(references, defaultQueryProcessor(descriptor, methodToDescriptorMap, kotlinOnly, false)); - } - - private static void processKtClassOrObjectCallers( - final KtClassOrObject classOrObject, - HierarchyNodeDescriptor descriptor, - Map methodToDescriptorMap, - SearchScope searchScope - ) { - Processor processor = new FilteringProcessor( - new Condition() { - @Override - public boolean value(PsiReference reference) { - return UtilsKt.isConstructorUsage(reference, classOrObject); - } - }, - defaultQueryProcessor(descriptor, methodToDescriptorMap, false, false) - ); - ReferencesSearch.search(classOrObject, searchScope, false).forEach(processor); - } - - static Processor defaultQueryProcessor( - final HierarchyNodeDescriptor descriptor, - final Map methodToDescriptorMap, - boolean kotlinOnly, - final boolean wrapAsLightElements - ) { - return new CalleeReferenceProcessor(kotlinOnly) { - @Override - protected void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element) { - addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor, wrapAsLightElements); - } - }; - } - - public static abstract class CalleeReferenceProcessor extends ReadActionProcessor { - private final boolean kotlinOnly; - - public CalleeReferenceProcessor(boolean only) { - kotlinOnly = only; - } - - @Override - public boolean processInReadAction(PsiReference ref) { - // copied from Java - if (!(ref instanceof PsiReferenceExpression || ref instanceof KtReference)) { - if (!(ref instanceof PsiElement)) { - return true; - } - - PsiElement parent = ((PsiElement) ref).getParent(); - if (parent instanceof PsiNewExpression) { - if (((PsiNewExpression) parent).getClassReference() != ref) { - return true; - } - } - else if (parent instanceof PsiAnonymousClass) { - if (((PsiAnonymousClass) parent).getBaseClassReference() != ref) { - return true; - } - } - else if (ref instanceof LightMemberReference) { - PsiElement refTarget = ref.resolve(); - // Accept implicit superclass constructor reference in Java code - if (!(refTarget instanceof PsiMethod && ((PsiMethod) refTarget).isConstructor())) return true; - } - else { - return true; - } - } - - PsiElement refElement = ref.getElement(); - if (PsiTreeUtil.getParentOfType(refElement, KtImportDirective.class, true) != null) return true; - - PsiElement element = HierarchyUtilsKt.getCallHierarchyElement(refElement); - - if (kotlinOnly && !(element instanceof KtNamedDeclaration)) return true; - - // If reference belongs to property initializer, show enclosing declaration instead - if (element instanceof KtProperty) { - KtProperty property = (KtProperty) element; - if (PsiTreeUtil.isAncestor(property.getInitializer(), refElement, false)) { - element = HierarchyUtilsKt.getCallHierarchyElement(element.getParent()); - } - } - - if (element != null) { - onAccept(ref, element); - } - - return true; - } - - protected abstract void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element); - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerTreeStructure.kt b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerTreeStructure.kt new file mode 100644 index 00000000000..90c60f4b184 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerTreeStructure.kt @@ -0,0 +1,147 @@ +/* + * Copyright 2010-2017 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 org.jetbrains.kotlin.idea.hierarchy.calls + +import com.google.common.collect.Maps +import com.intellij.find.findUsages.JavaFindUsagesOptions +import com.intellij.ide.hierarchy.HierarchyNodeDescriptor +import com.intellij.ide.hierarchy.HierarchyTreeStructure +import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor +import com.intellij.ide.hierarchy.call.CallerMethodsTreeStructure +import com.intellij.ide.util.treeView.NodeDescriptor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiReference +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.ArrayUtil +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations +import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions +import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions +import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions +import org.jetbrains.kotlin.idea.findUsages.processAllUsages +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType + +class KotlinCallerTreeStructure( + element: KtElement, + private val scopeType: String +) : HierarchyTreeStructure(element.project, + KotlinCallHierarchyNodeDescriptor(null, element, true, false)) { + companion object { + internal fun processReference( + reference: PsiReference?, + refElement: PsiElement, + nodeDescriptor: HierarchyNodeDescriptor, + callerToDescriptorMap: MutableMap>, + isJavaMap: Boolean + ) { + var callerElement: PsiElement? = when (refElement) { + is KtElement -> getCallHierarchyElement(refElement) + else -> { + val psiMember = refElement.getNonStrictParentOfType() + psiMember as? PsiMethod ?: psiMember?.containingClass + } + } + if (callerElement is KtProperty) { + if (PsiTreeUtil.isAncestor(callerElement.initializer, refElement, false)) { + callerElement = getCallHierarchyElement(callerElement.parent) + } + } + if (callerElement == null) return + + getOrCreateNodeDescriptor(nodeDescriptor, callerElement, reference, true, callerToDescriptorMap, isJavaMap) + } + } + + private fun buildChildren( + element: PsiElement, + nodeDescriptor: HierarchyNodeDescriptor, + callerToDescriptorMap: MutableMap> + ): Collection { + if (nodeDescriptor is CallHierarchyNodeDescriptor) { + val psiMethod = nodeDescriptor.enclosingElement as? PsiMethod ?: return emptyList() + return CallerMethodsTreeStructure(myProject, psiMethod, scopeType).getChildElements(nodeDescriptor).toList() + } + + if (element !is KtDeclaration) return emptyList() + + val baseClass = (element as? KtDeclaration)?.containingClassOrObject + val searchScope = getSearchScope(scopeType, baseClass) + + val findOptions: JavaFindUsagesOptions = when (element) { + is KtNamedFunction, is KtConstructor<*> -> KotlinFunctionFindUsagesOptions(myProject) + is KtProperty -> KotlinPropertyFindUsagesOptions(myProject) + is KtPropertyAccessor -> KotlinPropertyFindUsagesOptions(myProject).apply { + isReadAccess = element.isGetter + isWriteAccess = element.isSetter + } + is KtClass -> KotlinClassFindUsagesOptions(myProject).apply { + isUsages = false + } + else -> return emptyList() + } + findOptions.isSkipImportStatements = true + findOptions.searchScope = searchScope + findOptions.isSearchForTextOccurrences = false + + val elementToSearch = when (element) { + is KtPropertyAccessor -> element.property + else -> element + } + + // If reference belongs to property initializer, show enclosing declaration instead + elementToSearch.processAllUsages(findOptions) { + processReference(it.reference, it.element ?: return@processAllUsages, nodeDescriptor, callerToDescriptorMap, false) + } + + return callerToDescriptorMap.values + } + + override fun buildChildren(nodeDescriptor: HierarchyNodeDescriptor): Array { + val element = nodeDescriptor.psiElement as? KtDeclaration ?: return ArrayUtil.EMPTY_OBJECT_ARRAY + val callerToDescriptorMap = Maps.newHashMap>() + val descriptor = element.resolveToDescriptor() + if (descriptor is CallableMemberDescriptor) { + return descriptor.getDeepestSuperDeclarations().flatMap { rootDescriptor -> + val rootElement = DescriptorToSourceUtilsIde.getAnyDeclaration(myProject, rootDescriptor) + ?: return@flatMap emptyList() + val rootNodeDescriptor = when (rootElement) { + is KtElement -> nodeDescriptor + is PsiMethod -> CallHierarchyNodeDescriptor( + myProject, + nodeDescriptor.parentDescriptor as HierarchyNodeDescriptor?, + rootElement, + nodeDescriptor.parentDescriptor == null, + false + ) + else -> return@flatMap emptyList() + } + buildChildren(rootElement, rootNodeDescriptor, callerToDescriptorMap) + }.toTypedArray() + } + else { + return buildChildren(element, nodeDescriptor, callerToDescriptorMap).toTypedArray() + } + } + + override fun isAlwaysShowPlus() = true +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/callHierarchyUtils.kt b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/callHierarchyUtils.kt new file mode 100644 index 00000000000..6e262b75302 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/callHierarchyUtils.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2010-2017 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 org.jetbrains.kotlin.idea.hierarchy.calls + +import com.intellij.ide.hierarchy.HierarchyNodeDescriptor +import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor +import com.intellij.ide.util.treeView.NodeDescriptor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiReference +import org.jetbrains.kotlin.asJava.toLightElements +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf + +fun isCallHierarchyElement(e: PsiElement): Boolean { + return (e is KtNamedFunction && e.name != null) || + e is KtSecondaryConstructor || + (e is KtProperty && !e.isLocal) || + e is KtObjectDeclaration || + (e is KtClass && !e.isInterface()) || + e is KtFile +} + +fun getCallHierarchyElement(element: PsiElement) = element.parentsWithSelf.firstOrNull(::isCallHierarchyElement) as? KtElement + +private fun NodeDescriptor<*>.incrementUsageCount() { + when (this) { + is KotlinCallHierarchyNodeDescriptor -> incrementUsageCount() + is CallHierarchyNodeDescriptor -> incrementUsageCount() + } +} + +private fun NodeDescriptor<*>.addReference(reference: PsiReference) { + when (this) { + is KotlinCallHierarchyNodeDescriptor -> addReference(reference) + is CallHierarchyNodeDescriptor -> addReference(reference) + } +} + +internal fun getOrCreateNodeDescriptor( + parent: HierarchyNodeDescriptor, + originalElement: PsiElement, + reference: PsiReference?, + navigateToReference: Boolean, + elementToDescriptorMap: MutableMap>, + isJavaMap: Boolean +): HierarchyNodeDescriptor? { + val element = (if (isJavaMap && originalElement is KtElement) originalElement.toLightElements().firstOrNull() else originalElement) + ?: return null + + val existingDescriptor = elementToDescriptorMap[element] as? HierarchyNodeDescriptor + val result = if (existingDescriptor != null) { + existingDescriptor.incrementUsageCount() + existingDescriptor + } + else { + val newDescriptor: HierarchyNodeDescriptor = when (element) { + is KtElement -> KotlinCallHierarchyNodeDescriptor(parent, element, false, navigateToReference) + is PsiMember -> CallHierarchyNodeDescriptor(element.project, parent, element, false, navigateToReference) + else -> return null + } + elementToDescriptorMap[element] = newDescriptor + newDescriptor + } + + if (reference != null) { + result.addReference(reference) + } + + return result +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/overrides/KotlinOverrideTreeStructure.kt b/idea/src/org/jetbrains/kotlin/idea/hierarchy/overrides/KotlinOverrideTreeStructure.kt index 35ef1e486a5..41a8bf393db 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/overrides/KotlinOverrideTreeStructure.kt +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/overrides/KotlinOverrideTreeStructure.kt @@ -33,9 +33,9 @@ class KotlinOverrideTreeStructure(project: Project, val element: PsiElement) : H setBaseElement(javaTreeStructures.first().baseDescriptor!!) } - override fun buildChildren(descriptor: HierarchyNodeDescriptor): Array { + override fun buildChildren(nodeDescriptor: HierarchyNodeDescriptor): Array { fun buildChildrenByTreeStructure(javaTreeStructure: MethodHierarchyTreeStructure): Array { - return javaTreeStructure.getChildElements(descriptor as MethodHierarchyNodeDescriptor) ?: ArrayUtil.EMPTY_OBJECT_ARRAY + return javaTreeStructure.getChildElements(nodeDescriptor as MethodHierarchyNodeDescriptor) ?: ArrayUtil.EMPTY_OBJECT_ARRAY } return javaTreeStructures diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt index 49c82632fac..34aa00b2a6e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt @@ -40,13 +40,12 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.hierarchy.calls.CalleeReferenceProcessor import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallHierarchyNodeDescriptor -import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerMethodsTreeStructure import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import java.util.HashSet -import java.util.LinkedHashSet +import java.util.* class KotlinCallerChooser( declaration: PsiElement, @@ -110,7 +109,7 @@ class KotlinMethodNode( val callers = LinkedHashSet() - val processor = object: KotlinCallerMethodsTreeStructure.CalleeReferenceProcessor(false) { + val processor = object: CalleeReferenceProcessor(false) { override fun onAccept(ref: PsiReference, element: PsiElement) { if ((element is KtFunction || element is KtClass || element is PsiMethod) && element !in myCalled) { callers.add(element) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/dataContextUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/dataContextUtils.kt index 51a52610add..aed384af123 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/dataContextUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/dataContextUtils.kt @@ -20,9 +20,13 @@ import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement val DataContext.project: Project get() = CommonDataKeys.PROJECT.getData(this)!! val DataContext.hostEditor: Editor? - get() = CommonDataKeys.HOST_EDITOR.getData(this) \ No newline at end of file + get() = CommonDataKeys.HOST_EDITOR.getData(this) + +val DataContext.psiElement : PsiElement? + get() = CommonDataKeys.PSI_ELEMENT.getData(this) \ No newline at end of file diff --git a/idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/CallInsideAnonymousFun_verification.xml b/idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/CallInsideAnonymousFun_verification.xml new file mode 100644 index 00000000000..0b38b9f7c93 --- /dev/null +++ b/idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/CallInsideAnonymousFun_verification.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/main0.kt b/idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/main0.kt new file mode 100644 index 00000000000..af28a90ca52 --- /dev/null +++ b/idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/main0.kt @@ -0,0 +1,11 @@ +fun foo() = 1 + +fun bar() { + val x = fun () { + val y = foo() + } +} + +fun baz() { + bar() +} \ No newline at end of file diff --git a/idea/testData/hierarchy/calls/callers/callInsideLambda/CallInsideLambda_verification.xml b/idea/testData/hierarchy/calls/callers/callInsideLambda/CallInsideLambda_verification.xml new file mode 100644 index 00000000000..0b38b9f7c93 --- /dev/null +++ b/idea/testData/hierarchy/calls/callers/callInsideLambda/CallInsideLambda_verification.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/idea/testData/hierarchy/calls/callers/callInsideLambda/main0.kt b/idea/testData/hierarchy/calls/callers/callInsideLambda/main0.kt new file mode 100644 index 00000000000..70ed3dec54c --- /dev/null +++ b/idea/testData/hierarchy/calls/callers/callInsideLambda/main0.kt @@ -0,0 +1,11 @@ +fun foo() = 1 + +fun bar() { + val x = { + val y = foo() + } +} + +fun baz() { + bar() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/hierarchy/AbstractHierarchyTest.java b/idea/tests/org/jetbrains/kotlin/idea/hierarchy/AbstractHierarchyTest.java index 7d87a290d86..a10aba160b9 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/hierarchy/AbstractHierarchyTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/hierarchy/AbstractHierarchyTest.java @@ -26,6 +26,7 @@ import com.intellij.lang.LanguageExtension; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; @@ -36,10 +37,11 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.KotlinHierarchyViewTestBase; -import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCalleeMethodsTreeStructure; -import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerMethodsTreeStructure; +import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCalleeTreeStructure; +import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerTreeStructure; import org.jetbrains.kotlin.idea.hierarchy.overrides.KotlinOverrideTreeStructure; import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; +import org.jetbrains.kotlin.psi.KtElement; import java.io.File; import java.util.ArrayList; @@ -132,9 +134,8 @@ public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase return new Computable() { @Override public HierarchyTreeStructure compute() { - return new KotlinCallerMethodsTreeStructure( - getProject(), - getElementAtCaret(LanguageCallHierarchy.INSTANCE), + return new KotlinCallerTreeStructure( + (KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE), HierarchyBrowserBaseEx.SCOPE_PROJECT ); } @@ -158,9 +159,8 @@ public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase return new Computable() { @Override public HierarchyTreeStructure compute() { - return new KotlinCalleeMethodsTreeStructure( - getProject(), - getElementAtCaret(LanguageCallHierarchy.INSTANCE), + return new KotlinCalleeTreeStructure( + (KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE), HierarchyBrowserBaseEx.SCOPE_PROJECT ); } @@ -189,12 +189,16 @@ public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase private DataContext getDataContext() { Editor editor = getEditor(); - PsiFile psiFile = getFile(); MapDataContext context = new MapDataContext(); context.put(CommonDataKeys.PROJECT, getProject()); context.put(CommonDataKeys.EDITOR, editor); - context.put(CommonDataKeys.PSI_ELEMENT, psiFile.findElementAt(editor.getCaretModel().getOffset())); + PsiElement targetElement = (PsiElement) new TextEditorPsiDataProvider().getData( + CommonDataKeys.PSI_ELEMENT.getName(), + editor, + editor.getCaretModel().getCurrentCaret() + ); + context.put(CommonDataKeys.PSI_ELEMENT, targetElement); return context; } diff --git a/idea/tests/org/jetbrains/kotlin/idea/hierarchy/HierarchyTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/hierarchy/HierarchyTestGenerated.java index 1b37e753beb..4970fef301d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/hierarchy/HierarchyTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/hierarchy/HierarchyTestGenerated.java @@ -323,6 +323,18 @@ public class HierarchyTestGenerated extends AbstractHierarchyTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/hierarchy/calls/callers"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, false); } + @TestMetadata("callInsideAnonymousFun") + public void testCallInsideAnonymousFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/"); + doCallerHierarchyTest(fileName); + } + + @TestMetadata("callInsideLambda") + public void testCallInsideLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/callInsideLambda/"); + doCallerHierarchyTest(fileName); + } + @TestMetadata("kotlinClass") public void testKotlinClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/kotlinClass/");