Support for callable references in smart completion

This commit is contained in:
Valentin Kipyatkov
2015-09-30 19:56:17 +03:00
parent 3f64b25df3
commit ebf649a681
24 changed files with 241 additions and 46 deletions
@@ -30,10 +30,7 @@ import org.jetbrains.kotlin.psi.JetCallableReferenceExpression
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.TypeResolver
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
@@ -204,8 +201,8 @@ private fun bindPropertyReference(expression: JetCallableReferenceExpression, re
private fun createReflectionTypeForCallableDescriptor(
descriptor: CallableDescriptor,
context: ResolutionContext<*>,
reflectionTypes: ReflectionTypes,
trace: BindingTrace?,
reportOn: JetExpression?
): JetType? {
val extensionReceiver = descriptor.getExtensionReceiverParameter()
@@ -213,7 +210,7 @@ private fun createReflectionTypeForCallableDescriptor(
if (extensionReceiver != null && dispatchReceiver != null && descriptor is CallableMemberDescriptor) {
if (reportOn != null) {
context.trace.report(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED.on(reportOn, descriptor))
trace?.report(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED.on(reportOn, descriptor))
}
return null
}
@@ -227,7 +224,7 @@ private fun createReflectionTypeForCallableDescriptor(
createReflectionTypeForProperty(descriptor, receiverType, reflectionTypes)
is VariableDescriptor -> {
if (reportOn != null) {
context.trace.report(UNSUPPORTED.on(reportOn, "References to variables aren't supported yet"))
trace?.report(UNSUPPORTED.on(reportOn, "References to variables aren't supported yet"))
}
null
}
@@ -238,10 +235,9 @@ private fun createReflectionTypeForCallableDescriptor(
public fun getReflectionTypeForCandidateDescriptor(
descriptor: CallableDescriptor,
context: ResolutionContext<*>,
reflectionTypes: ReflectionTypes
): JetType? =
createReflectionTypeForCallableDescriptor(descriptor, context, reflectionTypes, null)
createReflectionTypeForCallableDescriptor(descriptor, reflectionTypes, null, null)
public fun createReflectionTypeForResolvedCallableReference(
reference: JetCallableReferenceExpression,
@@ -249,7 +245,7 @@ public fun createReflectionTypeForResolvedCallableReference(
context: ResolutionContext<*>,
reflectionTypes: ReflectionTypes
): JetType? {
val type = createReflectionTypeForCallableDescriptor(descriptor, context, reflectionTypes, reference.getCallableReference())
val type = createReflectionTypeForCallableDescriptor(descriptor, reflectionTypes, context.trace, reference.getCallableReference())
?: return null
when (descriptor) {
is FunctionDescriptor -> {
@@ -276,7 +272,7 @@ public fun getResolvedCallableReferenceShapeType(
null
overloadResolutionResults.isSingleResult() ->
OverloadResolutionResultsUtil.getResultingCall(overloadResolutionResults, context.contextDependency)?.let { call ->
createReflectionTypeForCallableDescriptor(call.getResultingDescriptor(), context, reflectionTypes, reference)
createReflectionTypeForCallableDescriptor(call.getResultingDescriptor(), reflectionTypes, context.trace, reference)
}
expectedTypeUnknown /* && overload resolution was ambiguous */ ->
functionPlaceholders.createFunctionPlaceholderType(emptyList(), false)
@@ -164,7 +164,7 @@ public class CandidateResolver(
= check {
if (!noExpectedType(expectedType)) {
val candidate = candidateCall.getCandidateDescriptor()
val candidateReflectionType = getReflectionTypeForCandidateDescriptor(candidate, this, reflectionTypes);
val candidateReflectionType = getReflectionTypeForCandidateDescriptor(candidate, reflectionTypes);
if (candidateReflectionType != null) {
if (!JetTypeChecker.DEFAULT.isSubtypeOf(candidateReflectionType, expectedType)) {
candidateCall.addStatus(OTHER_ERROR)
@@ -102,7 +102,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
private val smartCompletion = expression?.let {
SmartCompletion(
it, resolutionFacade, bindingContext, isVisibleFilter, prefixMatcher,
GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory, forBasicCompletion = true
GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver, forBasicCompletion = true
)
}
@@ -353,7 +353,8 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
}
if (smartCompletion != null) {
sorter = sorter.weighBefore(KindWeigher.toString(), SmartCompletionInBasicWeigher(smartCompletion), SmartCompletionPriorityWeigher)
val smartCompletionInBasicWeigher = SmartCompletionInBasicWeigher(smartCompletion, callTypeAndReceiver.callType, resolutionFacade)
sorter = sorter.weighBefore(KindWeigher.toString(), smartCompletionInBasicWeigher, SmartCompletionPriorityWeigher)
}
return sorter
@@ -126,7 +126,14 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, isVisibleFilter)
protected val lookupElementFactory = createLookupElementFactory()
protected val callTypeAndReceiver: CallTypeAndReceiver<*, *>
protected val lookupElementFactory: LookupElementFactory
init {
val (callTypeAndReceiver, receiverTypes) = detectCallTypeAndReceiverTypes()
this.callTypeAndReceiver = callTypeAndReceiver
this.lookupElementFactory = createLookupElementFactory(callTypeAndReceiver.callType, receiverTypes)
}
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) {
@@ -298,9 +305,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
)
}
private fun createLookupElementFactory(): LookupElementFactory {
val (callType, receiverTypes) = detectCallTypeAndReceiverTypes()
private fun createLookupElementFactory(callType: CallType<*>, receiverTypes: Collection<JetType>?): LookupElementFactory {
val contextVariablesProvider = {
nameExpression?.let {
referenceVariantsHelper.getReferenceVariants(it, DescriptorKindFilter.VARIABLES, { true }, CallTypeAndReceiver.DEFAULT)
@@ -314,9 +319,9 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
insertHandlerProvider, contextVariablesProvider)
}
private fun detectCallTypeAndReceiverTypes(): Pair<CallType<*>, Collection<JetType>?> {
private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<JetType>?> {
if (nameExpression == null) {
return CallType.DEFAULT to null
return CallTypeAndReceiver.DEFAULT to null
}
val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression)
@@ -326,7 +331,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
is CallTypeAndReceiver.CALLABLE_REFERENCE -> {
if (callTypeAndReceiver.receiver != null) {
val type = bindingContext[BindingContext.TYPE, callTypeAndReceiver.receiver]
return callTypeAndReceiver.callType to type.singletonOrEmptyList()
return callTypeAndReceiver to type.singletonOrEmptyList()
}
else {
receiverExpression = null
@@ -343,7 +348,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
is CallTypeAndReceiver.PACKAGE_DIRECTIVE,
is CallTypeAndReceiver.TYPE ->
// we don't need to highlight immediate members in these cases
return callTypeAndReceiver.callType to null
return callTypeAndReceiver to null
else -> throw RuntimeException() //TODO: see KT-9394
}
@@ -374,6 +379,6 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
receiverTypes = receiverTypes.map { it.makeNotNullable() }
}
return callTypeAndReceiver.callType to receiverTypes
return callTypeAndReceiver to receiverTypes
}
}
@@ -30,6 +30,8 @@ import org.jetbrains.kotlin.idea.completion.smart.*
import org.jetbrains.kotlin.idea.core.ImportableFqNameClassifier
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
@@ -177,7 +179,11 @@ object PreferMatchingItemWeigher : LookupElementWeigher("kotlin.preferMatching",
}
}
class SmartCompletionInBasicWeigher(private val smartCompletion: SmartCompletion) : LookupElementWeigher("kotlin.smartInBasic", true, false) {
class SmartCompletionInBasicWeigher(
private val smartCompletion: SmartCompletion,
private val callType: CallType<*>,
private val resolutionFacade: ResolutionFacade
) : LookupElementWeigher("kotlin.smartInBasic", true, false) {
private val descriptorsToSkip = smartCompletion.descriptorsToSkip
private val expectedInfos = smartCompletion.expectedInfos
@@ -235,7 +241,7 @@ class SmartCompletionInBasicWeigher(private val smartCompletion: SmartCompletion
val (fuzzyTypes, name) = when (o) {
is DeclarationLookupObject -> {
val descriptor = o.descriptor ?: return NO_MATCH_WEIGHT
descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator) to descriptor.name
descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callType, resolutionFacade) to descriptor.name
}
is ThisItemLookupObject -> smartCastCalculator.types(o.receiverParameter).map { FuzzyType(it, emptyList()) } to null
@@ -29,11 +29,11 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.idea.util.isAlmostEverything
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.JetType
@@ -57,10 +57,22 @@ class SmartCompletion(
private val inheritorSearchScope: GlobalSearchScope,
private val toFromOriginalFileMapper: ToFromOriginalFileMapper,
private val lookupElementFactory: LookupElementFactory,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val forBasicCompletion: Boolean = false
) {
private val receiver = (expression as? JetSimpleNameExpression)?.getReceiverExpression()
private val expressionWithType = receiver?.parent as? JetExpression ?: expression
private val expressionWithType = when (callTypeAndReceiver) {
is CallTypeAndReceiver.DEFAULT ->
expression
is CallTypeAndReceiver.DOT,
is CallTypeAndReceiver.SAFE,
is CallTypeAndReceiver.INFIX,
is CallTypeAndReceiver.CALLABLE_REFERENCE ->
expression.parent as JetExpression
else -> // actually no smart completion for such places
expression
}
public val expectedInfos: Collection<ExpectedInfo> = calcExpectedInfos(expressionWithType)
@@ -141,14 +153,14 @@ class SmartCompletion(
if (descriptor in descriptorsToSkip) return emptyList()
val result = SmartList<LookupElement>()
val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator)
val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callTypeAndReceiver.callType, resolutionFacade)
val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) }
result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = receiver == null) { descriptor ->
result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { descriptor ->
lookupElementFactory.createLookupElementsInSmartCompletion(descriptor, bindingContext, true)
}
if (receiver == null) {
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
toFunctionReferenceLookupElement(descriptor, expectedInfos.filterFunctionExpected())?.let { result.add(it) }
}
@@ -165,7 +177,7 @@ class SmartCompletion(
val items = ArrayList<LookupElement>()
val inheritanceSearchers = ArrayList<InheritanceItemsSearcher>()
if (expectedInfos.isNotEmpty() && receiver == null) {
if (expectedInfos.isNotEmpty() && callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
TypeInstantiationItems(resolutionFacade, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion)
.addTo(items, inheritanceSearchers, expectedInfos)
@@ -41,7 +41,7 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
expression?.let {
SmartCompletion(it, resolutionFacade,
bindingContext, isVisibleFilter, prefixMatcher, originalSearchScope,
toFromOriginalFileMapper, lookupElementFactory)
toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver)
}
}
@@ -23,13 +23,16 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.callableReferences.getReflectionTypeForCandidateDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeSubstitutor
@@ -306,7 +309,17 @@ fun LookupElement.assignSmartCompletionPriority(priority: SmartCompletionItemPri
return this
}
fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(smartCastCalculator: SmartCastCalculator): Collection<FuzzyType> {
fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(
smartCastCalculator: SmartCastCalculator,
callType: CallType<*>,
resolutionFacade: ResolutionFacade
): Collection<FuzzyType> {
if (callType == CallType.CALLABLE_REFERENCE) {
if (this !is CallableDescriptor) return emptyList()
val type = getReflectionTypeForCandidateDescriptor(this, resolutionFacade.getFrontendService(ReflectionTypes::class.java)) ?: return emptyList()
return listOf(FuzzyType(type, emptyList()/* references to generic functions not supported yet */))
}
if (this is CallableDescriptor) {
var returnType = fuzzyReturnType() ?: return emptyList()
// skip declarations of type Nothing or of generic parameter type which has no real bounds
@@ -0,0 +1,9 @@
fun foo(p: () -> Unit){}
fun bar() {
foo(::<caret>)
}
fun f(){}
// ELEMENT: f
@@ -0,0 +1,9 @@
fun foo(p: () -> Unit){}
fun bar() {
foo(::f)<caret>
}
fun f(){}
// ELEMENT: f
@@ -0,0 +1,12 @@
class C
fun C.extFun(s: String){}
fun foo(p: C.(String) -> Unit, c: Char){}
fun foo(p: (String) -> Unit){}
fun bar() {
foo(C::<caret>)
}
// ELEMENT: extFun
@@ -0,0 +1,12 @@
class C
fun C.extFun(s: String){}
fun foo(p: C.(String) -> Unit, c: Char){}
fun foo(p: (String) -> Unit){}
fun bar() {
foo(C::extFun, <caret>)
}
// ELEMENT: extFun
@@ -0,0 +1,12 @@
fun foo(p: () -> Unit){}
fun bar() {
foo(::xf<caret>)
}
fun xf1(){}
fun xf1(s: String){}
fun xf2(i: Int){}
// EXIST: { lookupString:"xf1", itemText:"xf1", tailText: "() (<root>)", typeText: "Unit" }
// NOTHING_ELSE
@@ -0,0 +1,15 @@
import kotlin.reflect.KProperty0
fun foo(p: KProperty0<Int>){}
fun bar() {
foo(::xT<caret>)
}
val xTopLevelIntVal = 1
var xTopLevelIntVar = 1
val xTopLevelStringVal = "1"
// EXIST: { lookupString:"xTopLevelIntVal", itemText:"xTopLevelIntVal", tailText: " (<root>)", typeText: "Int" }
// EXIST: { lookupString:"xTopLevelIntVar", itemText:"xTopLevelIntVar", tailText: " (<root>)", typeText: "Int" }
// NOTHING_ELSE
@@ -0,0 +1,9 @@
class C(i: Int){}
fun foo(p: (Int) -> C){}
fun bar(){
foo(::<caret>)
}
// EXIST: C
@@ -0,0 +1,20 @@
class C {
fun xf1(){}
fun xf1(s: String){}
fun xf2(i: Int){}
}
fun C.xfe1(){}
fun C.xfe1(s: String){}
fun C.xfe2(i: Int){}
fun foo(p: C.(String) -> Unit){}
fun bar() {
foo(C::<caret>)
}
// EXIST: { lookupString:"xf1", itemText:"xf1", tailText: "(s: String)", typeText: "Unit" }
// EXIST: { lookupString:"xfe1", itemText:"xfe1", tailText: "(s: String) for C in <root>", typeText: "Unit" }
// NOTHING_ELSE
@@ -0,0 +1,15 @@
import kotlin.reflect.KProperty1
fun foo(p: KProperty1<String, Int>){}
fun bar() {
foo(String::xT<caret>)
}
val String.xTopLevelIntVal: Int get() = 1
val String.xTopLevelStringVal: String get() = "1"
val Any.xTopLevelValOnAny: Int get() = 1
val Int.xTopLevelValOnInt: Int get() = 1
// EXIST: { lookupString:"xTopLevelIntVal", itemText:"xTopLevelIntVal", tailText: " for String in <root>", typeText: "Int" }
// NOTHING_ELSE
@@ -0,0 +1,7 @@
fun foo(p: (() -> Unit) -> Runnable){}
fun bar(){
foo(::<caret>)
}
// EXIST: Runnable
@@ -595,6 +595,24 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/smart/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("EmptyQualifier1.kt")
public void testEmptyQualifier1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/EmptyQualifier1.kt");
doTest(fileName);
}
@TestMetadata("EmptyQualifier2.kt")
public void testEmptyQualifier2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/EmptyQualifier2.kt");
doTest(fileName);
}
@TestMetadata("EmptyQualifier3.kt")
public void testEmptyQualifier3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/EmptyQualifier3.kt");
doTest(fileName);
}
@TestMetadata("NoQualifier1.kt")
public void testNoQualifier1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/NoQualifier1.kt");
@@ -642,6 +660,18 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/NoQualifier9.kt");
doTest(fileName);
}
@TestMetadata("NonEmptyQualifier1.kt")
public void testNonEmptyQualifier1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/NonEmptyQualifier1.kt");
doTest(fileName);
}
@TestMetadata("NonEmptyQualifier2.kt")
public void testNonEmptyQualifier2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/NonEmptyQualifier2.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/smart/constructor")
@@ -113,6 +113,30 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest(fileName);
}
@TestMetadata("CallableReference1.kt")
public void testCallableReference1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/CallableReference1.kt");
doTest(fileName);
}
@TestMetadata("CallableReference2.kt")
public void testCallableReference2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/CallableReference2.kt");
doTest(fileName);
}
@TestMetadata("CallableReference3.kt")
public void testCallableReference3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/CallableReference3.kt");
doTest(fileName);
}
@TestMetadata("CallableReference4.kt")
public void testCallableReference4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/CallableReference4.kt");
doTest(fileName);
}
@TestMetadata("ClassInClassObject.kt")
public void testClassInClassObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ClassInClassObject.kt");
@@ -359,18 +383,6 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest(fileName);
}
@TestMetadata("FunctionReference1.kt")
public void testFunctionReference1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/FunctionReference1.kt");
doTest(fileName);
}
@TestMetadata("FunctionReference2.kt")
public void testFunctionReference2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/FunctionReference2.kt");
doTest(fileName);
}
@TestMetadata("GenericFunction.kt")
public void testGenericFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/GenericFunction.kt");