diff --git a/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallHierarchyBrowser.java b/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallHierarchyBrowser.java index b1326b5d17a..fc3ed2e6cfc 100644 --- a/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallHierarchyBrowser.java +++ b/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallHierarchyBrowser.java @@ -80,7 +80,7 @@ public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase { @Override protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) { if (typeName.equals(CALLER_TYPE)) { - return KotlinCallerMethodsTreeStructure.newInstance(myProject, psiElement, getCurrentScopeType()); + return new KotlinCallerMethodsTreeStructure(myProject, psiElement, getCurrentScopeType()); } if (typeName.equals(CALLEE_TYPE)) { diff --git a/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallerMethodsTreeStructure.java b/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallerMethodsTreeStructure.java index 11b5e69992b..ffe0ae03b72 100644 --- a/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallerMethodsTreeStructure.java +++ b/idea/src/org/jetbrains/jet/plugin/hierarchy/calls/KotlinCallerMethodsTreeStructure.java @@ -42,29 +42,32 @@ import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; import org.jetbrains.jet.plugin.references.JetReference; import org.jetbrains.jet.plugin.search.usagesSearch.UsagesSearchPackage; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; -public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure { - private static class WithLocalRoot extends KotlinCallerMethodsTreeStructure { - private final JetElement codeBlockForLocalDeclaration; +public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure { + private final CallerMethodsTreeStructure javaTreeStructure; + private final PsiClass basePsiClass; - private WithLocalRoot( - @NotNull Project project, - @NotNull PsiElement element, - String scopeType, - JetElement codeBlockForLocalDeclaration - ) { - super(project, element, scopeType); - this.codeBlockForLocalDeclaration = codeBlockForLocalDeclaration; - } + public KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) { + super(project, element, scopeType); - @NotNull - @Override - protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) { - final PsiElement element = getTargetElement(descriptor); - assert element instanceof JetElement : - "JetElement must be passed by KotlinCallerMethodsTreeStructure.newInstance(): " + element.getText(); + 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); + + JetElement codeBlockForLocalDeclaration = getEnclosingElementForLocalDeclaration(element); + if (codeBlockForLocalDeclaration != null) { BindingContext bindingContext = AnalyzerFacadeWithCache.getContextForElement((JetElement) element); final Map referencesToElements = new HashMap(); @@ -74,8 +77,10 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr if (!declaration.equals(element)) return; //noinspection unchecked - PsiElement container = - PsiTreeUtil.getParentOfType(reference, JetNamedFunction.class, JetPropertyAccessor.class, JetClassOrObject.class); + PsiElement container = PsiTreeUtil.getParentOfType( + reference, + JetNamedFunction.class, JetPropertyAccessor.class, JetClassOrObject.class + ); if (container instanceof JetPropertyAccessor) { container = PsiTreeUtil.getParentOfType(container, JetProperty.class); } @@ -87,168 +92,137 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr }); 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 JetNamedFunction) { + PsiMethod lightMethod = LightClassUtil.getLightClassMethod((JetNamedFunction) element); + processPsiMethodCallers(Collections.singleton(lightMethod), descriptor, methodToDescriptorMap, searchScope, false); + } + if (element instanceof JetProperty) { + LightClassUtil.PropertyAccessorsPsiMethods propertyMethods = + LightClassUtil.getLightClassPropertyMethods((JetProperty) element); + processPsiMethodCallers(propertyMethods, descriptor, methodToDescriptorMap, searchScope, false); + } + if (element instanceof JetClassOrObject) { + processJetClassOrObjectCallers((JetClassOrObject) element, descriptor, methodToDescriptorMap, searchScope); + } + + Object[] callers = methodToDescriptorMap.values().toArray(new Object[methodToDescriptorMap.size()]); + return (javaCallers != null) ? ArrayUtil.mergeArrays(javaCallers, callers) : callers; } - private static class WithNonLocalRoot extends KotlinCallerMethodsTreeStructure { - private final CallerMethodsTreeStructure javaTreeStructure; - private final PsiClass basePsiClass; + private void processPsiMethodCallers( + Iterable lightMethods, + HierarchyNodeDescriptor descriptor, + Map methodToDescriptorMap, + SearchScope searchScope, + boolean kotlinOnly + ) { + Set methodsToFind = new HashSet(); + for (PsiMethod lightMethod : lightMethods) { + if (lightMethod == null) continue; - private WithNonLocalRoot(@NotNull Project project, @NotNull PsiElement element, String scopeType, PsiMethod basePsiMethod) { - super(project, element, scopeType); - - this.basePsiClass = basePsiMethod.getContainingClass(); - this.javaTreeStructure = new CallerMethodsTreeStructure(project, basePsiMethod, scopeType); + PsiMethod[] superMethods = lightMethod.findDeepestSuperMethods(); + methodsToFind.add(lightMethod); + ContainerUtil.addAll(methodsToFind, superMethods); } - @NotNull - @Override - protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) { - PsiElement element = getTargetElement(descriptor); + if (methodsToFind.isEmpty()) return; - 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 JetNamedFunction) { - PsiMethod lightMethod = LightClassUtil.getLightClassMethod((JetNamedFunction) element); - processPsiMethodCallers(Collections.singleton(lightMethod), descriptor, methodToDescriptorMap, searchScope, false); - } - if (element instanceof JetProperty) { - LightClassUtil.PropertyAccessorsPsiMethods propertyMethods = - LightClassUtil.getLightClassPropertyMethods((JetProperty) element); - processPsiMethodCallers(propertyMethods, descriptor, methodToDescriptorMap, searchScope, false); - } - if (element instanceof JetClassOrObject) { - processJetClassOrObjectCallers((JetClassOrObject) element, descriptor, methodToDescriptorMap, searchScope); - } - - Object[] callers = methodToDescriptorMap.values().toArray(new Object[methodToDescriptorMap.size()]); - return (javaCallers != null) ? ArrayUtil.mergeArrays(javaCallers, callers) : callers; - } - - private 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)); - } - - private void processJetClassOrObjectCallers( - final JetClassOrObject classOrObject, - HierarchyNodeDescriptor descriptor, - Map methodToDescriptorMap, - SearchScope searchScope - ) { - Processor processor = new FilteringProcessor( - new Condition() { - @Override - public boolean value(PsiReference reference) { - return UsagesSearchPackage.isConstructorUsage(reference, classOrObject); - } - }, - defaultQueryProcessor(descriptor, methodToDescriptorMap, false) - ); - ReferencesSearch.search(classOrObject, searchScope, false).forEach(processor); - } - - private Processor defaultQueryProcessor( - final HierarchyNodeDescriptor descriptor, - final Map methodToDescriptorMap, - final boolean kotlinOnly - ) { - return new ReadActionProcessor() { - @Override - public boolean processInReadAction(PsiReference ref) { - // copied from Java - if (!(ref instanceof PsiReferenceExpression || ref instanceof JetReference)) { - 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 { - return true; - } + Set references = ContainerUtil.newTroveSet( + new TObjectHashingStrategy() { + @Override + public int computeHashCode(PsiReference object) { + return object.getElement().hashCode(); } - PsiElement element = HierarchyUtils.getCallHierarchyElement(ref.getElement()); - - if (kotlinOnly && !(element instanceof JetNamedDeclaration)) return true; - - // If reference belongs to property initializer, show enclosing declaration instead - if (element instanceof JetProperty) { - JetProperty property = (JetProperty) element; - if (PsiTreeUtil.isAncestor(property.getInitializer(), ref.getElement(), false)) { - element = HierarchyUtils.getCallHierarchyElement(element.getParent()); - } + @Override + public boolean equals(PsiReference o1, PsiReference o2) { + return o1.getElement().equals(o2.getElement()); } - - if (element != null) { - addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor); - } - - return true; } - }; + ); + for (PsiMethod superMethod: methodsToFind) { + ContainerUtil.addAll(references, MethodReferencesSearch.search(superMethod, searchScope, true)); } + ContainerUtil.process(references, defaultQueryProcessor(descriptor, methodToDescriptorMap, kotlinOnly)); } - private KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) { - super(project, element, scopeType); + private void processJetClassOrObjectCallers( + final JetClassOrObject classOrObject, + HierarchyNodeDescriptor descriptor, + Map methodToDescriptorMap, + SearchScope searchScope + ) { + Processor processor = new FilteringProcessor( + new Condition() { + @Override + public boolean value(PsiReference reference) { + return UsagesSearchPackage.isConstructorUsage(reference, classOrObject); + } + }, + defaultQueryProcessor(descriptor, methodToDescriptorMap, false) + ); + ReferencesSearch.search(classOrObject, searchScope, false).forEach(processor); } - public static KotlinCallerMethodsTreeStructure newInstance(@NotNull Project project, @NotNull PsiElement element, String scopeType) { - JetElement codeBlockForLocalDeclaration = getEnclosingElementForLocalDeclaration(element); - if (codeBlockForLocalDeclaration != null) return new WithLocalRoot(project, element, scopeType, codeBlockForLocalDeclaration); + private Processor defaultQueryProcessor( + final HierarchyNodeDescriptor descriptor, + final Map methodToDescriptorMap, + final boolean kotlinOnly + ) { + return new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference ref) { + // copied from Java + if (!(ref instanceof PsiReferenceExpression || ref instanceof JetReference)) { + if (!(ref instanceof PsiElement)) { + return true; + } - PsiMethod representativeMethod = getRepresentativePsiMethod(element); - assert representativeMethod != null : "Can't generate light method: " + element.getText(); - return new WithNonLocalRoot(project, element, scopeType, representativeMethod); + 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 { + return true; + } + } + + PsiElement element = HierarchyUtils.getCallHierarchyElement(ref.getElement()); + + if (kotlinOnly && !(element instanceof JetNamedDeclaration)) return true; + + // If reference belongs to property initializer, show enclosing declaration instead + if (element instanceof JetProperty) { + JetProperty property = (JetProperty) element; + if (PsiTreeUtil.isAncestor(property.getInitializer(), ref.getElement(), false)) { + element = HierarchyUtils.getCallHierarchyElement(element.getParent()); + } + } + + if (element != null) { + addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor); + } + + return true; + } + }; } -} +} \ No newline at end of file diff --git a/idea/testData/hierarchy/calls/callers/kotlinLocalFunctionWithNonLocalCallers/KotlinLocalFunctionWithNonLocalCallers_verification.xml b/idea/testData/hierarchy/calls/callers/kotlinLocalFunctionWithNonLocalCallers/KotlinLocalFunctionWithNonLocalCallers_verification.xml new file mode 100644 index 00000000000..a22c94acf6e --- /dev/null +++ b/idea/testData/hierarchy/calls/callers/kotlinLocalFunctionWithNonLocalCallers/KotlinLocalFunctionWithNonLocalCallers_verification.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/idea/testData/hierarchy/calls/callers/kotlinLocalFunctionWithNonLocalCallers/main0.kt b/idea/testData/hierarchy/calls/callers/kotlinLocalFunctionWithNonLocalCallers/main0.kt new file mode 100644 index 00000000000..c3461efa030 --- /dev/null +++ b/idea/testData/hierarchy/calls/callers/kotlinLocalFunctionWithNonLocalCallers/main0.kt @@ -0,0 +1,10 @@ +fun test() { + test1() +} + +fun test1() { + fun foo() { + + } + foo() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/hierarchy/AbstractHierarchyTest.java b/idea/tests/org/jetbrains/jet/plugin/hierarchy/AbstractHierarchyTest.java index 28328ac1adc..5e049772f9a 100644 --- a/idea/tests/org/jetbrains/jet/plugin/hierarchy/AbstractHierarchyTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/hierarchy/AbstractHierarchyTest.java @@ -126,7 +126,7 @@ public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase return new Computable() { @Override public HierarchyTreeStructure compute() { - return KotlinCallerMethodsTreeStructure.newInstance( + return new KotlinCallerMethodsTreeStructure( getProject(), getElementAtCaret(LanguageCallHierarchy.INSTANCE.forLanguage(getLanguage())), HierarchyBrowserBaseEx.SCOPE_PROJECT diff --git a/idea/tests/org/jetbrains/jet/plugin/hierarchy/HierarchyTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/hierarchy/HierarchyTestGenerated.java index 0423ef9d1b9..2be08e86ace 100644 --- a/idea/tests/org/jetbrains/jet/plugin/hierarchy/HierarchyTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/hierarchy/HierarchyTestGenerated.java @@ -324,6 +324,12 @@ public class HierarchyTestGenerated extends AbstractHierarchyTest { doCallerHierarchyTest(fileName); } + @TestMetadata("kotlinLocalFunctionWithNonLocalCallers") + public void testKotlinLocalFunctionWithNonLocalCallers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/kotlinLocalFunctionWithNonLocalCallers/"); + doCallerHierarchyTest(fileName); + } + @TestMetadata("kotlinNestedClass") public void testKotlinNestedClass() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/kotlinNestedClass/");