diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.kt new file mode 100644 index 00000000000..96764751f64 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.kt @@ -0,0 +1,5 @@ +fun foo(): Int { + val x = fun() = 4 + val y = fun() = 2 + return 10 * x() + y() +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.txt new file mode 100644 index 00000000000..cf32a300edb --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.txt @@ -0,0 +1,12 @@ +FILE: invoke.kt + public final fun foo(): R|kotlin/Int| { + lval x: R|() -> kotlin/Int| = fun (): R|kotlin/Int| { + ^ Int(4) + } + + lval y: R|() -> kotlin/Int| = fun (): R|kotlin/Int| { + ^ Int(2) + } + + ^foo Int(10).R|kotlin/Int.times|(R|/x|.R|SubstitutionOverride|()).R|kotlin/Int.plus|(R|/y|.R|SubstitutionOverride|()) + } diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java index e61136729ac..d1d2c85606e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java @@ -330,6 +330,11 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/classProperty.kt"); } + @TestMetadata("invoke.kt") + public void testInvoke() throws Exception { + runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.kt"); + } + @TestMetadata("lambda.kt") public void testLambda() throws Exception { runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/lambda.kt"); diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java index faec62595f6..09565f356d5 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java @@ -330,6 +330,11 @@ public class ExtendedFirWithLightTreeDiagnosticsTestGenerated extends AbstractEx runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/classProperty.kt"); } + @TestMetadata("invoke.kt") + public void testInvoke() throws Exception { + runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/invoke.kt"); + } + @TestMetadata("lambda.kt") public void testLambda() throws Exception { runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/lambda.kt"); 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 1008992cc57..2b54d48eb3f 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 @@ -10,6 +10,7 @@ import kotlinx.collections.immutable.persistentMapOf import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSymbolOwner import org.jetbrains.kotlin.fir.analysis.cfa.* import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker @@ -23,7 +24,10 @@ import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* +import org.jetbrains.kotlin.fir.resolve.inference.isFunctionalType +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.types.coneType object UnusedChecker : FirControlFlowChecker() { override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter, checkerContext: CheckerContext) { @@ -34,7 +38,7 @@ object UnusedChecker : FirControlFlowChecker() { val properties = LocalPropertyCollector.collect(graph) if (properties.isEmpty()) return - val data = ValueWritesWithoutReading(properties).getData(graph) + val data = ValueWritesWithoutReading(checkerContext.session, properties).getData(graph) graph.traverse(TraverseDirection.Backward, CfaVisitor(data, reporter)) } @@ -151,6 +155,7 @@ object UnusedChecker : FirControlFlowChecker() { } private class ValueWritesWithoutReading( + private val session: FirSession, private val localProperties: Set ) : ControlFlowGraphVisitor>>() { fun getData(graph: ControlFlowGraph): Map, PathAwareVariableStatusInfo> { @@ -265,6 +270,23 @@ object UnusedChecker : FirControlFlowChecker() { return update(dataForNode, *symbols) { status } } + override fun visitFunctionCallNode( + node: FunctionCallNode, + data: Collection> + ): PathAwareVariableStatusInfo { + val dataForNode = visitNode(node, data) + val reference = node.fir.calleeReference as? FirResolvedNamedReference ?: return dataForNode + val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode + val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") { + localProperties.find { it.fir.name == reference.name && it.fir.returnTypeRef.coneType.isFunctionalType(session) } + } else null + symbol ?: return dataForNode + + val status = VariableStatus.READ + status.isRead = true + return update(dataForNode, symbol) { status } + } + private fun update( pathAwareInfo: PathAwareVariableStatusInfo, vararg symbols: FirPropertySymbol,