FIR checker: make unused checker handle invoke properly
#KT-43688 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
d907c48d9c
commit
44c6ec2c44
@@ -0,0 +1,5 @@
|
||||
fun foo(): Int {
|
||||
val x = fun() = 4
|
||||
val y = fun() = 2
|
||||
return 10 * x() + y()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
FILE: invoke.kt
|
||||
public final fun foo(): R|kotlin/Int| {
|
||||
lval x: R|() -> kotlin/Int| = fun <anonymous>(): R|kotlin/Int| {
|
||||
^ Int(4)
|
||||
}
|
||||
|
||||
lval y: R|() -> kotlin/Int| = fun <anonymous>(): R|kotlin/Int| {
|
||||
^ Int(2)
|
||||
}
|
||||
|
||||
^foo Int(10).R|kotlin/Int.times|(R|<local>/x|.R|SubstitutionOverride<kotlin/Function0.invoke: R|kotlin/Int|>|()).R|kotlin/Int.plus|(R|<local>/y|.R|SubstitutionOverride<kotlin/Function0.invoke: R|kotlin/Int|>|())
|
||||
}
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+23
-1
@@ -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<FirPropertySymbol>
|
||||
) : ControlFlowGraphVisitor<PathAwareVariableStatusInfo, Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>>() {
|
||||
fun getData(graph: ControlFlowGraph): Map<CFGNode<*>, PathAwareVariableStatusInfo> {
|
||||
@@ -265,6 +270,23 @@ object UnusedChecker : FirControlFlowChecker() {
|
||||
return update(dataForNode, *symbols) { status }
|
||||
}
|
||||
|
||||
override fun visitFunctionCallNode(
|
||||
node: FunctionCallNode,
|
||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
||||
): 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,
|
||||
|
||||
Reference in New Issue
Block a user