FIR checker: make unused checker visit qualified accesses in annotations

#KT-43687 Fixed
This commit is contained in:
Jinseong Jeon
2020-12-01 14:19:22 -08:00
committed by Mikhail Glukhikh
parent 5efe774dba
commit 148d540580
5 changed files with 71 additions and 13 deletions
@@ -0,0 +1,7 @@
annotation class Ann(val value: Int)
fun foo(): Int {
val x = 3
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>x<!>) val y = 5
return y
}
@@ -0,0 +1,15 @@
FILE: usedInAnnotationArguments.kt
public final annotation class Ann : R|kotlin/Annotation| {
public constructor(value: R|kotlin/Int|): R|Ann| {
super<R|kotlin/Any|>()
}
public final val value: R|kotlin/Int| = R|<local>/value|
public get(): R|kotlin/Int|
}
public final fun foo(): R|kotlin/Int| {
lval x: R|kotlin/Int| = Int(3)
@R|Ann|(R|<local>/x|) lval y: R|kotlin/Int| = Int(5)
^foo R|<local>/y|
}
@@ -344,6 +344,11 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/manyLocalVariables.kt");
}
@TestMetadata("usedInAnnotationArguments.kt")
public void testUsedInAnnotationArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/usedInAnnotationArguments.kt");
}
@TestMetadata("valueIsNeverRead.kt")
public void testValueIsNeverRead() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/valueIsNeverRead.kt");
@@ -344,6 +344,11 @@ public class ExtendedFirWithLightTreeDiagnosticsTestGenerated extends AbstractEx
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/manyLocalVariables.kt");
}
@TestMetadata("usedInAnnotationArguments.kt")
public void testUsedInAnnotationArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/usedInAnnotationArguments.kt");
}
@TestMetadata("valueIsNeverRead.kt")
public void testValueIsNeverRead() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/valueIsNeverRead.kt");
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
import kotlinx.collections.immutable.PersistentMap
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.FirSymbolOwner
import org.jetbrains.kotlin.fir.analysis.cfa.*
@@ -17,7 +18,9 @@ import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
import org.jetbrains.kotlin.fir.analysis.checkers.isIterator
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
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.symbols.impl.FirPropertySymbol
@@ -159,8 +162,10 @@ object UnusedChecker : FirControlFlowChecker() {
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
): PathAwareVariableStatusInfo {
if (data.isEmpty()) return PathAwareVariableStatusInfo.EMPTY
return data.map { (label, info) -> info.applyLabel(node, label) }
val result = data.map { (label, info) -> info.applyLabel(node, label) }
.reduce(PathAwareVariableStatusInfo::merge)
return (node.fir as? FirAnnotationContainer)?.annotations?.fold(result, ::visitAnnotation)
?: result
}
override fun visitVariableDeclarationNode(
@@ -230,31 +235,52 @@ object UnusedChecker : FirControlFlowChecker() {
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
): PathAwareVariableStatusInfo {
val dataForNode = visitNode(node, data)
if (node.fir.source?.kind is FirFakeSourceElementKind) return dataForNode
val reference = node.fir.calleeReference as? FirResolvedNamedReference ?: return dataForNode
val symbol = reference.resolvedSymbol as? FirPropertySymbol ?: return dataForNode
return visitQualifiedAccesses(dataForNode, node.fir)
}
if (symbol !in localProperties) return dataForNode
private fun visitAnnotation(
dataForNode: PathAwareVariableStatusInfo,
annotation: FirAnnotationCall,
): PathAwareVariableStatusInfo {
val qualifiedAccesses = annotation.argumentList.arguments.mapNotNull { it as? FirQualifiedAccess }.toTypedArray()
return visitQualifiedAccesses(dataForNode, *qualifiedAccesses)
}
private fun visitQualifiedAccesses(
dataForNode: PathAwareVariableStatusInfo,
vararg qualifiedAccesses: FirQualifiedAccess,
): PathAwareVariableStatusInfo {
fun retrieveSymbol(qualifiedAccess: FirQualifiedAccess): FirPropertySymbol? {
if (qualifiedAccess.source?.kind is FirFakeSourceElementKind) return null
val reference = qualifiedAccess.calleeReference as? FirResolvedNamedReference ?: return null
val symbol = reference.resolvedSymbol as? FirPropertySymbol ?: return null
return if (symbol !in localProperties) null else symbol
}
val symbols = qualifiedAccesses.mapNotNull { retrieveSymbol(it) }.toTypedArray()
val status = VariableStatus.READ
status.isRead = true
return update(dataForNode, symbol) { status }
return update(dataForNode, *symbols) { status }
}
private fun update(
pathAwareInfo: PathAwareVariableStatusInfo,
symbol: FirPropertySymbol,
vararg symbols: FirPropertySymbol,
updater: (VariableStatus?) -> VariableStatus?,
): PathAwareVariableStatusInfo {
var resultMap = persistentMapOf<EdgeLabel, VariableStatusInfo>()
var changed = false
for ((label, dataPerLabel) in pathAwareInfo) {
val v = updater.invoke(dataPerLabel[symbol])
if (v != null) {
resultMap = resultMap.put(label, dataPerLabel.put(symbol, v))
changed = true
} else {
resultMap = resultMap.put(label, dataPerLabel)
for (symbol in symbols) {
val v = updater.invoke(dataPerLabel[symbol])
if (v != null) {
resultMap = resultMap.put(label, dataPerLabel.put(symbol, v))
changed = true
} else {
resultMap = resultMap.put(label, dataPerLabel)
}
}
}
return if (changed) PathAwareVariableStatusInfo(resultMap) else pathAwareInfo