diff --git a/idea/idea-completion/testData/basic/java/JavaSyntheticProperty.kt b/idea/idea-completion/testData/basic/java/JavaSyntheticProperty.kt new file mode 100644 index 00000000000..435127e68d1 --- /dev/null +++ b/idea/idea-completion/testData/basic/java/JavaSyntheticProperty.kt @@ -0,0 +1,8 @@ +// FIR_COMPARISON + +fun foo(a: java.lang.Thread) { + a.na +} + + +// EXIST: {"lookupString":"name","tailText":" (from getName()/setName())","allLookupStrings":"getName, name, setName","itemText":"name"} diff --git a/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt b/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt index a4f68a36f93..5899e318f8f 100644 --- a/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt +++ b/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt @@ -1,3 +1,5 @@ +// FIR_COMPARISON + import java.io.File fun foo(file: File) { diff --git a/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt.after b/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt.after index 69f9eb2d9b1..f0fdc1ea787 100644 --- a/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt.after +++ b/idea/idea-completion/testData/handlers/basic/SyntheticExtension.kt.after @@ -1,3 +1,5 @@ +// FIR_COMPARISON + import java.io.File fun foo(file: File) { diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 7a18e4ff642..76be124f413 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -2950,6 +2950,11 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT runTest("idea/idea-completion/testData/basic/java/JavaPackage.kt"); } + @TestMetadata("JavaSyntheticProperty.kt") + public void testJavaSyntheticProperty() throws Exception { + runTest("idea/idea-completion/testData/basic/java/JavaSyntheticProperty.kt"); + } + @TestMetadata("KProperty.kt") public void testKProperty() throws Exception { runTest("idea/idea-completion/testData/basic/java/KProperty.kt"); diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt index b842a33a884..1b75bed1628 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt @@ -34,7 +34,7 @@ internal class KotlinFirLookupElementFactory { fun KtAnalysisSession.createLookupElement(symbol: KtNamedSymbol): LookupElement? { val elementBuilder = when (symbol) { is KtFunctionSymbol -> with(functionLookupElementFactory) { createLookup(symbol) } - is KtVariableLikeSymbol -> with(variableLookupElementFactory) { createLookup(symbol) } + is KtVariableLikeSymbol -> with(variableLookupElementFactory) { createLookup(symbol) } is KtClassLikeSymbol -> classLookupElementFactory.createLookup(symbol) is KtTypeParameterSymbol -> typeParameterLookupElementFactory.createLookup(symbol) else -> throw IllegalArgumentException("Cannot create a lookup element for $symbol") @@ -67,9 +67,23 @@ private class VariableLookupElementFactory { fun KtAnalysisSession.createLookup(symbol: KtVariableLikeSymbol): LookupElementBuilder { return LookupElementBuilder.create(UniqueLookupObject(), symbol.name.asString()) .withTypeText(symbol.type.render()) + .markIfSyntheticJavaProperty(symbol) .withInsertHandler(createInsertHandler(symbol)) } + private fun LookupElementBuilder.markIfSyntheticJavaProperty(symbol: KtVariableLikeSymbol): LookupElementBuilder = when (symbol) { + is KtSyntheticJavaPropertySymbol -> { + val getterName = symbol.javaGetterName.asString() + val setterName = symbol.javaSetterName?.asString() + this.withTailText((" (from ${buildSyntheticPropertyTailText(getterName, setterName)})")) + .withLookupStrings(listOfNotNull(getterName, setterName)) + } + else -> this + } + + private fun buildSyntheticPropertyTailText(getterName: String, setterName: String?): String = + if (setterName != null) "$getterName()/$setterName()" else "$getterName()" + private fun createInsertHandler(symbol: KtVariableLikeSymbol): InsertHandler { return QuotedNamesAwareInsertionHandler(symbol.name) } diff --git a/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java b/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java index e987af8d50a..9e2cc499aff 100644 --- a/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java +++ b/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java @@ -2950,6 +2950,11 @@ public class HighLevelJvmBasicCompletionTestGenerated extends AbstractHighLevelJ runTest("idea/idea-completion/testData/basic/java/JavaPackage.kt"); } + @TestMetadata("JavaSyntheticProperty.kt") + public void testJavaSyntheticProperty() throws Exception { + runTest("idea/idea-completion/testData/basic/java/JavaSyntheticProperty.kt"); + } + @TestMetadata("KProperty.kt") public void testKProperty() throws Exception { runTest("idea/idea-completion/testData/basic/java/KProperty.kt"); diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt index 6024798f860..ee24918c7de 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt @@ -6,15 +6,14 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.components import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertiesScope import org.jetbrains.kotlin.fir.resolve.scope -import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator -import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope -import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.* -import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCompletion import org.jetbrains.kotlin.idea.fir.low.level.api.api.getFirFile @@ -122,9 +121,17 @@ internal class KtFirScopeProvider( firResolveState.firTransformerProvider.getScopeSession(firSession), FakeOverrideTypeCalculator.Forced ) ?: return null - return convertToKtScope(firTypeScope) + return getCompositeScope( + listOf( + convertToKtScope(firTypeScope), + firTypeScope.getSyntheticPropertiesScope(firSession) + ) + ) } + private fun FirTypeScope.getSyntheticPropertiesScope(firSession: FirSession): KtScope = + convertToKtScope(FirSyntheticPropertiesScope(firSession, this)) + override fun getScopeContextForPosition( originalFile: KtFile, positionInFakeFile: KtElement