Skip to content

Commit 094125c

Browse files
committed
KT-19154: Fix auto-import to check receiver for members properly
#KT-19154 fixed
1 parent f313001 commit 094125c

File tree

10 files changed

+141
-26
lines changed

10 files changed

+141
-26
lines changed

idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import com.intellij.psi.PsiElement
2020
import org.jetbrains.kotlin.descriptors.*
2121
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
2222
import org.jetbrains.kotlin.lexer.KtTokens
23+
import org.jetbrains.kotlin.name.FqName
2324
import org.jetbrains.kotlin.psi.*
2425
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
2526
import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression
2627
import org.jetbrains.kotlin.psi.psiUtil.isPackageDirectiveExpression
2728
import org.jetbrains.kotlin.resolve.BindingContext
2829
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
30+
import org.jetbrains.kotlin.resolve.calls.checkers.DslScopeViolationCallChecker.extractDslMarkerFqNames
2931
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
3032
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
3133
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager
@@ -328,3 +330,20 @@ private fun receiverValueTypes(
328330
listOf(receiverValue.type)
329331
}
330332
}
333+
334+
335+
fun Collection<ReceiverType>.shadowedByDslMarkers(): Set<ReceiverType> {
336+
val typesByDslScopes = LinkedHashMap<FqName, MutableList<ReceiverType>>()
337+
338+
this
339+
.mapNotNull { receiver ->
340+
val dslMarkers = receiver.type.extractDslMarkerFqNames()
341+
(receiver to dslMarkers).takeIf { dslMarkers.isNotEmpty() }
342+
}
343+
.forEach { (v, dslMarkers) -> dslMarkers.forEach { typesByDslScopes.getOrPut(it, { mutableListOf() }) += v } }
344+
345+
val shadowedDslReceivers = mutableSetOf<ReceiverType>()
346+
typesByDslScopes.flatMapTo(shadowedDslReceivers) { (_, v) -> v.asSequence().drop(1).asIterable() }
347+
348+
return shadowedDslReceivers
349+
}

idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -403,20 +403,7 @@ abstract class CompletionSession(
403403
}
404404

405405
if (receiverTypes != null && nameExpression.languageVersionSettings.supportsFeature(LanguageFeature.DslMarkersSupport)) {
406-
407-
val typesByDslScopes = LinkedHashMap<FqName, MutableList<ReceiverType>>()
408-
409-
receiverTypes
410-
.mapNotNull { receiver ->
411-
val dslMarkers = receiver.type.extractDslMarkerFqNames()
412-
(receiver to dslMarkers).takeIf { dslMarkers.isNotEmpty() }
413-
}
414-
.forEach { (v, dslMarkers) -> dslMarkers.forEach { typesByDslScopes.getOrPut(it, { mutableListOf() }) += v } }
415-
416-
val shadowedDslReceivers = mutableSetOf<ReceiverType>()
417-
typesByDslScopes.flatMapTo(shadowedDslReceivers) { (_, v) -> v.asSequence().drop(1).asIterable() }
418-
419-
receiverTypes -= shadowedDslReceivers
406+
receiverTypes -= receiverTypes.shadowedByDslMarkers()
420407
}
421408

422409
return receiverTypes

idea/src/org/jetbrains/kotlin/idea/quickfix/ImportFix.kt

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.intellij.psi.PsiErrorElement
3131
import com.intellij.psi.PsiFile
3232
import com.intellij.psi.PsiModifier
3333
import com.intellij.psi.util.PsiModificationTracker
34+
import org.jetbrains.kotlin.config.LanguageFeature
3435
import org.jetbrains.kotlin.descriptors.*
3536
import org.jetbrains.kotlin.diagnostics.Diagnostic
3637
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
@@ -51,10 +52,9 @@ import org.jetbrains.kotlin.idea.core.isVisible
5152
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
5253
import org.jetbrains.kotlin.idea.imports.importableFqName
5354
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
55+
import org.jetbrains.kotlin.idea.project.languageVersionSettings
5456
import org.jetbrains.kotlin.idea.references.mainReference
55-
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
56-
import org.jetbrains.kotlin.idea.util.getResolutionScope
57-
import org.jetbrains.kotlin.idea.util.receiverTypes
57+
import org.jetbrains.kotlin.idea.util.*
5858
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
5959
import org.jetbrains.kotlin.js.resolve.JsPlatform
6060
import org.jetbrains.kotlin.name.FqName
@@ -74,6 +74,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScope
7474
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
7575
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
7676
import org.jetbrains.kotlin.util.OperatorNameConventions
77+
import org.jetbrains.kotlin.utils.ifEmpty
7778
import java.util.*
7879

