Call Hierarchy: Add support of local declarations

This commit is contained in:
Alexey Sedunov
2013-09-26 15:37:35 +04:00
parent 41ea7da1e3
commit 5b7881f40c
28 changed files with 494 additions and 175 deletions
@@ -1029,4 +1029,16 @@ public class JetPsiUtil {
return header != null ? header.getQualifiedName() : null;
}
public static JetElement getLocalizingCodeBlock(@NotNull JetNamedDeclaration declaration) {
//noinspection unchecked
JetDeclaration container =
PsiTreeUtil.getParentOfType(declaration, JetNamedFunction.class, JetPropertyAccessor.class, JetClassInitializer.class);
if (container == null) return null;
return (container instanceof JetClassInitializer)
? ((JetClassInitializer) container).getBody()
: ((JetDeclarationWithBody) container).getBodyExpression();
}
}
@@ -15,9 +15,9 @@ public class HierarchyUtils {
return input instanceof PsiMethod ||
input instanceof PsiClass ||
input instanceof JetFile ||
(input instanceof JetNamedFunction && !((JetNamedFunction) input).isLocal()) ||
input instanceof JetNamedFunction ||
input instanceof JetClassOrObject ||
(input instanceof JetProperty && !((JetProperty) input).isLocal());
input instanceof JetProperty;
}
};
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2013 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.jet.plugin.hierarchy.calls;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
public abstract class CalleeReferenceVisitorBase extends JetTreeVisitorVoid {
private final BindingContext bindingContext;
private final boolean deepTraversal;
protected CalleeReferenceVisitorBase(BindingContext bindingContext, boolean deepTraversal) {
this.bindingContext = bindingContext;
this.deepTraversal = deepTraversal;
}
protected abstract void processDeclaration(JetReferenceExpression reference, PsiElement declaration);
@Override
public void visitJetElement(JetElement element) {
if (deepTraversal || !(element instanceof JetClassOrObject || element instanceof JetNamedFunction)) {
super.visitJetElement(element);
}
}
@Override
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expression);
if (descriptor == null) return;
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (declaration == null) return;
if (isProperty(descriptor, declaration) || isCallable(descriptor, declaration, expression)) {
processDeclaration(expression, declaration);
}
}
// Accept callees of JetCallElement which refer to Kotlin function, Kotlin class or Java method
private static boolean isCallable(DeclarationDescriptor descriptor, PsiElement declaration, JetSimpleNameExpression reference) {
JetCallElement callElement = PsiTreeUtil.getParentOfType(reference, JetCallElement.class);
if (callElement == null || !PsiTreeUtil.isAncestor(callElement.getCalleeExpression(), reference, false)) return false;
return descriptor instanceof FunctionDescriptor
&& (declaration instanceof JetClassOrObject
|| declaration instanceof JetNamedFunction
|| declaration instanceof PsiMethod);
}
// Accept only properties (not local variables or references to Java fields)
private static boolean isProperty(DeclarationDescriptor descriptor, PsiElement declaration) {
return descriptor instanceof PropertyDescriptor && declaration instanceof JetProperty;
}
}
@@ -12,8 +12,10 @@ 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.*;
import com.intellij.psi.presentation.java.ClassPresentationUtil;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.LayeredIcon;
import com.intellij.util.Function;
@@ -8,14 +8,48 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.*;
import java.util.List;
import java.util.Map;
public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
public KotlinCallTreeStructure(@NotNull Project project, HierarchyNodeDescriptor baseDescriptor) {
super(project, baseDescriptor);
protected final String scopeType;
protected final JetElement localizingCodeBlock;
protected final PsiMethod basePsiMethod;
protected final PsiClass basePsiClass;
public KotlinCallTreeStructure(@NotNull Project project, PsiElement element, String scopeType) {
super(project, createNodeDescriptor(project, element, null));
this.scopeType = scopeType;
localizingCodeBlock = element instanceof JetNamedDeclaration
? JetPsiUtil.getLocalizingCodeBlock((JetNamedDeclaration) element)
: null;
if (localizingCodeBlock == null) {
basePsiMethod = getPsiMethod(element);
assert basePsiMethod != null;
basePsiClass = basePsiMethod.getContainingClass();
assert basePsiClass != null;
}
else {
basePsiMethod = null;
basePsiClass = null;
}
}
protected static HierarchyNodeDescriptor createNodeDescriptor(Project project, PsiElement element, HierarchyNodeDescriptor parent) {
boolean root = (parent == null);
return element instanceof JetElement
? new KotlinCallHierarchyNodeDescriptor(project, parent, element, root, false)
: new CallHierarchyNodeDescriptor(project, parent, element, root, false);
}
protected static PsiElement getTargetElement(HierarchyNodeDescriptor descriptor) {
@@ -24,17 +58,6 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
: ((KotlinCallHierarchyNodeDescriptor)descriptor).getTargetElement();
}
@Nullable
protected PsiMethod getBasePsiMethod() {
return getPsiMethod(((KotlinCallHierarchyNodeDescriptor) getBaseDescriptor()).getTargetElement());
}
@Nullable
protected PsiClass getBasePsiClass() {
PsiMethod method = getBasePsiMethod();
return method != null ? method.getContainingClass() : null;
}
private static PsiMethod getClosestContainingClassConstructor(PsiElement element) {
while (element != null) {
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(element, JetClassOrObject.class, false);
@@ -53,7 +76,9 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
}
@Nullable
protected static PsiMethod getPsiMethod(PsiElement element) {
protected final PsiMethod getPsiMethod(PsiElement element) {
assert localizingCodeBlock == null : "Can't build light method for local declaration";
if (element instanceof PsiMethod) {
return (PsiMethod) element;
}
@@ -77,12 +102,6 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
return method != null ? method : getClosestContainingClassConstructor(element);
}
@Nullable
protected static PsiClass getPsiClass(PsiElement element) {
PsiMethod method = getPsiMethod(element);
return method != null ? method.getContainingClass() : null;
}
protected static CallHierarchyNodeDescriptor getJavaNodeDescriptor(HierarchyNodeDescriptor originalDescriptor) {
if (originalDescriptor instanceof CallHierarchyNodeDescriptor) return (CallHierarchyNodeDescriptor) originalDescriptor;
@@ -90,6 +109,36 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
return ((KotlinCallHierarchyNodeDescriptor) originalDescriptor).getJavaDelegate();
}
protected final Object[] collectNodeDescriptors(HierarchyNodeDescriptor descriptor, List<? extends PsiElement> calleeElements) {
HashMap<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap = new HashMap<PsiElement, HierarchyNodeDescriptor>();
for (PsiElement callee : calleeElements) {
if (basePsiClass != null && !isInScope(basePsiClass, callee, scopeType)) continue;
addNodeDescriptorForElement(callee, declarationToDescriptorMap, descriptor);
}
return declarationToDescriptorMap.values().toArray(new Object[declarationToDescriptorMap.size()]);
}
protected final void addNodeDescriptorForElement(
PsiElement element,
Map<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap,
HierarchyNodeDescriptor descriptor
) {
HierarchyNodeDescriptor d = declarationToDescriptorMap.get(element);
if (d == null) {
d = createNodeDescriptor(myProject, element, descriptor);
declarationToDescriptorMap.put(element, d);
}
else {
if (d instanceof CallHierarchyNodeDescriptor) {
((CallHierarchyNodeDescriptor) d).incrementUsageCount();
}
else if (d instanceof KotlinCallHierarchyNodeDescriptor) {
((KotlinCallHierarchyNodeDescriptor) d).incrementUsageCount();
}
}
}
@Override
public boolean isAlwaysShowPlus() {
return true;
@@ -24,13 +24,9 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetClsMethod;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
@@ -38,87 +34,21 @@ import java.util.ArrayList;
import java.util.List;
public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
private final String scopeType;
private final CalleeMethodsTreeStructure javaTreeStucture;
private final CalleeMethodsTreeStructure javaTreeStructure;
public KotlinCalleeMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
super(project, new KotlinCallHierarchyNodeDescriptor(project, null, element, true, false));
this.scopeType = scopeType;
PsiMethod psiMethod = getPsiMethod(element);
assert psiMethod != null;
this.javaTreeStucture = new CalleeMethodsTreeStructure(project, psiMethod, scopeType);
}
private static class VisitorBase extends JetTreeVisitorVoid {
final BindingContext bindingContext;
final ArrayList<PsiElement> result;
private VisitorBase(BindingContext bindingContext, ArrayList<PsiElement> result) {
this.bindingContext = bindingContext;
this.result = result;
}
@Override
public void visitJetElement(JetElement element) {
if (!(element instanceof JetClassOrObject || element instanceof JetNamedFunction)) {
super.visitJetElement(element);
}
}
@Override
public void visitCallExpression(JetCallExpression expression) {
super.visitCallExpression(expression);
processCallElement(expression);
}
@Override
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expression);
if (descriptor instanceof PropertyDescriptor) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (declaration instanceof JetProperty) {
result.add(declaration);
}
}
}
@Override
public void visitDelegationSpecifier(JetDelegationSpecifier specifier) {
if (specifier instanceof JetCallElement) {
processCallElement((JetCallElement) specifier);
}
}
private void processCallElement(JetCallElement element) {
JetExpression callee = element.getCalleeExpression();
JetReferenceExpression referenceExpression = null;
if (callee instanceof JetReferenceExpression) {
referenceExpression = (JetReferenceExpression) callee;
}
else if (callee instanceof JetConstructorCalleeExpression) {
referenceExpression = ((JetConstructorCalleeExpression) callee).getConstructorReferenceExpression();
}
if (referenceExpression != null) {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, referenceExpression);
if (descriptor == null) return;
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (declaration instanceof JetClassOrObject
|| (declaration instanceof JetNamedFunction && !((JetNamedFunction) declaration).isLocal())
|| declaration instanceof PsiMethod ) {
result.add(declaration);
}
}
}
super(project, element, scopeType);
this.javaTreeStructure = basePsiMethod != null ? new CalleeMethodsTreeStructure(project, basePsiMethod, scopeType) : null;
}
private static List<? extends PsiElement> getCalleeElements(@NotNull JetElement rootElement, BindingContext bindingContext) {
ArrayList<PsiElement> result = new ArrayList<PsiElement>();
JetVisitorVoid visitor = new VisitorBase(bindingContext, result);
final ArrayList<PsiElement> result = new ArrayList<PsiElement>();
JetVisitorVoid visitor = new CalleeReferenceVisitorBase(bindingContext, false) {
@Override
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
result.add(declaration);
}
};
if (rootElement instanceof JetNamedFunction) {
JetExpression body = ((JetNamedFunction) rootElement).getBodyExpression();
@@ -157,33 +87,6 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
return result;
}
// copied from Java
private Object[] collectNodeDescriptors(
HierarchyNodeDescriptor descriptor,
List<? extends PsiElement> calleeElements,
PsiElement baseElement
) {
HashMap<PsiElement, KotlinCallHierarchyNodeDescriptor> declarationToDescriptorMap =
new HashMap<PsiElement, KotlinCallHierarchyNodeDescriptor>();
ArrayList<KotlinCallHierarchyNodeDescriptor> result = new ArrayList<KotlinCallHierarchyNodeDescriptor>();
for (PsiElement callee : calleeElements) {
if (!isInScope(baseElement, callee, scopeType)) continue;
KotlinCallHierarchyNodeDescriptor d = declarationToDescriptorMap.get(callee);
if (d == null) {
d = new KotlinCallHierarchyNodeDescriptor(myProject, descriptor, callee, false, false);
declarationToDescriptorMap.put(callee, d);
result.add(d);
}
else {
d.incrementUsageCount();
}
}
return ArrayUtil.toObjectArray(result);
}
@Override
protected Object[] buildChildren(HierarchyNodeDescriptor descriptor) {
PsiElement targetElement = getTargetElement(descriptor);
@@ -209,24 +112,20 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
return buildChildrenByKotlinTarget(descriptor, (JetElement) targetElement);
}
if (javaTreeStucture != null) {
if (javaTreeStructure != null) {
CallHierarchyNodeDescriptor javaDescriptor = descriptor instanceof CallHierarchyNodeDescriptor
? (CallHierarchyNodeDescriptor) descriptor
: ((KotlinCallHierarchyNodeDescriptor)descriptor).getJavaDelegate();
return javaTreeStucture.getChildElements(javaDescriptor);
return javaTreeStructure.getChildElements(javaDescriptor);
}
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
private Object[] buildChildrenByKotlinTarget(HierarchyNodeDescriptor descriptor, JetElement targetElement) {
PsiClass baseClass = getBasePsiClass();
if (baseClass == null) return ArrayUtil.EMPTY_OBJECT_ARRAY;
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) targetElement.getContainingFile()).getBindingContext();
List<? extends PsiElement> calleeDescriptors = getCalleeElements((JetElement) targetElement, bindingContext);
return collectNodeDescriptors(descriptor, calleeDescriptors, baseClass);
return collectNodeDescriptors(descriptor, calleeDescriptors);
}
}
@@ -34,38 +34,61 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.findUsages.FindUsagesUtils;
import org.jetbrains.jet.plugin.hierarchy.HierarchyUtils;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.references.JetPsiReference;
import java.util.ArrayList;
import java.util.Map;
public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure {
private final String scopeType;
private final CallerMethodsTreeStructure javaTreeStructure;
public KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
super(project, new KotlinCallHierarchyNodeDescriptor(project, null, element, true, false));
this.scopeType = scopeType;
PsiMethod psiMethod = getPsiMethod(element);
assert psiMethod != null;
this.javaTreeStructure = new CallerMethodsTreeStructure(project, psiMethod, scopeType);
super(project, element, scopeType);
this.javaTreeStructure = basePsiMethod != null ? new CallerMethodsTreeStructure(project, basePsiMethod, scopeType) : null;
}
@Override
protected Object[] buildChildren(HierarchyNodeDescriptor descriptor) {
PsiElement targetElement = getTargetElement(descriptor);
PsiClass baseClass = getBasePsiClass();
if (baseClass == null) return ArrayUtil.EMPTY_OBJECT_ARRAY;
if (localizingCodeBlock != null) {
return buildLocalizedChildren(targetElement, (KotlinCallHierarchyNodeDescriptor) descriptor);
}
return processCallers(targetElement, descriptor, baseClass);
return processCallers(targetElement, descriptor);
}
private Object[] processCallers(PsiElement element, HierarchyNodeDescriptor descriptor, PsiClass baseClass) {
SearchScope searchScope = getSearchScope(scopeType, baseClass);
private Object[] buildLocalizedChildren(final PsiElement element, KotlinCallHierarchyNodeDescriptor descriptor) {
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
final ArrayList<PsiElement> result = new ArrayList<PsiElement>();
localizingCodeBlock.accept(new CalleeReferenceVisitorBase(bindingContext, true) {
@Override
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
if (!declaration.equals(element)) return;
//noinspection unchecked
PsiElement container =
PsiTreeUtil.getParentOfType(reference, JetNamedFunction.class, JetPropertyAccessor.class, JetClassOrObject.class);
if (container instanceof JetPropertyAccessor) {
container = PsiTreeUtil.getParentOfType(container, JetProperty.class);
}
if (container != null) {
result.add(container);
}
}
});
return collectNodeDescriptors(descriptor, result);
}
private Object[] processCallers(PsiElement element, HierarchyNodeDescriptor descriptor) {
SearchScope searchScope = getSearchScope(scopeType, basePsiClass);
Map<PsiElement, KotlinCallHierarchyNodeDescriptor> methodToDescriptorMap =
new HashMap<PsiElement, KotlinCallHierarchyNodeDescriptor>();
@@ -0,0 +1,10 @@
<node text="client.[anonymous] ()" base="true">
<node text="JA.JA()(2 usages) ()"/>
<node text="JA.foo(String) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="KA(2 usages) ()"/>
<node text="KA.foo(String) ()"/>
<node text="KA.name ()"/>
<node text="packageFun(String)(2 usages) ()"/>
</node>
@@ -0,0 +1,32 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
fun client() {
val obj = <caret>object {
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
}
fun localFun(s: String): String = packageFun(s)
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
@@ -0,0 +1,12 @@
class JA {
public String name = "A";
public JA() {
}
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -1,12 +1,15 @@
<node text="KClient" base="true">
<node text="KClientBase"/>
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="KClient.localFun(String)">
<node text="packageFun(String)"/>
</node>
<node text="packageVal"/>
<node text="packageFun(String)(2 usages)"/>
</node>
@@ -1,7 +1,10 @@
<node text="KClient.bar()" base="true">
<node text="bar.localFun(String)(2 usages)">
<node text="packageFun(String)"/>
</node>
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
@@ -0,0 +1,10 @@
<node text="client.T ()" base="true">
<node text="JA.JA()(2 usages) ()"/>
<node text="JA.foo(String) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="KA(2 usages) ()"/>
<node text="KA.foo(String) ()"/>
<node text="KA.name ()"/>
<node text="packageFun(String)(2 usages) ()"/>
</node>
@@ -0,0 +1,32 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
fun client() {
class <caret>T {
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
}
fun localFun(s: String): String = packageFun(s)
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
@@ -0,0 +1,12 @@
class JA {
public String name = "A";
public JA() {
}
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,19 @@
<node text="client.localFun(String) ()" base="true">
<node text="JA.JA()(2 usages) ()"/>
<node text="JA.foo(String) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="KA(2 usages) ()"/>
<node text="KA.foo(String) ()"/>
<node text="KA.name ()"/>
<node text="localFun.bar() ()">
<node text="KA.name ()"/>
<node text="JA.foo(String) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="KA(2 usages) ()"/>
<node text="JA.JA()(2 usages) ()"/>
<node text="KA.foo(String) ()"/>
</node>
<node text="packageFun(String)(2 usages) ()"/>
</node>
@@ -0,0 +1,31 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
fun client() {
fun <caret>localFun(s: String): String {
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
bar()
}
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
@@ -0,0 +1,12 @@
class JA {
public String name = "A";
public JA() {
}
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -1,12 +1,15 @@
<node text="KClient" base="true">
<node text="KClientBase"/>
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="KClient.localFun(String)">
<node text="packageFun(String)"/>
</node>
<node text="packageVal"/>
<node text="packageFun(String)(2 usages)"/>
</node>
@@ -1,7 +1,10 @@
<node text="KClient.bar" base="true">
<node text="KClient.localFun(String)(2 usages)">
<node text="packageFun(String)"/>
</node>
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
@@ -1,11 +1,12 @@
<node text="KA" base="true">
<node text="JA"/>
<node text="JA.newKA()"/>
<node text="KClient"/>
<node text="KClient"/>
<node text="KClient.bar"/>
<node text="KClient.bar()(2 usages)"/>
<node text="KClientObj"/>
<node text="main0.kt"/>
<node text="bar.localFun()"/>
<node text="packageFun(String)"/>
<node text="KClient"/>
<node text="KClient.bar()"/>
<node text="main0.kt"/>
<node text="JA.newKA()"/>
<node text="JA"/>
<node text="KClient.bar"/>
<node text="KClient"/>
</node>
@@ -1,11 +1,12 @@
<node text="KA.foo(String)" base="true">
<node text="JA"/>
<node text="JA.foo()"/>
<node text="KClient"/>
<node text="KClient"/>
<node text="KClient.bar"/>
<node text="KClient.bar()(2 usages)"/>
<node text="KClientObj"/>
<node text="main0.kt"/>
<node text="bar.localFun()"/>
<node text="packageFun(String)"/>
<node text="KClientObj"/>
<node text="KClient.bar"/>
<node text="main0.kt"/>
<node text="JA.foo()"/>
<node text="JA"/>
<node text="KClient"/>
<node text="KClient.bar()"/>
</node>
@@ -0,0 +1,6 @@
<node text="client.KA ()" base="true">
<node text="bar.localFun() ()"/>
<node text="client.bar() ()"/>
<node text="client.KClientObj ()"/>
<node text="KClient.client() ()"/>
</node>
@@ -0,0 +1,20 @@
class KClient {
fun client() {
class <caret>KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
val bar: String = KA().name
fun bar() {
fun localFun() = KA()
KA()
}
object KClientObj {
val a = KA()
}
}
}
@@ -0,0 +1,6 @@
<node text="client.foo(String) ()" base="true">
<node text="client.bar() ()"/>
<node text="bar.localFun() ()"/>
<node text="client.KClientObj ()"/>
<node text="KClient.client() ()"/>
</node>
@@ -0,0 +1,17 @@
class KClient {
fun client() {
fun <caret>foo(s: String): String = ""
val bar: String = foo("")
fun bar() {
fun localFun() = foo("")
foo("")
}
object KClientObj {
val a = foo("")
}
}
}
@@ -1,11 +1,12 @@
<node text="KA.name" base="true">
<node text="JA"/>
<node text="JA.getName()"/>
<node text="KClient(2 usages)"/>
<node text="KClient(2 usages)"/>
<node text="KClient.bar()(4 usages)"/>
<node text="packageFun(String)(2 usages)"/>
<node text="KClient.bar()(2 usages)"/>
<node text="KClient.bar(4 usages)"/>
<node text="bar.localFun()(2 usages)"/>
<node text="JA.getName()"/>
<node text="JA"/>
<node text="KClient(2 usages)"/>
<node text="KClient(2 usages)"/>
<node text="KClientObj(4 usages)"/>
<node text="main0.kt(2 usages)"/>
<node text="packageFun(String)(2 usages)"/>
</node>
@@ -217,6 +217,16 @@ public class HierarchyTestGenerated extends AbstractHierarchyTest {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinFunction");
}
@TestMetadata("kotlinLocalClass")
public void testKotlinLocalClass() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinLocalClass");
}
@TestMetadata("kotlinLocalFunction")
public void testKotlinLocalFunction() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinLocalFunction");
}
@TestMetadata("kotlinNestedClass")
public void testKotlinNestedClass() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinNestedClass");
@@ -250,6 +260,11 @@ public class HierarchyTestGenerated extends AbstractHierarchyTest {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/javaMethod");
}
@TestMetadata("kotlinAnonymousObject")
public void testKotlinAnonymousObject() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinAnonymousObject");
}
@TestMetadata("kotlinClass")
public void testKotlinClass() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinClass");
@@ -270,6 +285,16 @@ public class HierarchyTestGenerated extends AbstractHierarchyTest {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinFunction");
}
@TestMetadata("kotlinLocalClass")
public void testKotlinLocalClass() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinLocalClass");
}
@TestMetadata("kotlinLocalFunction")
public void testKotlinLocalFunction() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinLocalFunction");
}
@TestMetadata("kotlinNestedClass")
public void testKotlinNestedClass() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinNestedClass");