KT-5072 Values of extension function type are not seen in completion
#KT-5072 Fixed
This commit is contained in:
@@ -34,13 +34,13 @@ import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.nullability
|
||||
|
||||
public fun CallableDescriptor.substituteExtensionIfCallable(
|
||||
public fun <TCallable : CallableDescriptor> TCallable.substituteExtensionIfCallable(
|
||||
receivers: Collection<ReceiverValue>,
|
||||
context: BindingContext,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
callType: CallType<*>,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): Collection<CallableDescriptor> {
|
||||
): Collection<TCallable> {
|
||||
val sequence = receivers.asSequence().flatMap { substituteExtensionIfCallable(it, callType, context, dataFlowInfo, containingDeclarationOrModule).asSequence() }
|
||||
if (getTypeParameters().isEmpty()) { // optimization for non-generic callables
|
||||
return sequence.firstOrNull()?.let { listOf(it) } ?: listOf()
|
||||
@@ -50,32 +50,32 @@ public fun CallableDescriptor.substituteExtensionIfCallable(
|
||||
}
|
||||
}
|
||||
|
||||
public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(
|
||||
public fun <TCallable : CallableDescriptor> TCallable.substituteExtensionIfCallableWithImplicitReceiver(
|
||||
scope: LexicalScope,
|
||||
context: BindingContext,
|
||||
dataFlowInfo: DataFlowInfo
|
||||
): Collection<CallableDescriptor> {
|
||||
): Collection<TCallable> {
|
||||
val receiverValues = scope.getImplicitReceiversWithInstance().map { it.getValue() }
|
||||
return substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, CallType.DEFAULT, scope.ownerDescriptor)
|
||||
}
|
||||
|
||||
public fun CallableDescriptor.substituteExtensionIfCallable(
|
||||
public fun <TCallable : CallableDescriptor> TCallable.substituteExtensionIfCallable(
|
||||
receiver: ReceiverValue,
|
||||
callType: CallType<*>,
|
||||
bindingContext: BindingContext,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): Collection<CallableDescriptor> {
|
||||
): Collection<TCallable> {
|
||||
if (!receiver.exists()) return listOf()
|
||||
|
||||
var types = SmartCastManager().getSmartCastVariants(receiver, bindingContext, containingDeclarationOrModule, dataFlowInfo)
|
||||
return substituteExtensionIfCallable(types, callType)
|
||||
}
|
||||
|
||||
public fun CallableDescriptor.substituteExtensionIfCallable(
|
||||
public fun <TCallable : CallableDescriptor> TCallable.substituteExtensionIfCallable(
|
||||
receiverTypes: Collection<KotlinType>,
|
||||
callType: CallType<*>
|
||||
): Collection<CallableDescriptor> {
|
||||
): Collection<TCallable> {
|
||||
if (!callType.descriptorKindFilter.accepts(this)) return listOf()
|
||||
|
||||
var types = receiverTypes.asSequence()
|
||||
@@ -98,7 +98,7 @@ public fun CallableDescriptor.substituteExtensionIfCallable(
|
||||
return if (substitutors.any()) listOf(this) else listOf()
|
||||
}
|
||||
else {
|
||||
return substitutors.map { substitute(it)!! }.toList()
|
||||
return substitutors.map { @Suppress("UNCHECKED_CAST") (substitute(it) as TCallable) }.toList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -207,6 +207,15 @@ class BasicCompletionSession(
|
||||
|
||||
val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position)
|
||||
withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory ->
|
||||
if (receiverTypes != null) {
|
||||
ExtensionFunctionTypeValueCompletion(receiverTypes, callTypeAndReceiver.callType, lookupElementFactory)
|
||||
.processVariables(contextVariablesProvider)
|
||||
.forEach {
|
||||
val lookupElements = it.factory.createStandardLookupElementsForDescriptor(it.invokeDescriptor, useReceiverTypes = true)
|
||||
collector.addElements(lookupElements)
|
||||
}
|
||||
}
|
||||
|
||||
if (contextVariableTypesForSmartCompletion.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
|
||||
completeWithSmartCompletion(lookupElementFactory)
|
||||
}
|
||||
|
||||
+3
-2
@@ -37,7 +37,8 @@ class RealContextVariablesProvider(
|
||||
private val referenceVariantsHelper: ReferenceVariantsHelper,
|
||||
private val contextElement: PsiElement
|
||||
) : ContextVariablesProvider {
|
||||
private val functionTypeVariables by lazy {
|
||||
|
||||
val allFunctionTypeVariables by lazy {
|
||||
collectVariables().filter { KotlinBuiltIns.isFunctionOrExtensionFunctionType(it.type) }
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ class RealContextVariablesProvider(
|
||||
|
||||
override fun functionTypeVariables(requiredType: FuzzyType): Collection<Pair<VariableDescriptor, TypeSubstitutor>> {
|
||||
val result = SmartList<Pair<VariableDescriptor, TypeSubstitutor>>()
|
||||
for (variable in functionTypeVariables) {
|
||||
for (variable in allFunctionTypeVariables) {
|
||||
val substitutor = variable.fuzzyReturnType()?.checkIsSubtypeOf(requiredType) ?: continue
|
||||
result.add(variable to substitutor)
|
||||
}
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.createSynthesizedInvokes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
class ExtensionFunctionTypeValueCompletion(
|
||||
private val receiverTypes: Collection<KotlinType>,
|
||||
private val callType: CallType<*>,
|
||||
private val lookupElementFactory: LookupElementFactory
|
||||
) {
|
||||
data class Result(val invokeDescriptor: FunctionDescriptor, val factory: AbstractLookupElementFactory)
|
||||
|
||||
fun processVariables(variablesProvider: RealContextVariablesProvider): Collection<Result> {
|
||||
if (callType != CallType.DOT && callType != CallType.SAFE) return emptyList()
|
||||
|
||||
val results = ArrayList<Result>()
|
||||
|
||||
for (variable in variablesProvider.allFunctionTypeVariables) {
|
||||
val variableType = variable.type
|
||||
if (!KotlinBuiltIns.isExtensionFunctionType(variableType)) continue
|
||||
|
||||
val invokes = variableType.memberScope.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_IDE)
|
||||
for (invoke in createSynthesizedInvokes(invokes)) {
|
||||
for (substituted in invoke.substituteExtensionIfCallable(receiverTypes, callType)) {
|
||||
val factory = object : AbstractLookupElementFactory {
|
||||
override fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
|
||||
if (!useReceiverTypes) return emptyList()
|
||||
descriptor as FunctionDescriptor // should be descriptor for "invoke"
|
||||
|
||||
val invokeLookupElement = lookupElementFactory.createLookupElement(substituted, useReceiverTypes = true)
|
||||
val variableLookupElement = lookupElementFactory.createLookupElement(variable, useReceiverTypes = false)
|
||||
val insertHandler = lookupElementFactory.insertHandlerProvider.insertHandler(invoke)
|
||||
|
||||
val lookupElement = object : LookupElementDecorator<LookupElement>(variableLookupElement) {
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
invokeLookupElement.renderElement(presentation)
|
||||
|
||||
presentation.itemText = variable.name.asString()
|
||||
|
||||
val parameterTail = presentation.tailFragments.first()
|
||||
presentation.clearTail()
|
||||
presentation.appendTailText(parameterTail.text, false)
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext?) {
|
||||
insertHandler.handleInsert(context, this)
|
||||
}
|
||||
}
|
||||
return listOf(lookupElement)
|
||||
}
|
||||
|
||||
override fun createLookupElement(
|
||||
descriptor: DeclarationDescriptor,
|
||||
useReceiverTypes: Boolean,
|
||||
qualifyNestedClasses: Boolean,
|
||||
includeClassTypeArguments: Boolean,
|
||||
parametersAndTypeGrayed: Boolean): LookupElement? = null
|
||||
}
|
||||
|
||||
results.add(Result(substituted, factory))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
}
|
||||
+8
@@ -97,6 +97,14 @@ class SmartCompletionSession(
|
||||
|
||||
val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position)
|
||||
withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory ->
|
||||
if (filter != null && receiverTypes != null) {
|
||||
val results = ExtensionFunctionTypeValueCompletion(receiverTypes, callTypeAndReceiver.callType, lookupElementFactory)
|
||||
.processVariables(contextVariablesProvider)
|
||||
for ((invokeDescriptor, factory) in results) {
|
||||
collector.addElements(filter(invokeDescriptor, factory))
|
||||
}
|
||||
}
|
||||
|
||||
if (contextVariableTypesForAdditionalItems.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
|
||||
val additionalItems = smartCompletion!!.additionalItems(lookupElementFactory).first
|
||||
collector.addElements(additionalItems)
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
class C
|
||||
|
||||
fun C.test(foo: C.() -> Unit) {
|
||||
fo<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "foo", itemText: "foo", tailText: null, typeText: "C.() -> Unit" }
|
||||
// ABSENT: { itemText: "foo", typeText: "Unit" }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun test(i: Int?, foo: Int.(String) -> Char) {
|
||||
i?.fo<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "foo", itemText: "foo", tailText: "(String)", typeText: "Char", attributes: "bold" }
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun test(i: Int, foo: Int.(String) -> Char, fooAny: Any.() -> Unit) {
|
||||
i.fo<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "foo", itemText: "foo", tailText: "(String)", typeText: "Char", attributes: "bold" }
|
||||
// EXIST: { lookupString: "fooAny", itemText: "fooAny", tailText: "()", typeText: "Unit", attributes: "" }
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
interface I
|
||||
interface J
|
||||
|
||||
fun test(p: I, fooI: I.() -> Unit, fooJ: J.() -> Unit) {
|
||||
if (p is J) {
|
||||
p.fo<caret>
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "fooI", itemText: "fooI", tailText: "()", typeText: "Unit", attributes: "bold" }
|
||||
// EXIST: { lookupString: "fooJ", itemText: "fooJ", tailText: "()", typeText: "Unit", attributes: "bold" }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun test(i: Int, foo: Int.() -> Char) {
|
||||
i.<caret>
|
||||
}
|
||||
|
||||
// ELEMENT: foo
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun test(i: Int, foo: Int.() -> Char) {
|
||||
i.foo()<caret>
|
||||
}
|
||||
|
||||
// ELEMENT: foo
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun test(i: Int, foo: Int.(String) -> Char) {
|
||||
i.<caret>
|
||||
}
|
||||
|
||||
// ELEMENT: foo
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun test(i: Int, foo: Int.(String) -> Char) {
|
||||
i.foo(<caret>)
|
||||
}
|
||||
|
||||
// ELEMENT: foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test(i: Int, foo: Int.() -> Char) {
|
||||
bar(i.<caret>)
|
||||
}
|
||||
|
||||
fun bar(p1: Char, p2: Int){}
|
||||
|
||||
// ELEMENT: foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test(i: Int, foo: Int.() -> Char) {
|
||||
bar(i.foo(), <caret>)
|
||||
}
|
||||
|
||||
fun bar(p1: Char, p2: Int){}
|
||||
|
||||
// ELEMENT: foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test(i: Int, foo: Int.(String) -> Char) {
|
||||
bar(i.<caret>)
|
||||
}
|
||||
|
||||
fun bar(p1: Char, p2: Int){}
|
||||
|
||||
// ELEMENT: foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test(i: Int, foo: Int.(String) -> Char) {
|
||||
bar(i.foo(<caret>), )
|
||||
}
|
||||
|
||||
fun bar(p1: Char, p2: Int){}
|
||||
|
||||
// ELEMENT: foo
|
||||
@@ -0,0 +1,7 @@
|
||||
fun test(i: Int, foo1: Int.(String) -> Char, foo2: Int.() -> Int, foo3: String.() -> Char): Char {
|
||||
return i.<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "foo1", itemText: "foo1", tailText: "(String)", typeText: "Char", attributes: "bold" }
|
||||
// ABSENT: foo2
|
||||
// ABSENT: foo3
|
||||
+33
@@ -1201,6 +1201,39 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ExtensionFunctionTypeValues extends AbstractJSBasicCompletionTest {
|
||||
public void testAllFilesPresentInExtensionFunctionTypeValues() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("ImplicitReceiver.kt")
|
||||
public void testImplicitReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/ImplicitReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SafeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/SafeCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/Simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SmartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/SmartCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensions")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+33
@@ -1201,6 +1201,39 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ExtensionFunctionTypeValues extends AbstractJvmBasicCompletionTest {
|
||||
public void testAllFilesPresentInExtensionFunctionTypeValues() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("ImplicitReceiver.kt")
|
||||
public void testImplicitReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/ImplicitReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SafeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/SafeCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/Simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SmartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues/SmartCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensions")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+6
@@ -131,6 +131,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunctionTypeVariables.kt")
|
||||
public void testExtensionFunctionTypeVariables() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/ExtensionFunctionTypeVariables.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("FilterTo.kt")
|
||||
public void testFilterTo() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/FilterTo.kt");
|
||||
|
||||
+12
@@ -71,6 +71,18 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunctionTypeVariable1.kt")
|
||||
public void testExtensionFunctionTypeVariable1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/ExtensionFunctionTypeVariable1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunctionTypeVariable2.kt")
|
||||
public void testExtensionFunctionTypeVariable2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/ExtensionFunctionTypeVariable2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionReceiverTypeArg.kt")
|
||||
public void testExtensionReceiverTypeArg() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/ExtensionReceiverTypeArg.kt");
|
||||
|
||||
+12
@@ -383,6 +383,18 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunctionTypeVariable1.kt")
|
||||
public void testExtensionFunctionTypeVariable1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ExtensionFunctionTypeVariable1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunctionTypeVariable2.kt")
|
||||
public void testExtensionFunctionTypeVariable2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ExtensionFunctionTypeVariable2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ForLoopRange.kt")
|
||||
public void testForLoopRange() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ForLoopRange.kt");
|
||||
|
||||
Reference in New Issue
Block a user