[kotlin] KTIJ-28693 - Use NotPropertiesService to filter out properties from completion and use underlying getters instead

^KTIJ-28693 fixed

GitOrigin-RevId: 0a417898b5baaa275d0b493f1d0def31163ec556
This commit is contained in:
Frederik Haselmeier
2025-05-14 20:08:40 +00:00
committed by intellij-monorepo-bot
parent a3924486b9
commit 3fbc56ed97
6 changed files with 68 additions and 6 deletions
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.scopes.KaScope
import org.jetbrains.kotlin.analysis.api.signatures.KaCallableSignature
import org.jetbrains.kotlin.analysis.api.signatures.KaFunctionSignature
import org.jetbrains.kotlin.analysis.api.signatures.KaVariableSignature
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.types.KaErrorType
import org.jetbrains.kotlin.analysis.api.types.KaType
@@ -38,6 +39,7 @@ import org.jetbrains.kotlin.idea.completion.lookups.factories.FunctionInsertionH
import org.jetbrains.kotlin.idea.completion.reference
import org.jetbrains.kotlin.idea.completion.weighers.CallableWeigher.callableWeight
import org.jetbrains.kotlin.idea.completion.weighers.WeighingContext
import org.jetbrains.kotlin.idea.core.NotPropertiesService
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.positionContext.KotlinNameReferencePositionContext
import org.jetbrains.kotlin.idea.util.positionContext.KotlinSimpleNameReferencePositionContext
@@ -49,6 +51,8 @@ import org.jetbrains.kotlin.resolve.ArrayFqNames
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.exceptions.KotlinIllegalArgumentExceptionWithAttachments
private val NOT_PROPERTIES = NotPropertiesService.DEFAULT.toSet()
internal open class FirCallableCompletionContributor(
parameters: KotlinFirCompletionParameters,
sink: LookupElementSink,
@@ -649,6 +653,25 @@ internal open class FirCallableCompletionContributor(
}
}
/**
* If the signature is that of certain synthetic java properties (e.g. `AtomicInteger.getAndIncrement`),
* we do not want to use the synthetic property (e.g. `andIncrement`) because it would be unnatural as they are not real properties.
* For these cases, this function will return the signature of the underlying Java getter instead.
*
* @see NotPropertiesService
*/
context(KaSession)
@OptIn(KaExperimentalApi::class)
private fun KaCallableSignature<*>.getJavaGetterSignatureIfNotProperty(): KaCallableSignature<*>? {
if (this !is KaVariableSignature<*>) return null
val symbol = symbol
if (symbol !is KaSyntheticJavaPropertySymbol || symbol.javaSetterSymbol != null) return null
val fqName = symbol.javaGetterSymbol.callableId?.asSingleFqName()?.asString() ?: return null
if (fqName !in NOT_PROPERTIES) return null
return symbol.javaGetterSymbol.asSignature()
}
/**
* Note, that [isImportDefinitelyNotRequired] should be set to true only if the callable is available without import, and it doesn't
* require import or fully-qualified name to be resolved unambiguously.
@@ -659,11 +682,19 @@ internal open class FirCallableCompletionContributor(
scopeKind: KaScopeKind,
isImportDefinitelyNotRequired: Boolean = false,
options: CallableInsertionOptions = getOptions(signature, isImportDefinitelyNotRequired),
): CallableWithMetadataForCompletion = CallableWithMetadataForCompletion(
_signature = signature,
options = options,
symbolOrigin = CompletionSymbolOrigin.Scope(scopeKind),
)
): CallableWithMetadataForCompletion {
val javaGetterIfNotProperty = signature.getJavaGetterSignatureIfNotProperty()
val optionsToUse = if (javaGetterIfNotProperty != null && options.insertionStrategy == CallableInsertionStrategy.AsIdentifier) {
options.copy(insertionStrategy = CallableInsertionStrategy.AsCall)
} else {
options
}
return CallableWithMetadataForCompletion(
_signature = javaGetterIfNotProperty ?: signature,
options = optionsToUse,
symbolOrigin = CompletionSymbolOrigin.Scope(scopeKind),
)
}
private fun isUninitializedCallable(
position: PsiElement,
@@ -0,0 +1,17 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
import java.util.concurrent.atomic.AtomicInteger
fun test(a: AtomicInteger) {
a.<caret>
}
// EXIST: getAndDecrement
// EXIST: getAndIncrement
// EXIST: getAndSet
// EXIST: getAndUpdate
// EXIST: getAndAdd
// ABSENT: andDecrement
// ABSENT: andIncrement
// ABSENT: andSet
// ABSENT: andUpdate
// ABSENT: andAdd
@@ -5,6 +5,5 @@ fun main(args: Array<String>) {
s.<caret>
}
// IGNORE_K2
// EXIST: {"lookupString":"getInputStream","tailText":"()","typeText":"InputStream!","attributes":"bold","allLookupStrings":"getInputStream","itemText":"getInputStream"}
// ABSENT: inputStream
@@ -4482,6 +4482,11 @@ public abstract class K1JvmBasicCompletionTestGenerated extends AbstractK1JvmBas
runTest("../testData/basic/java/syntheticExtensions/SafeCall.kt");
}
@TestMetadata("ShowAccessorsForAtomicOperations.kt")
public void testShowAccessorsForAtomicOperations() throws Exception {
runTest("../testData/basic/java/syntheticExtensions/ShowAccessorsForAtomicOperations.kt");
}
@TestMetadata("ShowGetSetOnSecondCompletion.kt")
public void testShowGetSetOnSecondCompletion() throws Exception {
runTest("../testData/basic/java/syntheticExtensions/ShowGetSetOnSecondCompletion.kt");
@@ -4482,6 +4482,11 @@ public abstract class K1MLPerformanceCompletionTestGenerated extends AbstractK1M
runTest("../testData/basic/java/syntheticExtensions/SafeCall.kt");
}
@TestMetadata("ShowAccessorsForAtomicOperations.kt")
public void testShowAccessorsForAtomicOperations() throws Exception {
runTest("../testData/basic/java/syntheticExtensions/ShowAccessorsForAtomicOperations.kt");
}
@TestMetadata("ShowGetSetOnSecondCompletion.kt")
public void testShowGetSetOnSecondCompletion() throws Exception {
runTest("../testData/basic/java/syntheticExtensions/ShowGetSetOnSecondCompletion.kt");
@@ -4482,6 +4482,11 @@ public abstract class K2JvmBasicCompletionTestGenerated extends AbstractK2JvmBas
runTest("../../completion/testData/basic/java/syntheticExtensions/SafeCall.kt");
}
@TestMetadata("ShowAccessorsForAtomicOperations.kt")
public void testShowAccessorsForAtomicOperations() throws Exception {
runTest("../../completion/testData/basic/java/syntheticExtensions/ShowAccessorsForAtomicOperations.kt");
}
@TestMetadata("ShowGetSetOnSecondCompletion.kt")
public void testShowGetSetOnSecondCompletion() throws Exception {
runTest("../../completion/testData/basic/java/syntheticExtensions/ShowGetSetOnSecondCompletion.kt");