From dbaaeef8dc412d441cee8c6a4514401cf40a93ad Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Thu, 15 Feb 2024 13:22:07 -0800 Subject: [PATCH] KT UAST: always preserve receiver expression of callable reference It seems like qualifier type and qualifier expression did not co-exist, but that only made users (like Lint detector) deal with inconsistent examination of both information that are sometimes available or not. Java UAST counterpart has both information always, so no good reason to keep such contract in Kotlin UAST. GitOrigin-RevId: 69f96325910cf8eea056449c984151925dffcf10 --- .../usagesInCallableReceiver/expected.xml | 5 ----- .../deadCode/usagesInCallableReceiver/src/TC.kt | 2 +- .../KotlinUCallableReferenceExpression.kt | 15 +++++---------- .../kotlin/FirKotlinUastResolveProviderService.kt | 3 ++- .../legacyRenderLog/MethodReference.log.txt | 1 + .../legacyTypes/MethodReference.types.txt | 1 + .../legacyValues/MethodReference.values.txt | 1 + .../expressions/UCallableReferenceExpression.kt | 2 -- 8 files changed, 11 insertions(+), 19 deletions(-) diff --git a/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/expected.xml b/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/expected.xml index 72a38af0352a..5e933496b9cf 100644 --- a/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/expected.xml +++ b/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/expected.xml @@ -1,8 +1,3 @@ - - TC.kt - 6 - Parameter <code>s</code> is not used - \ No newline at end of file diff --git a/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/src/TC.kt b/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/src/TC.kt index 3f801b4efa2a..c9de9052bb59 100644 --- a/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/src/TC.kt +++ b/java/java-tests/testData/inspection/jvm/deadCode/usagesInCallableReceiver/src/TC.kt @@ -3,7 +3,7 @@ fun main() { } fun foo(i: Int, - s: String //todo wrongly marked as unused, because simple references in callables are treated as class references + s: String ) { println("Foo $i"::toString) println(s::toString) diff --git a/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt b/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt index 6965a0bf3b22..e3f0a99d9a40 100644 --- a/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt +++ b/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt @@ -8,8 +8,6 @@ import com.intellij.psi.PsiType import com.intellij.psi.ResolveResult import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.psi.KtCallableReferenceExpression -import org.jetbrains.kotlin.psi.KtDotQualifiedExpression -import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.uast.* import org.jetbrains.uast.kotlin.internal.getResolveResultVariants @@ -19,21 +17,18 @@ class KotlinUCallableReferenceExpression( givenParent: UElement? ) : KotlinAbstractUExpression(givenParent), UCallableReferenceExpression, UMultiResolvable, KotlinUElementWithType { + private val qualifierExpressionPart = UastLazyPart() private val qualifierTypePart = UastLazyPart() override val qualifierExpression: UExpression? - get() { - if (qualifierType != null) return null - val receiverExpression = sourcePsi.receiverExpression ?: return null - return baseResolveProviderService.baseKotlinConverter.convertExpression(receiverExpression, this, DEFAULT_EXPRESSION_TYPES_LIST) + get() = qualifierExpressionPart.getOrBuild { + val receiverExpression = sourcePsi.receiverExpression ?: return@getOrBuild null + baseResolveProviderService.baseKotlinConverter.convertExpression(receiverExpression, this, DEFAULT_EXPRESSION_TYPES_LIST) } override val qualifierType: PsiType? get() = qualifierTypePart.getOrBuild { - if (sourcePsi.receiverExpression !is KtNameReferenceExpression && sourcePsi.receiverExpression !is KtDotQualifiedExpression) { - null - } else - baseResolveProviderService.getDoubleColonReceiverType(sourcePsi, this) + baseResolveProviderService.getDoubleColonReceiverType(sourcePsi, this) } override val callableName: String diff --git a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/FirKotlinUastResolveProviderService.kt b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/FirKotlinUastResolveProviderService.kt index d191f0ef5a60..1a8d7bde0b1c 100644 --- a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/FirKotlinUastResolveProviderService.kt +++ b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/FirKotlinUastResolveProviderService.kt @@ -618,9 +618,10 @@ interface FirKotlinUastResolveProviderService : BaseKotlinUastResolveProviderSer // Again, Analysis API returns [Unit] for statements, so we need to filter out // some cases that are not actually expression's return type. if (ktType.isUnit) { + val parent = ktExpression.parent // E.g., AnnotationTarget.FIELD, reference to enum class is resolved to the constructor call, // and then returned as Unit expression type. Same for path segments in a fully qualified name - if (ktExpression.parent is KtQualifiedExpression && + if ((parent is KtQualifiedExpression || parent is KtDoubleColonExpression) && (ktExpression is KtQualifiedExpression || ktExpression is KtNameReferenceExpression) ) { return null diff --git a/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyRenderLog/MethodReference.log.txt b/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyRenderLog/MethodReference.log.txt index 5f63d4f73e9a..69cfd8527f5d 100644 --- a/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyRenderLog/MethodReference.log.txt +++ b/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyRenderLog/MethodReference.log.txt @@ -3,6 +3,7 @@ UFile (package = ) UField (name = x) UAnnotation (fqName = org.jetbrains.annotations.NotNull) UCallableReferenceExpression (name = bar) + USimpleNameReferenceExpression (identifier = Foo) UMethod (name = getX) UClass (name = Foo) UMethod (name = bar) diff --git a/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyTypes/MethodReference.types.txt b/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyTypes/MethodReference.types.txt index d7e16b1eec22..52d17b69b3c4 100644 --- a/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyTypes/MethodReference.types.txt +++ b/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyTypes/MethodReference.types.txt @@ -3,6 +3,7 @@ UFile (package = ) [public final class MethodReferenceKt {...] UField (name = x) [@org.jetbrains.annotations.NotNull private static final var x: kotlin.reflect.KFunction = Foo::bar] UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull] UCallableReferenceExpression (name = bar) [Foo::bar] : PsiType:KFunction + USimpleNameReferenceExpression (identifier = Foo) [Foo] UMethod (name = getX) [public static final fun getX() : kotlin.reflect.KFunction = UastEmptyExpression] UClass (name = Foo) [public final class Foo {...}] UMethod (name = bar) [public final fun bar() : void {...}] diff --git a/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyValues/MethodReference.values.txt b/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyValues/MethodReference.values.txt index c456c1c5639c..31cdc1e0bd58 100644 --- a/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyValues/MethodReference.values.txt +++ b/plugins/kotlin/uast/uast-kotlin-fir/tests/testData/legacyValues/MethodReference.values.txt @@ -3,6 +3,7 @@ UFile (package = ) [public final class MethodReferenceKt {...] UField (name = x) [@org.jetbrains.annotations.NotNull private static final var x: kotlin.reflect.KFunction = Foo::bar] UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull] UCallableReferenceExpression (name = bar) [Foo::bar] = external Foo::bar() + USimpleNameReferenceExpression (identifier = Foo) [Foo] = external Foo() UMethod (name = getX) [public static final fun getX() : kotlin.reflect.KFunction = UastEmptyExpression] UClass (name = Foo) [public final class Foo {...}] UMethod (name = bar) [public final fun bar() : void {...}] diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt index 8b5ca2d66005..b4b371eeec29 100644 --- a/uast/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt @@ -14,13 +14,11 @@ import org.jetbrains.uast.visitor.UastVisitor interface UCallableReferenceExpression : UReferenceExpression { /** * Returns the qualifier expression. - * Can be null if the [qualifierType] is known. */ val qualifierExpression: UExpression? /** * Returns the qualifier type. - * Can be null if the qualifier is an expression. */ val qualifierType: PsiType?