KT-17074: Make completion respect DslMarker's
#KT-17074 fixed
This commit is contained in:
@@ -27,6 +27,7 @@ import com.intellij.patterns.PatternCondition
|
||||
import com.intellij.patterns.StandardPatterns
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.ProcessingContext
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ModuleOrigin
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.OriginCapability
|
||||
@@ -36,10 +37,13 @@ import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||
import org.jetbrains.kotlin.idea.core.*
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.DslScopeViolationCallChecker.extractDslMarkerFqNames
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
@@ -48,6 +52,7 @@ import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import java.util.*
|
||||
import kotlin.collections.LinkedHashMap
|
||||
|
||||
class CompletionSessionConfiguration(
|
||||
val useBetterPrefixMatcherForNonImportedClasses: Boolean,
|
||||
@@ -374,12 +379,30 @@ abstract class CompletionSession(
|
||||
callTypeAndReceiver: CallTypeAndReceiver<*, *>): Collection<ReceiverType>? {
|
||||
var receiverTypes = callTypeAndReceiver.receiverTypesWithIndex(
|
||||
bindingContext, nameExpression, moduleDescriptor, resolutionFacade,
|
||||
stableSmartCastsOnly = true /* we don't include smart cast receiver types for "unstable" receiver value to mark members grayed */)
|
||||
stableSmartCastsOnly = true, /* we don't include smart cast receiver types for "unstable" receiver value to mark members grayed */
|
||||
withImplicitReceiversWhenExplicitPresent = true)
|
||||
|
||||
if (callTypeAndReceiver is CallTypeAndReceiver.SAFE || isDebuggerContext) {
|
||||
receiverTypes = receiverTypes?.map { ReceiverType(it.type.makeNotNullable(), it.receiverIndex) }
|
||||
}
|
||||
|
||||
if (receiverTypes != null && nameExpression.languageVersionSettings.supportsFeature(LanguageFeature.DslMarkersSupport)) {
|
||||
|
||||
val typesByDslScopes = LinkedHashMap<FqName, MutableList<ReceiverType>>()
|
||||
|
||||
receiverTypes
|
||||
.mapNotNull { receiver ->
|
||||
val dslMarkers = receiver.type.extractDslMarkerFqNames()
|
||||
(receiver to dslMarkers).takeIf { dslMarkers.isNotEmpty() }
|
||||
}
|
||||
.forEach { (v, dslMarkers) -> dslMarkers.forEach { typesByDslScopes.getOrPut(it, { mutableListOf() }) += v } }
|
||||
|
||||
val shadowedDslReceivers = mutableSetOf<ReceiverType>()
|
||||
typesByDslScopes.flatMapTo(shadowedDslReceivers) { (_, v) -> v.asSequence().drop(1).asIterable() }
|
||||
|
||||
receiverTypes -= shadowedDslReceivers
|
||||
}
|
||||
|
||||
return receiverTypes
|
||||
}
|
||||
}
|
||||
|
||||
+22
-3
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
|
||||
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -47,6 +48,7 @@ import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
import java.util.*
|
||||
|
||||
interface AbstractLookupElementFactory {
|
||||
@@ -347,9 +349,26 @@ class LookupElementFactory(
|
||||
receiverTypes: Collection<ReceiverType>,
|
||||
onReceiverTypeMismatch: CallableWeight?
|
||||
): CallableWeight? {
|
||||
val receiverParameter = extensionReceiverParameter
|
||||
?: dispatchReceiverParameter
|
||||
?: return null
|
||||
|
||||
val bothReceivers = listOfNotNull(extensionReceiverParameter, dispatchReceiverParameter)
|
||||
|
||||
val receiverTypesForFirstReceiver = receiverTypes.filterNot { it.implicit }.ifEmpty { receiverTypes }
|
||||
|
||||
val weights = bothReceivers.zip(generateSequence(receiverTypesForFirstReceiver) { receiverTypes }.asIterable())
|
||||
.map { (receiverParameter, receiverTypes) ->
|
||||
callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch, receiverParameter)
|
||||
}
|
||||
|
||||
if (weights.any { it == onReceiverTypeMismatch }) return onReceiverTypeMismatch
|
||||
return weights.firstOrNull()
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.callableWeightBasedOnReceiver(
|
||||
receiverTypes: Collection<ReceiverType>,
|
||||
onReceiverTypeMismatch: CallableWeight?,
|
||||
receiverParameter: ReceiverParameterDescriptor
|
||||
): CallableWeight? {
|
||||
if ((receiverParameter.value as? TransientReceiver)?.type?.isFunctionType == true) return null
|
||||
|
||||
val matchingReceiverIndices = HashSet<Int>()
|
||||
var bestReceiverType: ReceiverType? = null
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
@DslMarker
|
||||
annotation class SimpleDsl
|
||||
|
||||
@SimpleDsl
|
||||
class DslRoot {
|
||||
fun container(body: DslContainer.() -> Unit) {
|
||||
body(DslContainer())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslContainer {
|
||||
fun child(body: DslChild.() -> Unit) {
|
||||
body(DslChild())
|
||||
}
|
||||
|
||||
fun otherChild(body: DslOtherChild.() -> Unit) {
|
||||
body(DslOtherChild())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslChild {
|
||||
operator fun String.unaryMinus() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
@DslMarker
|
||||
annotation class SimpleOtherDsl
|
||||
|
||||
@SimpleOtherDsl
|
||||
class DslOtherChild {
|
||||
fun otherThing() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun dsl(body: DslRoot.() -> Unit) {
|
||||
body(DslRoot())
|
||||
}
|
||||
|
||||
fun test() {
|
||||
dsl {
|
||||
container {
|
||||
child {
|
||||
-"Some"
|
||||
|
||||
}
|
||||
otherChild {
|
||||
otherThing()
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: {"lookupString":"otherThing", "attributes": ["bold"]}
|
||||
// EXIST: {"lookupString":"child", "attributes": ["bold"]}
|
||||
// EXIST: {"lookupString":"otherChild", "attributes": ["bold"]}
|
||||
@@ -0,0 +1,47 @@
|
||||
@DslMarker
|
||||
annotation class SimpleDsl
|
||||
|
||||
class DslRoot {
|
||||
|
||||
}
|
||||
|
||||
interface DslContainer
|
||||
|
||||
@SimpleDsl
|
||||
class DslContainerOne : DslContainer {
|
||||
fun DslContainer.one() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslContainerTwo : DslContainer {
|
||||
fun DslContainer.two() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun DslRoot.containerOne(body: DslContainerOne.() -> Unit) {
|
||||
body(DslContainerOne())
|
||||
}
|
||||
|
||||
fun DslRoot.containerTwo(body: DslContainerTwo.() -> Unit) {
|
||||
body(DslContainerTwo())
|
||||
}
|
||||
|
||||
fun dsl(body: DslRoot.() -> Unit) {
|
||||
body(DslRoot())
|
||||
}
|
||||
|
||||
fun test() {
|
||||
dsl {
|
||||
containerOne {
|
||||
containerTwo {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: {"lookupString":"two", "attributes":""}
|
||||
// EXIST: {"lookupString":"one", "attributes":["grayed"]}
|
||||
@@ -0,0 +1,41 @@
|
||||
@DslMarker
|
||||
annotation class SimpleDsl
|
||||
|
||||
@SimpleDsl
|
||||
class DslRoot {
|
||||
fun container(body: DslContainer.() -> Unit) {
|
||||
body(DslContainer())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslContainer {
|
||||
fun child(body: DslChild.() -> Unit) {
|
||||
body(DslChild())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslChild {
|
||||
operator fun String.unaryMinus() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun dsl(body: DslRoot.() -> Unit) {
|
||||
body(DslRoot())
|
||||
}
|
||||
|
||||
fun test() {
|
||||
dsl {
|
||||
container {
|
||||
child {
|
||||
-"Some"
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: {"lookupString":"container", "attributes":["grayed"]}
|
||||
// EXIST: {"lookupString":"child", "attributes":["grayed"]}
|
||||
@@ -0,0 +1,71 @@
|
||||
@DslMarker
|
||||
annotation class SimpleDsl
|
||||
|
||||
@SimpleDsl
|
||||
class DslRoot {
|
||||
fun container(body: DslContainer.() -> Unit) {
|
||||
body(DslContainer())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslContainer {
|
||||
fun child(body: DslChild.() -> Unit) {
|
||||
body(DslChild())
|
||||
}
|
||||
|
||||
fun otherChild(body: DslOtherChild.() -> Unit) {
|
||||
body(DslOtherChild())
|
||||
}
|
||||
|
||||
fun anotherChild(body: DslAnotherChild.() -> Unit) {
|
||||
body(DslAnotherChild())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslChild {
|
||||
operator fun String.unaryMinus() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
@DslMarker
|
||||
annotation class SimpleOtherDsl
|
||||
|
||||
@SimpleOtherDsl
|
||||
class DslOtherChild {
|
||||
fun otherThing() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleOtherDsl @SimpleDsl
|
||||
class DslAnotherChild {
|
||||
|
||||
}
|
||||
|
||||
fun dsl(body: DslRoot.() -> Unit) {
|
||||
body(DslRoot())
|
||||
}
|
||||
|
||||
fun test() {
|
||||
dsl {
|
||||
container {
|
||||
child {
|
||||
-"Some"
|
||||
|
||||
}
|
||||
otherChild {
|
||||
otherThing()
|
||||
anotherChild {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: {lookupString:"otherThing", attributes:["grayed"]}
|
||||
// EXIST: {lookupString:"child", attributes:["grayed"]}
|
||||
// EXIST: {lookupString:"otherChild", attributes:["grayed"]}
|
||||
@@ -0,0 +1,38 @@
|
||||
@DslMarker
|
||||
annotation class SimpleDsl
|
||||
|
||||
@SimpleDsl
|
||||
class DslRoot {
|
||||
fun container(body: DslContainer.() -> Unit) {
|
||||
body(DslContainer())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslContainer {
|
||||
fun child(body: DslChild.() -> Unit) {
|
||||
body(DslChild())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslChild {
|
||||
operator fun String.unaryMinus() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun dsl(body: DslRoot.() -> Unit) {
|
||||
body(DslRoot())
|
||||
}
|
||||
|
||||
fun test() {
|
||||
dsl {
|
||||
container {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: {"lookupString":"child", "attributes":["bold"]}
|
||||
// EXIST: {"lookupString":"container", "attributes":["grayed"]}
|
||||
@@ -0,0 +1,35 @@
|
||||
@DslMarker
|
||||
annotation class SimpleDsl
|
||||
|
||||
@SimpleDsl
|
||||
class DslRoot {
|
||||
fun container(body: DslContainer.() -> Unit) {
|
||||
body(DslContainer())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslContainer {
|
||||
fun child(body: DslChild.() -> Unit) {
|
||||
body(DslChild())
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleDsl
|
||||
class DslChild {
|
||||
operator fun String.unaryMinus() {
|
||||
println(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun dsl(body: DslRoot.() -> Unit) {
|
||||
body(DslRoot())
|
||||
}
|
||||
|
||||
fun test() {
|
||||
dsl {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: {"lookupString":"container", "attributes":["bold"]}
|
||||
+45
@@ -1310,6 +1310,51 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/dslMarker")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DslMarker extends AbstractJSBasicCompletionTest {
|
||||
@TestMetadata("2dslsInsideOtherChild.kt")
|
||||
public void test2dslsInsideOtherChild() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/2dslsInsideOtherChild.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("2receivers.kt")
|
||||
public void test2receivers() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/2receivers.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDslMarker() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/dslMarker"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("child.kt")
|
||||
public void testChild() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/child.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compositeDsl.kt")
|
||||
public void testCompositeDsl() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/compositeDsl.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("container.kt")
|
||||
public void testContainer() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/container.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("root.kt")
|
||||
public void testRoot() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/root.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+45
@@ -1310,6 +1310,51 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/dslMarker")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DslMarker extends AbstractJvmBasicCompletionTest {
|
||||
@TestMetadata("2dslsInsideOtherChild.kt")
|
||||
public void test2dslsInsideOtherChild() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/2dslsInsideOtherChild.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("2receivers.kt")
|
||||
public void test2receivers() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/2receivers.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDslMarker() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/dslMarker"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("child.kt")
|
||||
public void testChild() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/child.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compositeDsl.kt")
|
||||
public void testCompositeDsl() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/compositeDsl.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("container.kt")
|
||||
public void testContainer() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/container.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("root.kt")
|
||||
public void testRoot() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/dslMarker/root.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user