FIR checker: make unused checker handle invoke properly

#KT-43688 Fixed
This commit is contained in:
Jinseong Jeon
2020-12-01 15:23:16 -08:00
committed by Mikhail Glukhikh
parent d907c48d9c
commit 44c6ec2c44
5 changed files with 50 additions and 1 deletions
@@ -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|>|())
}
@@ -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");
@@ -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");
@@ -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,