diff --git a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirClassifierCompletionContributor.kt b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirClassifierCompletionContributor.kt index e9a99174a6d2..10fa372b5cfb 100644 --- a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirClassifierCompletionContributor.kt +++ b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/FirClassifierCompletionContributor.kt @@ -10,14 +10,17 @@ import com.intellij.openapi.diagnostic.debug import com.intellij.openapi.diagnostic.logger import com.intellij.psi.SmartPsiElementPointer import com.intellij.psi.createSmartPointer +import com.intellij.psi.search.searches.DirectClassInheritorsSearch import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.concurrency.annotations.RequiresReadLock +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.analyze import org.jetbrains.kotlin.analysis.api.components.KaScopeWithKind import org.jetbrains.kotlin.analysis.api.components.ShortenCommand import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.types.KaClassType +import org.jetbrains.kotlin.analysis.api.types.symbol import org.jetbrains.kotlin.idea.base.psi.replaced import org.jetbrains.kotlin.idea.completion.KotlinFirCompletionParameters import org.jetbrains.kotlin.idea.completion.contributors.helpers.CompletionSymbolOrigin @@ -40,6 +43,7 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.renderer.render import kotlin.reflect.KClass +import kotlin.sequences.emptySequence internal open class FirClassifierCompletionContributor( parameters: KotlinFirCompletionParameters, @@ -134,8 +138,22 @@ internal open class FirClassifierCompletionContributor( context: WeighingContext, ): Sequence { val availableFromScope = mutableSetOf() - val scopeClassifiers = context.scopeContext!! - .scopes + val scopesToCheck = context.scopeContext!!.scopes.toMutableList() + + context.preferredSubtype?.symbol?.let { preferredSubtypeSymbol -> + preferredSubtypeSymbol.staticScope?.let { + if (!scopesToCheck.contains(it)) { + scopesToCheck.add(it) + } + } + preferredSubtypeSymbol.containingSymbol?.staticScope?.let { + if (!scopesToCheck.contains(it)) { + scopesToCheck.add(it) + } + } + } + + val scopeClassifiers = scopesToCheck .asSequence() .flatMap { it.getAvailableClassifiers(positionContext, scopeNameFilter, visibilityChecker) } .filter { filterClassifiers(it.symbol) } diff --git a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/weighers/PreferredSubtypeWeigher.kt b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/weighers/PreferredSubtypeWeigher.kt new file mode 100644 index 000000000000..3cae7b018cae --- /dev/null +++ b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/weighers/PreferredSubtypeWeigher.kt @@ -0,0 +1,40 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.completion.impl.k2.weighers + +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementWeigher +import com.intellij.openapi.util.Key +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.idea.completion.weighers.WeighingContext +import org.jetbrains.kotlin.psi.UserDataProperty + +internal object PreferredSubtypeWeigher { + private const val WEIGHER_ID = "kotlin.preferredSubtype" + + private enum class Weight { + PREFERRED_SUBTYPE, + PREFERRED_EXACT_TYPE, // In this weigher we prefer subtypes over the exact type + UNRELATED + } + + context(KaSession) + fun addWeight(context: WeighingContext, lookupElement: LookupElement, symbol: KaSymbol) { + val actualClassType = (symbol as? KaClassLikeSymbol)?.defaultType ?: return + val preferredSubtype = context.preferredSubtype ?: return + lookupElement.hasPreferredSubtype = if (actualClassType.semanticallyEquals(preferredSubtype)) { + Weight.PREFERRED_EXACT_TYPE + } else if (!actualClassType.isNothingType && actualClassType.isSubtypeOf(preferredSubtype)) { + Weight.PREFERRED_SUBTYPE + } else { + Weight.UNRELATED + } + } + + private var LookupElement.hasPreferredSubtype: Weight? by UserDataProperty(Key("KOTLIN_HAS_PREFERRED_SUBTYPE")) + + object Weigher : LookupElementWeigher(WEIGHER_ID) { + override fun weigh(element: LookupElement): Comparable<*> = element.hasPreferredSubtype ?: Weight.UNRELATED + } +} \ No newline at end of file diff --git a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/weighers/Weighers.kt b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/weighers/Weighers.kt index df82541a392d..d78283e06b5d 100644 --- a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/weighers/Weighers.kt +++ b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/weighers/Weighers.kt @@ -16,7 +16,9 @@ import org.jetbrains.kotlin.analysis.api.resolution.KaAnnotationCall import org.jetbrains.kotlin.analysis.api.resolution.successfulCallOrNull import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolModality import org.jetbrains.kotlin.analysis.api.symbols.markers.KaNamedSymbol +import org.jetbrains.kotlin.analysis.api.types.KaClassType import org.jetbrains.kotlin.analysis.api.types.KaErrorType import org.jetbrains.kotlin.analysis.api.types.KaType import org.jetbrains.kotlin.analysis.api.types.KaTypeNullability @@ -32,6 +34,7 @@ import org.jetbrains.kotlin.idea.completion.contributors.helpers.CompletionSymbo import org.jetbrains.kotlin.idea.completion.contributors.helpers.KtSymbolWithOrigin import org.jetbrains.kotlin.idea.completion.impl.k2.context.getOriginalDeclarationOrSelf import org.jetbrains.kotlin.idea.completion.impl.k2.weighers.K2SoftDeprecationWeigher +import org.jetbrains.kotlin.idea.completion.impl.k2.weighers.PreferredSubtypeWeigher import org.jetbrains.kotlin.idea.completion.impl.k2.weighers.TrailingLambdaParameterNameWeigher import org.jetbrains.kotlin.idea.completion.impl.k2.weighers.TrailingLambdaWeigher import org.jetbrains.kotlin.idea.completion.implCommon.weighers.PreferKotlinClassesWeigher @@ -52,6 +55,7 @@ internal class WeighingContext private constructor( private val myScopeContext: KaScopeContext?, private val myExpectedType: KaType?, private val myActualReceiverTypes: List>, + private val myPreferredSubtype: KaClassType?, val contextualSymbolsCache: ContextualSymbolsCache, val importableFqNameClassifier: ImportableFqNameClassifier, private val mySymbolsToSkip: Set, @@ -62,6 +66,7 @@ internal class WeighingContext private constructor( */ class ContextualSymbolsCache(private val symbolsContainingPosition: Map>) { private val contextualOverriddenSymbols: MutableMap> = mutableMapOf() + context(KaSession) fun symbolIsPresentInContext(symbol: KaCallableSymbol): Boolean = withValidityAssertion { if (symbol !is KaNamedSymbol) return false @@ -88,6 +93,12 @@ internal class WeighingContext private constructor( myExpectedType } + val preferredSubtype: KaClassType? + get() = withValidityAssertion { + myPreferredSubtype + } + + val actualReceiverTypes: List> get() = withValidityAssertion { myActualReceiverTypes } @@ -110,8 +121,9 @@ internal class WeighingContext private constructor( elementInCompletionFile: PsiElement, scopeContext: KaScopeContext? = null, expectedType: KaType? = null, + preferredSubtype: KaClassType? = null, actualReceiverTypes: List> = emptyList(), - symbolsToSkip: Set = emptySet(), + symbolsToSkip: Set = emptySet() ): WeighingContext { val completionFile = parameters.completionFile val defaultImportPaths = completionFile.getDefaultImportPaths(useSiteModule = parameters.useSiteModule).toSet() @@ -128,11 +140,20 @@ internal class WeighingContext private constructor( originalFile = parameters.originalFile, ) ), + myPreferredSubtype = preferredSubtype, importableFqNameClassifier = ImportableFqNameClassifier(completionFile) { defaultImportPaths.hasImport(it) }, mySymbolsToSkip = symbolsToSkip, ) } + // For `is` and `as` operations we want to prefer sealed inheritors + context(KaSession) + fun KtExpression.getPreferredSealedType(): KaClassType? { + val comparisonType = expressionType as? KaClassType ?: return null + if (comparisonType.symbol.modality != KaSymbolModality.SEALED) return null + return comparisonType + } + context(KaSession) fun create( parameters: KotlinFirCompletionParameters, @@ -152,6 +173,25 @@ internal class WeighingContext private constructor( else -> null } + fun KtBinaryExpressionWithTypeRHS.isAsOrSafeAs(): Boolean { + val operationElementType = operationReference.getReferencedNameElementType() + return operationElementType == KtTokens.AS_KEYWORD || operationElementType == KtTokens.AS_SAFE + } + + val preferredSubtype = when (positionContext) { + is KotlinTypeNameReferencePositionContext -> { + val typeReferenceOwner = positionContext.typeReference?.parent + val leftHandExpression = when { + typeReferenceOwner is KtIsExpression -> typeReferenceOwner.leftHandSide + typeReferenceOwner is KtBinaryExpressionWithTypeRHS && typeReferenceOwner.isAsOrSafeAs() -> typeReferenceOwner.left + else -> null + } + leftHandExpression?.getPreferredSealedType() + } + + else -> null + } + val symbolToSkip = when (positionContext) { is KotlinWithSubjectEntryPositionContext -> (positionContext.subjectExpression as? KtSimpleNameExpression) ?.mainReference @@ -174,11 +214,12 @@ internal class WeighingContext private constructor( elementInCompletionFile = positionContext.position, scopeContext = scopeContext, expectedType = expectedType, + preferredSubtype = preferredSubtype, actualReceiverTypes = CallableMetadataProvider.calculateActualReceiverTypes( explicitReceiver = positionContext.explicitReceiver, implicitReceivers = ::implicitReceivers, ), - symbolsToSkip = setOfNotNull(symbolToSkip), + symbolsToSkip = setOfNotNull(symbolToSkip) ) } @@ -275,13 +316,14 @@ internal object Weighers { NotImportedWeigher.addWeight(context, lookupElement, symbol, availableWithoutImport) ClassifierWeigher.addWeight(lookupElement, symbol, symbolWithOrigin.origin) VariableOrFunctionWeigher.addWeight(lookupElement, symbol) + PreferredSubtypeWeigher.addWeight(context, lookupElement, symbol) if (symbol !is KaCallableSymbol) return@also K2SoftDeprecationWeigher.addWeight(lookupElement, symbol, context.languageVersionSettings) PreferContextualCallablesWeigher.addWeight(lookupElement, symbol, context.contextualSymbolsCache) - PreferFewerParametersWeigher. addWeight(lookupElement, symbol) + PreferFewerParametersWeigher.addWeight(lookupElement, symbol) } fun CompletionSorter.applyWeighers(positionContext: KotlinRawPositionContext): CompletionSorter = @@ -292,6 +334,7 @@ internal object Weighers { ExpectedTypeWeigher.Weigher, DeprecatedWeigher.Weigher, PriorityWeigher.Weigher, + PreferredSubtypeWeigher.Weigher, PreferGetSetMethodsToPropertyWeigher.Weigher, NotImportedWeigher.Weigher, KindWeigher.Weigher, diff --git a/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt new file mode 100644 index 000000000000..bee3c3889643 --- /dev/null +++ b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt @@ -0,0 +1,26 @@ +// IGNORE_K1 +class A1 +class Z1 +sealed class Foo +class A2 +class Z2 +class Bar: Foo() +class Baz: Foo() +class A3 +class Z3 + + +fun f(foo: Foo) { + val t = foo as +} + +// WITH_ORDER +// EXIST: Bar +// EXIST: Baz +// EXIST: Foo +// EXIST: A1 +// EXIST: A2 +// EXIST: A3 +// EXIST: Z1 +// EXIST: Z2 +// EXIST: Z3 \ No newline at end of file diff --git a/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt new file mode 100644 index 000000000000..5030b2dc75d0 --- /dev/null +++ b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt @@ -0,0 +1,26 @@ +// IGNORE_K1 +class A1 +class Z1 +sealed class Foo +class A2 +class Z2 +class Bar: Foo() +class Baz: Foo() +class A3 +class Z3 + + +fun f(foo: Foo) { + if (foo is ) {} +} + +// WITH_ORDER +// EXIST: Bar +// EXIST: Baz +// EXIST: Foo +// EXIST: A1 +// EXIST: A2 +// EXIST: A3 +// EXIST: Z1 +// EXIST: Z2 +// EXIST: Z3 \ No newline at end of file diff --git a/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt new file mode 100644 index 000000000000..823eb5e954e3 --- /dev/null +++ b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt @@ -0,0 +1,26 @@ +// IGNORE_K1 +class A1 +class Z1 +sealed class Foo +class A2 +class Z2 +class Bar: Foo() +class Baz: Foo() +class A3 +class Z3 + + +fun f(foo: Foo) { + val t = foo as? +} + +// WITH_ORDER +// EXIST: Bar +// EXIST: Baz +// EXIST: Foo +// EXIST: A1 +// EXIST: A2 +// EXIST: A3 +// EXIST: Z1 +// EXIST: Z2 +// EXIST: Z3 \ No newline at end of file diff --git a/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt new file mode 100644 index 000000000000..7d36e3ef0b3a --- /dev/null +++ b/plugins/kotlin/completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt @@ -0,0 +1,31 @@ +// IGNORE_K1 +class A1 +class Z1 +class A2 +sealed interface Foo { + sealed interface Bar : Foo + sealed interface Baz : Foo +} +class BarImpl : Foo.Bar +class BazImpl : Foo.Baz +class Z2 +class A3 +class Z3 + + +fun f(foo: Foo) { + val t = foo as +} + +// WITH_ORDER +// EXIST: Bar +// EXIST: Baz +// EXIST: BarImpl +// EXIST: BazImpl +// EXIST: Foo +// EXIST: A1 +// EXIST: A2 +// EXIST: A3 +// EXIST: Z1 +// EXIST: Z2 +// EXIST: Z3 \ No newline at end of file diff --git a/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JSBasicCompletionTestGenerated.java b/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JSBasicCompletionTestGenerated.java index d5a27e683337..48178719bd3a 100644 --- a/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JSBasicCompletionTestGenerated.java +++ b/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JSBasicCompletionTestGenerated.java @@ -2523,6 +2523,40 @@ public abstract class K1JSBasicCompletionTestGenerated extends AbstractK1JSBasic } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractK1JSBasicCompletionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K1; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../testData/basic/common/shadowing") public static class Shadowing extends AbstractK1JSBasicCompletionTest { diff --git a/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JvmBasicCompletionTestGenerated.java b/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JvmBasicCompletionTestGenerated.java index 46dfdc00e8f2..6e349e7e12f6 100644 --- a/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JvmBasicCompletionTestGenerated.java +++ b/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1JvmBasicCompletionTestGenerated.java @@ -2523,6 +2523,40 @@ public abstract class K1JvmBasicCompletionTestGenerated extends AbstractK1JvmBas } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractK1JvmBasicCompletionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K1; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../testData/basic/common/shadowing") public static class Shadowing extends AbstractK1JvmBasicCompletionTest { diff --git a/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1MLPerformanceCompletionTestGenerated.java b/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1MLPerformanceCompletionTestGenerated.java index dd183b9b3c65..22647e8ddaa2 100644 --- a/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1MLPerformanceCompletionTestGenerated.java +++ b/plugins/kotlin/completion/tests-k1/test/org/jetbrains/kotlin/idea/completion/test/K1MLPerformanceCompletionTestGenerated.java @@ -2523,6 +2523,40 @@ public abstract class K1MLPerformanceCompletionTestGenerated extends AbstractK1M } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractK1MLPerformanceCompletionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K1; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../testData/basic/common/shadowing") public static class Shadowing extends AbstractK1MLPerformanceCompletionTest { diff --git a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/K2JsBasicCompletionLegacyStdlibTestGenerated.java b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/K2JsBasicCompletionLegacyStdlibTestGenerated.java index a7fbf760fba4..2b732485c144 100644 --- a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/K2JsBasicCompletionLegacyStdlibTestGenerated.java +++ b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/K2JsBasicCompletionLegacyStdlibTestGenerated.java @@ -2523,6 +2523,40 @@ public abstract class K2JsBasicCompletionLegacyStdlibTestGenerated extends Abstr } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../completion/testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractK2JsBasicCompletionLegacyStdlibTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K2; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../../completion/testData/basic/common/shadowing") public static class Shadowing extends AbstractK2JsBasicCompletionLegacyStdlibTest { diff --git a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2JvmBasicCompletionTestGenerated.java b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2JvmBasicCompletionTestGenerated.java index e5aa4d2330b9..425b5c68eed5 100644 --- a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2JvmBasicCompletionTestGenerated.java +++ b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2JvmBasicCompletionTestGenerated.java @@ -2523,6 +2523,40 @@ public abstract class K2JvmBasicCompletionTestGenerated extends AbstractK2JvmBas } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../completion/testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractK2JvmBasicCompletionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K2; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../../completion/testData/basic/common/shadowing") public static class Shadowing extends AbstractK2JvmBasicCompletionTest { diff --git a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/CommonNativeJvmKotlinKmpCompletionTestGenerated.java b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/CommonNativeJvmKotlinKmpCompletionTestGenerated.java index f54b366302fe..27843c53f588 100644 --- a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/CommonNativeJvmKotlinKmpCompletionTestGenerated.java +++ b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/CommonNativeJvmKotlinKmpCompletionTestGenerated.java @@ -2667,6 +2667,45 @@ public abstract class CommonNativeJvmKotlinKmpCompletionTestGenerated extends Ab } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../completion/testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractKotlinKmpCompletionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K2; + } + + @java.lang.Override + public KMPTestPlatform getTestPlatform() { + return KMPTestPlatform.CommonNativeJvm; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../../completion/testData/basic/common/shadowing") public static class Shadowing extends AbstractKotlinKmpCompletionTest { diff --git a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/JsKotlinKmpCompletionTestGenerated.java b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/JsKotlinKmpCompletionTestGenerated.java index 7ccfb6a5aa06..e73b3efd2776 100644 --- a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/JsKotlinKmpCompletionTestGenerated.java +++ b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/JsKotlinKmpCompletionTestGenerated.java @@ -2667,6 +2667,45 @@ public abstract class JsKotlinKmpCompletionTestGenerated extends AbstractKotlinK } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../completion/testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractKotlinKmpCompletionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K2; + } + + @java.lang.Override + public KMPTestPlatform getTestPlatform() { + return KMPTestPlatform.Js; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../../completion/testData/basic/common/shadowing") public static class Shadowing extends AbstractKotlinKmpCompletionTest { diff --git a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/NativeLinuxKotlinKmpCompletionTestGenerated.java b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/NativeLinuxKotlinKmpCompletionTestGenerated.java index 8c520599dbdd..f5cec6430ed3 100644 --- a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/NativeLinuxKotlinKmpCompletionTestGenerated.java +++ b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/kmpBasic/NativeLinuxKotlinKmpCompletionTestGenerated.java @@ -2667,6 +2667,45 @@ public abstract class NativeLinuxKotlinKmpCompletionTestGenerated extends Abstra } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../completion/testData/basic/common/sealedHierarchy") + public static class SealedHierarchy extends AbstractKotlinKmpCompletionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K2; + } + + @java.lang.Override + public KMPTestPlatform getTestPlatform() { + return KMPTestPlatform.NativeLinux; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("PreferSealedHierarchyInAs.kt") + public void testPreferSealedHierarchyInAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInIs.kt") + public void testPreferSealedHierarchyInIs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInIs.kt"); + } + + @TestMetadata("PreferSealedHierarchyInSafeAs.kt") + public void testPreferSealedHierarchyInSafeAs() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyInSafeAs.kt"); + } + + @TestMetadata("PreferSealedHierarchyNested.kt") + public void testPreferSealedHierarchyNested() throws Exception { + runTest("../../completion/testData/basic/common/sealedHierarchy/PreferSealedHierarchyNested.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../../completion/testData/basic/common/shadowing") public static class Shadowing extends AbstractKotlinKmpCompletionTest { diff --git a/plugins/kotlin/frontend-independent/src/org/jetbrains/kotlin/idea/util/positionContext/KotlinPositionContext.kt b/plugins/kotlin/frontend-independent/src/org/jetbrains/kotlin/idea/util/positionContext/KotlinPositionContext.kt index 875ac1932138..bab8a72665a6 100644 --- a/plugins/kotlin/frontend-independent/src/org/jetbrains/kotlin/idea/util/positionContext/KotlinPositionContext.kt +++ b/plugins/kotlin/frontend-independent/src/org/jetbrains/kotlin/idea/util/positionContext/KotlinPositionContext.kt @@ -89,6 +89,7 @@ class KotlinTypeNameReferencePositionContext( val typeReference: KtTypeReference?, ) : KotlinSimpleNameReferencePositionContext() + class KotlinAnnotationTypeNameReferencePositionContext( override val position: PsiElement, override val reference: KtSimpleNameReference, @@ -159,10 +160,10 @@ class KotlinInfixCallPositionContext( /** * Represents an operator call - binary or unary, postfix or prefix. - * + * * [nameExpression] does not represent any name in this case - it references * an operator reference, like `+`, `+=` or unary `-`. - * + * * [explicitReceiver] points to the main expression in the operator call: * - the LHS in the binary call (except for the `contains` operator, where the receiver is on the RHS) * - the underlying expression in the unary call @@ -349,7 +350,7 @@ object KotlinPositionContextDetector { KotlinOperatorCallPositionContext(position, reference, nameExpression, explicitReceiver) } } - + parent is KtUnaryExpression && parent.operationReference == nameExpression -> KotlinOperatorCallPositionContext(position, reference, nameExpression, explicitReceiver)