From c3a61f539ba63da167a6b9a3a4c386cae0b36a1d Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Tue, 9 Jan 2024 17:04:41 +0100 Subject: [PATCH] KT-64832 KT-61032 [FIR] Correctly handle extension receiver from implicit invoke calls in `UnusedChecker` - use `FirImplicitInvokeCall` instance check to detect implicit calls, it is cleaner than checking particular resolve type and "invoke" name - reuse `visitQualifiedAccesses` to update the CFG instead of manually searching through the `localProperties` (because it didn't work in the case of overloads) ^KT-64832 Fixed ^KT-61032 Fixed --- .../unused/invokeCustomTypeExt.kt | 2 +- .../unused/invokeCustomTypeMember.kt | 2 +- .../unused/invokeKFunction.kt | 2 +- .../invokeKFunctionFromMethodReference.kt | 2 +- .../unused/invokeKSuspendFunction.kt | 2 +- .../unused/invokeOverload2.kt | 4 +-- .../extendedCheckers/unused/invokeSuspend.kt | 2 +- .../checkers/extended/UnusedChecker.kt | 25 ++++++++----------- 8 files changed, 18 insertions(+), 23 deletions(-) diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeExt.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeExt.kt index de12a288549..b0cb648d60a 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeExt.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeExt.kt @@ -3,7 +3,7 @@ class Foo operator fun Foo.invoke() {} fun foo() { - val x = Foo() + val x = Foo() x() } diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeMember.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeMember.kt index 2517cc38eb5..2dabca6f105 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeMember.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeCustomTypeMember.kt @@ -3,7 +3,7 @@ class Foo { } fun foo() { - val x = Foo() + val x = Foo() x() } diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunction.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunction.kt index c3c6467b022..9d59d55666c 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunction.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunction.kt @@ -2,7 +2,7 @@ import kotlin.reflect.KFunction1 fun foo(action: KFunction1): Int { - val localAction = action + val localAction = action return localAction("hello") } \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunctionFromMethodReference.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunctionFromMethodReference.kt index f2ea470d5f6..4faee60d589 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunctionFromMethodReference.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKFunctionFromMethodReference.kt @@ -2,7 +2,7 @@ fun foo(): Int { fun action(s: String): Int = s.toInt() - val localAction = ::action + val localAction = ::action return localAction("hello") } \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKSuspendFunction.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKSuspendFunction.kt index 76403dd73cb..b91e7cdb8ab 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKSuspendFunction.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeKSuspendFunction.kt @@ -2,7 +2,7 @@ import kotlin.reflect.KSuspendFunction1 suspend fun foo(action: KSuspendFunction1): Int { - val localAction = action + val localAction = action return localAction("hello") } \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeOverload2.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeOverload2.kt index 09c7c40860d..f08f493f9ad 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeOverload2.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeOverload2.kt @@ -1,8 +1,8 @@ fun foo() { - val x = fun(s: String) {} + val x = fun(s: String) {} fun nested() { - val x = fun(i: Int) {} + val x = fun(i: Int) {} x(10) } diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeSuspend.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeSuspend.kt index 96949baebaf..ac6b3db9ca6 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeSuspend.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/unused/invokeSuspend.kt @@ -1,5 +1,5 @@ suspend fun foo(action: suspend () -> Unit) { - val x = action + val x = action x() } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt index 2de28faaeaf..8857ec4b4bf 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt @@ -20,20 +20,16 @@ import org.jetbrains.kotlin.fir.analysis.checkers.isIterator import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.references.resolved import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.symbols.SymbolInternals -import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase -import org.jetbrains.kotlin.fir.types.coneType -import org.jetbrains.kotlin.fir.types.isBasicFunctionType import org.jetbrains.kotlin.name.SpecialNames object UnusedChecker : AbstractFirPropertyInitializationChecker() { override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) { - val ownData = ValueWritesWithoutReading(context.session, data.properties).getData(data.graph) + val ownData = ValueWritesWithoutReading(data.properties).getData(data.graph) data.graph.traverse(CfaVisitor(ownData, reporter, context)) } @@ -149,7 +145,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() { } private class ValueWritesWithoutReading( - private val session: FirSession, private val localProperties: Set ) : PathAwareControlFlowGraphVisitor() { companion object { @@ -274,16 +269,16 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() { data: PathAwareVariableStatusInfo ): PathAwareVariableStatusInfo { val dataForNode = visitNode(node, data) - val reference = node.fir.calleeReference.resolved ?: return dataForNode - val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode - val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") { - localProperties.find { it.name == reference.name && it.resolvedReturnTypeRef.coneType.isBasicFunctionType(session) } - } else null - symbol ?: return dataForNode - val status = VariableStatus.READ - status.isRead = true - return update(dataForNode, symbol) { status } + val functionCall = node.fir + if (functionCall is FirImplicitInvokeCall) { + val invokeReceiver = functionCall.explicitReceiver as? FirQualifiedAccessExpression + if (invokeReceiver != null) { + return visitQualifiedAccesses(dataForNode, invokeReceiver) + } + } + + return dataForNode } private fun update(