FIR checker: make unused checker visit qualified accesses in annotations
#KT-43687 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
5efe774dba
commit
148d540580
+7
@@ -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
|
||||
}
|
||||
+15
@@ -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|
|
||||
}
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+39
-13
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user