7980
/**
@@ -300,17 +301,27 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix
300301
indicesHelper.getKotlinEnumsByName(name).filterTo(result, filterByCallType)
301302

302303
val resolutionFacade = element.getResolutionFacade()
303-
val actualReceiverTypes =
304-
callTypeAndReceiver.receiverTypes(bindingContext, element, resolutionFacade.moduleDescriptor, resolutionFacade, false).orEmpty()
304+
var actualReceiverTypes = callTypeAndReceiver
305+
.receiverTypesWithIndex(bindingContext, element,
306+
resolutionFacade.moduleDescriptor, resolutionFacade,
307+
stableSmartCastsOnly = false,
308+
withImplicitReceiversWhenExplicitPresent = true).orEmpty()
309+
310+
if (element.languageVersionSettings.supportsFeature(LanguageFeature.DslMarkersSupport)) {
311+
actualReceiverTypes -= actualReceiverTypes.shadowedByDslMarkers()
312+
}
305313

306-
val processor = { descriptor: CallableDescriptor ->
307-
if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor)) {
308-
val extensionReceiverType = descriptor.extensionReceiverParameter?.type
314+
val explicitReceiverTypes = actualReceiverTypes.filterNot { it.implicit }
309315

310-
if ((actualReceiverTypes.isEmpty() && extensionReceiverType == null) ||
311-
(extensionReceiverType != null && actualReceiverTypes.any { it.isSubtypeOf(extensionReceiverType) })) {
312-
result.add(descriptor)
313-
}
316+
val checkDispatchReceiver = when(callTypeAndReceiver) {
317+
is CallTypeAndReceiver.OPERATOR, is CallTypeAndReceiver.INFIX -> true
318+
else -> false
319+
}
320+
321+
val processor = { descriptor: CallableDescriptor ->
322+
if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor)
323+
&& descriptor.isValidByReceiversFor(explicitReceiverTypes, actualReceiverTypes, checkDispatchReceiver)) {
324+
result.add(descriptor)
314325
}
315326
}
316327

@@ -331,6 +342,18 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix
331342
}
332343

333344

345+
private fun CallableDescriptor.isValidByReceiversFor(explicitReceiverTypes: Collection<ReceiverType>,
346+
allReceiverTypes: Collection<ReceiverType>,
347+
checkDispatchReceiver: Boolean): Boolean {
348+
val bothReceivers = listOfNotNull(extensionReceiverParameter, dispatchReceiverParameter.takeIf { checkDispatchReceiver })
349+
350+
val receiverTypesPerReceiver = generateSequence(explicitReceiverTypes.ifEmpty { allReceiverTypes }) { allReceiverTypes }
351+
352+
return bothReceivers
353+
.zip(receiverTypesPerReceiver.asIterable())
354+
.all { (receiver, possibleTypes) -> possibleTypes.any { it.type.isSubtypeOf(receiver.type) } }
355+
}
356+
334357
override fun fillCandidates(
335358
name: String,
336359
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import BExtSpace.aaa
2+
3+
// "Import" "true"
4+
// WITH_RUNTIME
5+
// ERROR: Unresolved reference: aaa
6+
7+
fun test() {
8+
AAA().apply {
9+
sub {
10+
aaa<caret>()
11+
}
12+
}
13+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
@DslMarker
3+
annotation class DSL
4+
5+
@DSL
6+
class AAA {
7+
fun sub(l: BBB.() -> Unit) {
8+
l(BBB())
9+
}
10+
}
11+
12+
@DSL
13+
class BBB
14+
15+
object AExtSpace {
16+
fun AAA.aaa() {
17+
18+
}
19+
}
20+
21+
object BExtSpace {
22+
fun BBB.aaa() {
23+
24+
}
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// "Import" "true"
2+
// WITH_RUNTIME
3+
// ERROR: Unresolved reference: aaa
4+
5+
fun test() {
6+
AAA().apply {
7+
sub {
8+
aaa<caret>()
9+
}
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// "Import" "true"
2+
// ERROR: Unresolved reference: foobar
3+
package p2
4+
5+
import p1.Some.foobar
6+
7+
class A {
8+
fun some() {
9+
foobar<caret>()
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package p1
2+
3+
object Some {
4+
fun foobar() {}
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// "Import" "true"
2+
// ERROR: Unresolved reference: foobar
3+
package p2
4+
5+
class A {
6+
fun some() {
7+
foobar<caret>()
8+
}
9+
}

idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ public void testDivOperator() throws Exception {
131131
doTestWithExtraFile(fileName);
132132
}
133133

134+
@TestMetadata("dslMarkers.before.Main.kt")
135+
public void testDslMarkers() throws Exception {
136+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/dslMarkers.before.Main.kt");
137+
doTestWithExtraFile(fileName);
138+
}
139+
134140
@TestMetadata("extensionFunctionImport.before.Main.kt")
135141
public void testExtensionFunctionImport() throws Exception {
136142
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/extensionFunctionImport.before.Main.kt");
@@ -545,6 +551,12 @@ public void testObjectImport() throws Exception {
545551
doTestWithExtraFile(fileName);
546552
}
547553

554+
@TestMetadata("objectMemberFunctionImportWhenReceiverPresent.before.Main.kt")
555+
public void testObjectMemberFunctionImportWhenReceiverPresent() throws Exception {
556+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/objectMemberFunctionImportWhenReceiverPresent.before.Main.kt");
557+
doTestWithExtraFile(fileName);
558+
}
559+
548560
@TestMetadata("operatorAssignPlus.test")
549561
public void testOperatorAssignPlus() throws Exception {
550562
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/operatorAssignPlus.test");

0 commit comments

Comments
 (0)