From 8e242655f96671b419d96c1eee204e2649454979 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Wed, 25 Aug 2021 00:24:06 -0700 Subject: [PATCH] FIR IDE: resolve call to property accessors --- .../jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt | 8 ++++ .../frontend/api/components/KtCallResolver.kt | 14 +++++-- .../api/symbols/KtPropertyAccessorSymbol.kt | 7 +--- .../frontend/api/fir/KtSymbolByFirBuilder.kt | 11 +++++ .../api/fir/components/KtFirCallResolver.kt | 34 +++++++++++++++ .../fir/symbols/KtFirPropertyGetterSymbol.kt | 23 ++++++++++ .../fir/symbols/KtFirPropertySetterSymbol.kt | 21 ++++++++++ .../resolveCall/javaPropertyGetter.kt | 10 +++++ .../resolveCall/javaPropertyGetter.txt | 3 ++ .../resolveCall/javaPropertyNestedGetter.kt | 18 ++++++++ .../resolveCall/javaPropertyNestedGetter.txt | 3 ++ .../resolveCall/javaPropertySetter.kt | 12 ++++++ .../resolveCall/javaPropertySetter.txt | 3 ++ .../javaPropertySetterIncomplete.kt | 13 ++++++ .../javaPropertySetterIncomplete.txt | 3 ++ .../resolveCall/kotlinPropertyGetter.kt | 9 ++++ .../resolveCall/kotlinPropertyGetter.txt | 3 ++ .../resolveCall/kotlinPropertyNestedGetter.kt | 13 ++++++ .../kotlinPropertyNestedGetter.txt | 3 ++ .../resolveCall/kotlinPropertySetter.kt | 8 ++++ .../resolveCall/kotlinPropertySetter.txt | 3 ++ .../symbols/symbolByPsi/enumValueMember.txt | 2 + .../testData/symbols/symbolByPsi/jvmName.txt | 6 +++ .../symbols/symbolByPsi/memberProperties.txt | 2 + .../symbolByPsi/topLevelProperties.txt | 2 + .../api/fir/AbstractResolveCallTest.kt | 15 +++++-- .../api/fir/ResolveCallTestGenerated.java | 42 +++++++++++++++++++ 27 files changed, 278 insertions(+), 13 deletions(-) create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.kt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.txt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.kt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.txt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.kt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.txt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.kt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.txt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.kt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.txt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.kt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.txt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.kt create mode 100644 idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.txt diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt index 4e952fc6a75..5ca5c7753c4 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt @@ -381,6 +381,14 @@ fun KtExpression.getAssignmentByLHS(): KtBinaryExpression? { return if (KtPsiUtil.isAssignment(parent) && parent.left == this) parent else null } +tailrec fun findAssignment(element: PsiElement?): KtBinaryExpression? = + when (val parent = element?.parent) { + is KtBinaryExpression -> if (parent.left == element && parent.operationToken == KtTokens.EQ) parent else null + is KtQualifiedExpression -> findAssignment(element.parent) + is KtSimpleNameExpression -> findAssignment(element.parent) + else -> null + } + fun KtStringTemplateExpression.getContentRange(): TextRange { val start = node.firstChildNode.textLength val lastChild = node.lastChildNode diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtCallResolver.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtCallResolver.kt index 7e4aa9b80a5..022fb960f48 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtCallResolver.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtCallResolver.kt @@ -6,12 +6,10 @@ package org.jetbrains.kotlin.idea.frontend.api.components import org.jetbrains.kotlin.idea.frontend.api.calls.KtCall -import org.jetbrains.kotlin.psi.KtArrayAccessExpression -import org.jetbrains.kotlin.psi.KtBinaryExpression -import org.jetbrains.kotlin.psi.KtCallElement -import org.jetbrains.kotlin.psi.KtUnaryExpression +import org.jetbrains.kotlin.psi.* public abstract class KtCallResolver : KtAnalysisSessionComponent() { + public abstract fun resolveAccessorCall(call: KtSimpleNameExpression): KtCall? public abstract fun resolveCall(call: KtCallElement): KtCall? public abstract fun resolveCall(call: KtBinaryExpression): KtCall? public abstract fun resolveCall(call: KtUnaryExpression): KtCall? @@ -19,6 +17,14 @@ public abstract class KtCallResolver : KtAnalysisSessionComponent() { } public interface KtCallResolverMixIn : KtAnalysisSessionMixIn { + /** + * Resolves the given simple name expression to an accessor call if that name refers to a property. + * + * This spans both Kotlin property and synthetic Java property. + */ + public fun KtSimpleNameExpression.resolveAccessorCall(): KtCall? = + analysisSession.callResolver.resolveAccessorCall(this) + public fun KtCallElement.resolveCall(): KtCall? = analysisSession.callResolver.resolveCall(this) diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtPropertyAccessorSymbol.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtPropertyAccessorSymbol.kt index 7ce31e97383..40ff117640d 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtPropertyAccessorSymbol.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtPropertyAccessorSymbol.kt @@ -7,18 +7,15 @@ package org.jetbrains.kotlin.idea.frontend.api.symbols import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.* import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer -import org.jetbrains.kotlin.name.CallableId -public sealed class KtPropertyAccessorSymbol : KtCallableSymbol(), +public sealed class KtPropertyAccessorSymbol : KtFunctionLikeSymbol(), KtPossibleMemberSymbol, KtSymbolWithModality, KtSymbolWithVisibility, KtAnnotatedSymbol, KtSymbolWithKind { - final override val callableIdIfNonLocal: CallableId? get() = null final override val isExtension: Boolean get() = false - final override val receiverType: KtTypeAndAnnotations? get() = null public abstract val isDefault: Boolean public abstract val isInline: Boolean @@ -38,4 +35,4 @@ public abstract class KtPropertySetterSymbol : KtPropertyAccessorSymbol() { public abstract val parameter: KtValueParameterSymbol abstract override fun createPointer(): KtSymbolPointer -} \ No newline at end of file +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt index bce671dc6fa..7c2f5128b5d 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt @@ -200,6 +200,7 @@ internal class KtSymbolByFirBuilder private constructor( } is FirConstructor -> buildConstructorSymbol(fir) is FirAnonymousFunction -> buildAnonymousFunctionSymbol(fir) + is FirPropertyAccessor -> buildPropertyAccessorSymbol(fir) else -> throwUnexpectedElementError(fir) } } @@ -224,6 +225,16 @@ internal class KtSymbolByFirBuilder private constructor( check(fir.origin == FirDeclarationOrigin.SamConstructor) return symbolsCache.cache(fir) { KtFirSamConstructorSymbol(fir, resolveState, token, this@KtSymbolByFirBuilder) } } + + fun buildPropertyAccessorSymbol(fir: FirPropertyAccessor): KtFunctionLikeSymbol { + return symbolsCache.cache(fir) { + if (fir.isGetter) { + KtFirPropertyGetterSymbol(fir, resolveState, token, this@KtSymbolByFirBuilder) + } else { + KtFirPropertySetterSymbol(fir, resolveState, token, this@KtSymbolByFirBuilder) + } + } + } } inner class VariableLikeSymbolBuilder { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirCallResolver.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirCallResolver.kt index ca1ecb94d8d..4a7042e0994 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirCallResolver.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirCallResolver.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.calls.FirErrorReferenceWithCandidate import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType import org.jetbrains.kotlin.fir.types.classId @@ -36,8 +37,10 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMember import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper +import org.jetbrains.kotlin.idea.references.readWriteAccess import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.findAssignment import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -47,6 +50,37 @@ internal class KtFirCallResolver( ) : KtCallResolver(), KtFirAnalysisSessionComponent { private val diagnosticCache = mutableListOf() + override fun resolveAccessorCall(call: KtSimpleNameExpression): KtCall? = withValidityAssertion { + when (val fir = call.getOrBuildFir(firResolveState)) { + is FirResolvedNamedReference -> { + val propertySymbol = fir.resolvedSymbol as? FirPropertySymbol ?: return null + val access = call.readWriteAccess(useResolveForReadWrite = false) + val setterValue = findAssignment(call)?.right + val accessor = when { + access.isWrite -> propertySymbol.setterSymbol?.fir + access.isRead -> propertySymbol.getterSymbol?.fir + else -> null + } ?: return null + val accessorSymbol = analysisSession.firSymbolBuilder.functionLikeBuilder.buildFunctionLikeSymbol(accessor) + val target = + if (!access.isWrite || setterValue != null) + KtSuccessCallTarget(accessorSymbol) + else // access.isWrite && setterValue == null + KtErrorCallTarget( + listOf(accessorSymbol), + KtNonBoundToPsiErrorDiagnostic(factoryName = null, "Setter value is missing", token) + ) + val ktArgumentMapping = LinkedHashMap() + if (access.isWrite && setterValue != null) { + val setterParameterSymbol = accessor.valueParameters.single().buildSymbol(firSymbolBuilder) as KtValueParameterSymbol + ktArgumentMapping[setterValue] = setterParameterSymbol + } + return KtFunctionCall(ktArgumentMapping, target) + } + else -> return null + } + } + override fun resolveCall(call: KtBinaryExpression): KtCall? = withValidityAssertion { when (val fir = call.getOrBuildFir(firResolveState)) { is FirFunctionCall -> resolveCall(fir) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertyGetterSymbol.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertyGetterSymbol.kt index f1de0505354..45a2bea9482 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertyGetterSymbol.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertyGetterSymbol.kt @@ -11,8 +11,10 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.declarations.utils.isOverride +import org.jetbrains.kotlin.fir.resolve.getHasStableParameterNames import org.jetbrains.kotlin.idea.fir.findPsi import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder @@ -23,12 +25,14 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.utils.cached import org.jetbrains.kotlin.idea.frontend.api.fir.utils.firRef import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPropertyGetterSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtValueParameterSymbol import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtAnnotationCall import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtTypeAndAnnotations import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtPsiBasedSymbolPointer import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.types.KtType +import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId internal class KtFirPropertyGetterSymbol( @@ -59,10 +63,29 @@ internal class KtFirPropertyGetterSymbol( override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId) override val annotationClassIds: Collection by cached { firRef.getAnnotationClassIds() } + /** + * Returns [CallableId] of the delegated Java method if the corresponding property of this getter is a synthetic Java property. + * Otherwise, returns `null` + */ + override val callableIdIfNonLocal: CallableId? by firRef.withFirAndCache { fir -> + if (fir is FirSyntheticPropertyAccessor) { + fir.delegate.symbol.callableId + } else null + } + override val dispatchType: KtType? by cached { firRef.dispatchReceiverTypeAndAnnotations(builder) } + override val receiverType: KtTypeAndAnnotations? by cached { + firRef.receiverTypeAndAnnotations(builder) + } + + override val valueParameters: List + get() = emptyList() + + override val hasStableParameterNames: Boolean = firRef.withFir { it.getHasStableParameterNames(it.moduleData.session) } + override fun createPointer(): KtSymbolPointer { KtPsiBasedSymbolPointer.createForSymbolFromSource(this)?.let { return it } TODO("Creating pointers for getters from library is not supported yet") diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertySetterSymbol.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertySetterSymbol.kt index 71b44301be4..0411e6a6ca4 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertySetterSymbol.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirPropertySetterSymbol.kt @@ -11,8 +11,10 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.declarations.utils.isOverride +import org.jetbrains.kotlin.fir.resolve.getHasStableParameterNames import org.jetbrains.kotlin.idea.fir.findPsi import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder @@ -30,6 +32,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtPsiBasedSymbolP import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.types.KtType +import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId internal class KtFirPropertySetterSymbol( @@ -58,10 +61,22 @@ internal class KtFirPropertySetterSymbol( override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId) override val annotationClassIds: Collection by cached { firRef.getAnnotationClassIds() } + /** + * Returns [CallableId] of the delegated Java method if the corresponding property of this setter is a synthetic Java property. + * Otherwise, returns `null` + */ + override val callableIdIfNonLocal: CallableId? by firRef.withFirAndCache { fir -> + if (fir is FirSyntheticPropertyAccessor) { + fir.delegate.symbol.callableId + } else null + } + override val parameter: KtValueParameterSymbol by firRef.withFirAndCache { fir -> builder.variableLikeBuilder.buildValueParameterSymbol(fir.valueParameters.single()) } + override val valueParameters: List by cached { listOf(parameter) } + override val annotatedType: KtTypeAndAnnotations by cached { firRef.returnTypeAndAnnotations(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE, builder) } @@ -70,6 +85,12 @@ internal class KtFirPropertySetterSymbol( firRef.dispatchReceiverTypeAndAnnotations(builder) } + override val receiverType: KtTypeAndAnnotations? by cached { + firRef.receiverTypeAndAnnotations(builder) + } + + override val hasStableParameterNames: Boolean = firRef.withFir { it.getHasStableParameterNames(it.moduleData.session) } + override fun createPointer(): KtSymbolPointer { KtPsiBasedSymbolPointer.createForSymbolFromSource(this)?.let { return it } TODO("Creating pointers for setters from library is not supported yet") diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.kt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.kt new file mode 100644 index 00000000000..abfbf3ed69c --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.kt @@ -0,0 +1,10 @@ +// FILE: call.kt +fun call() { + val javaClass = JavaClass() + javaClass.foo +} + +// FILE: JavaClass.java +class JavaClass { + int getFoo() { return 42; } +} diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.txt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.txt new file mode 100644 index 00000000000..f5cb7433f69 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.txt @@ -0,0 +1,3 @@ +KtFunctionCall: +argumentMapping = { } +targetFunction = /JavaClass.getFoo(): kotlin.Int diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.kt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.kt new file mode 100644 index 00000000000..247bc19a924 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.kt @@ -0,0 +1,18 @@ +// FILE: call.kt +fun call() { + val javaClass = JavaClass() + javaClass.sub.foo = 42 +} + +// FILE: JavaClass.java +class JavaClass { + private JavaSubClass instance = new JavaSubClass(); + JavaSubClass getSub() { return instance; } +} + +// FILE: JavaSubClass.java +class JavaSubClass { + private int foo = -1; + int getFoo() { return foo; } + void setFoo(int v) { foo = v; } +} diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.txt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.txt new file mode 100644 index 00000000000..a3093e57907 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.txt @@ -0,0 +1,3 @@ +KtFunctionCall: +argumentMapping = { } +targetFunction = /JavaClass.getSub(): ft diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.kt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.kt new file mode 100644 index 00000000000..99b60d46f85 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.kt @@ -0,0 +1,12 @@ +// FILE: call.kt +fun call() { + val javaClass = JavaClass() + javaClass.foo = 42 +} + +// FILE: JavaClass.java +class JavaClass { + private int foo = -1; + int getFoo() { return foo; } + void setFoo(int v) { foo = v; } +} diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.txt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.txt new file mode 100644 index 00000000000..7a8fe1c1ea2 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.txt @@ -0,0 +1,3 @@ +KtFunctionCall: +argumentMapping = { 42 -> (v: kotlin.Int) } +targetFunction = /JavaClass.setFoo(v: kotlin.Int): kotlin.Unit diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.kt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.kt new file mode 100644 index 00000000000..d9fd2b18aa6 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.kt @@ -0,0 +1,13 @@ +// FILE: call.kt +fun call() { + val javaClass = JavaClass() + // Intentionally incomplete to see if `foo` refers to `setFoo`. + javaClass.foo = +} + +// FILE: JavaClass.java +class JavaClass { + private int foo = -1; + int getFoo() { return foo; } + void setFoo(int v) { foo = v; } +} diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.txt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.txt new file mode 100644 index 00000000000..c2ee7cdaa4b --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.txt @@ -0,0 +1,3 @@ +KtFunctionCall: +argumentMapping = { } +targetFunction = ERR diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.kt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.kt new file mode 100644 index 00000000000..f577775e607 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.kt @@ -0,0 +1,9 @@ +fun call { + val ktClass = KtClass() + ktClass.foo +} + +class KtClass { + val foo: Int + get() = 42 +} diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.txt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.txt new file mode 100644 index 00000000000..ab7b4abbde8 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.txt @@ -0,0 +1,3 @@ +KtFunctionCall: +argumentMapping = { } +targetFunction = (): kotlin.Int diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.kt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.kt new file mode 100644 index 00000000000..309265443e0 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.kt @@ -0,0 +1,13 @@ +fun call() { + val ktClass = KtClass() + ktClass.instance.foo = 42 +} + + +class KtClass { + val instance : KtSubClass = KtSubClass() +} + +class KtSubClass { + var foo : Int = -1 +} diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.txt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.txt new file mode 100644 index 00000000000..72ed6120e8a --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.txt @@ -0,0 +1,3 @@ +KtFunctionCall: +argumentMapping = { } +targetFunction = (): KtSubClass diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.kt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.kt new file mode 100644 index 00000000000..dce0c98f9c0 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.kt @@ -0,0 +1,8 @@ +fun call { + val ktClass = KtClass() + ktClass.foo = 42 +} + +class KtClass { + var foo : Int = -1 +} diff --git a/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.txt b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.txt new file mode 100644 index 00000000000..5ae65f4e100 --- /dev/null +++ b/idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.txt @@ -0,0 +1,3 @@ +KtFunctionCall: +argumentMapping = { 42 -> (value: kotlin.Int) } +targetFunction = (value: kotlin.Int): kotlin.Unit diff --git a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/enumValueMember.txt b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/enumValueMember.txt index 9af9bebccd7..41768786e2d 100644 --- a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/enumValueMember.txt +++ b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/enumValueMember.txt @@ -39,6 +39,7 @@ KtFirPropertyGetterSymbol: callableIdIfNonLocal: null dispatchType: null hasBody: true + hasStableParameterNames: true isDefault: false isExtension: false isInline: false @@ -47,6 +48,7 @@ KtFirPropertyGetterSymbol: origin: SOURCE receiverType: null symbolKind: ACCESSOR + valueParameters: [] visibility: Public deprecationStatus: null diff --git a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/jvmName.txt b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/jvmName.txt index 7b63a8a3fdc..7892e914086 100644 --- a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/jvmName.txt +++ b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/jvmName.txt @@ -49,6 +49,7 @@ KtFirPropertyGetterSymbol: callableIdIfNonLocal: null dispatchType: null hasBody: true + hasStableParameterNames: true isDefault: false isExtension: false isInline: false @@ -57,6 +58,7 @@ KtFirPropertyGetterSymbol: origin: SOURCE receiverType: null symbolKind: ACCESSOR + valueParameters: [] visibility: Public deprecationStatus: null @@ -86,6 +88,7 @@ KtFirPropertySetterSymbol: callableIdIfNonLocal: null dispatchType: null hasBody: true + hasStableParameterNames: true isDefault: false isExtension: false isInline: false @@ -95,6 +98,9 @@ KtFirPropertySetterSymbol: parameter: KtFirValueParameterSymbol(value) receiverType: null symbolKind: ACCESSOR + valueParameters: [ + KtFirValueParameterSymbol(value) + ] visibility: Public deprecationStatus: null diff --git a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/memberProperties.txt b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/memberProperties.txt index aff1a186118..785c9605dc8 100644 --- a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/memberProperties.txt +++ b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/memberProperties.txt @@ -36,6 +36,7 @@ KtFirPropertyGetterSymbol: callableIdIfNonLocal: null dispatchType: null hasBody: true + hasStableParameterNames: true isDefault: false isExtension: false isInline: false @@ -44,6 +45,7 @@ KtFirPropertyGetterSymbol: origin: SOURCE receiverType: null symbolKind: ACCESSOR + valueParameters: [] visibility: Public deprecationStatus: null diff --git a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/topLevelProperties.txt b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/topLevelProperties.txt index 35aabf55743..455e6593806 100644 --- a/idea/idea-frontend-fir/testData/symbols/symbolByPsi/topLevelProperties.txt +++ b/idea/idea-frontend-fir/testData/symbols/symbolByPsi/topLevelProperties.txt @@ -36,6 +36,7 @@ KtFirPropertyGetterSymbol: callableIdIfNonLocal: null dispatchType: null hasBody: true + hasStableParameterNames: true isDefault: false isExtension: false isInline: false @@ -44,6 +45,7 @@ KtFirPropertyGetterSymbol: origin: SOURCE receiverType: null symbolKind: ACCESSOR + valueParameters: [] visibility: Public deprecationStatus: null diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/AbstractResolveCallTest.kt b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/AbstractResolveCallTest.kt index da8e1742833..aa2d6e28dca 100644 --- a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/AbstractResolveCallTest.kt +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/AbstractResolveCallTest.kt @@ -15,9 +15,7 @@ import org.jetbrains.kotlin.idea.frontend.api.calls.KtCall import org.jetbrains.kotlin.idea.frontend.api.calls.KtDelegatedConstructorCallKind import org.jetbrains.kotlin.idea.frontend.api.calls.KtErrorCallTarget import org.jetbrains.kotlin.idea.frontend.api.calls.KtSuccessCallTarget -import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionLikeSymbol -import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol -import org.jetbrains.kotlin.idea.frontend.api.symbols.KtValueParameterSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.* import org.jetbrains.kotlin.idea.frontend.api.types.KtType import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.test.model.TestModule @@ -46,6 +44,7 @@ abstract class AbstractResolveCallTest : AbstractHLApiSingleModuleTest() { is KtBinaryExpression -> element.resolveCall() is KtUnaryExpression -> element.resolveCall() is KtArrayAccessExpression -> element.resolveCall() + is KtSimpleNameExpression -> element.resolveAccessorCall() is KtValueArgument -> resolveCall(element.getArgumentExpression()!!) else -> error("Selected element type (${element::class.simpleName}) is not supported for resolveCall()") } @@ -56,7 +55,15 @@ private fun KtCall.stringRepresentation(): String { fun KtType.render() = asStringForDebugging().replace('/', '.') fun Any.stringValue(): String = when (this) { is KtFunctionLikeSymbol -> buildString { - append(if (this@stringValue is KtFunctionSymbol) callableIdIfNonLocal ?: name else "") + append( + when (this@stringValue) { + is KtFunctionSymbol -> callableIdIfNonLocal ?: name + is KtConstructorSymbol -> "" + is KtPropertyGetterSymbol -> callableIdIfNonLocal ?: "" + is KtPropertySetterSymbol -> callableIdIfNonLocal ?: "" + else -> error("unexpected symbol kind in KtCall: ${this@stringValue::class.java}") + } + ) append("(") (this@stringValue as? KtFunctionSymbol)?.receiverType?.let { receiver -> append(": ${receiver.type.render()}") diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/ResolveCallTestGenerated.java b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/ResolveCallTestGenerated.java index 6da1fc57885..6852b42db9f 100644 --- a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/ResolveCallTestGenerated.java +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/fir/ResolveCallTestGenerated.java @@ -156,6 +156,48 @@ public class ResolveCallTestGenerated extends AbstractResolveCallTest { runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaFunctionCall.kt"); } + @Test + @TestMetadata("javaPropertyGetter.kt") + public void testJavaPropertyGetter() throws Exception { + runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyGetter.kt"); + } + + @Test + @TestMetadata("javaPropertyNestedGetter.kt") + public void testJavaPropertyNestedGetter() throws Exception { + runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertyNestedGetter.kt"); + } + + @Test + @TestMetadata("javaPropertySetter.kt") + public void testJavaPropertySetter() throws Exception { + runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetter.kt"); + } + + @Test + @TestMetadata("javaPropertySetterIncomplete.kt") + public void testJavaPropertySetterIncomplete() throws Exception { + runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaPropertySetterIncomplete.kt"); + } + + @Test + @TestMetadata("kotlinPropertyGetter.kt") + public void testKotlinPropertyGetter() throws Exception { + runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyGetter.kt"); + } + + @Test + @TestMetadata("kotlinPropertyNestedGetter.kt") + public void testKotlinPropertyNestedGetter() throws Exception { + runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertyNestedGetter.kt"); + } + + @Test + @TestMetadata("kotlinPropertySetter.kt") + public void testKotlinPropertySetter() throws Exception { + runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/kotlinPropertySetter.kt"); + } + @Test @TestMetadata("resolveCallInSuperConstructorParam.kt") public void testResolveCallInSuperConstructorParam() throws Exception {