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");
|
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")
|
@TestMetadata("valueIsNeverRead.kt")
|
||||||
public void testValueIsNeverRead() throws Exception {
|
public void testValueIsNeverRead() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/valueIsNeverRead.kt");
|
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");
|
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")
|
@TestMetadata("valueIsNeverRead.kt")
|
||||||
public void testValueIsNeverRead() throws Exception {
|
public void testValueIsNeverRead() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/valueIsNeverRead.kt");
|
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.PersistentMap
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
|
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||||
import org.jetbrains.kotlin.fir.analysis.cfa.*
|
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.checkers.isIterator
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
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.FirFunctionCall
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
@@ -159,8 +162,10 @@ object UnusedChecker : FirControlFlowChecker() {
|
|||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
if (data.isEmpty()) return PathAwareVariableStatusInfo.EMPTY
|
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)
|
.reduce(PathAwareVariableStatusInfo::merge)
|
||||||
|
return (node.fir as? FirAnnotationContainer)?.annotations?.fold(result, ::visitAnnotation)
|
||||||
|
?: result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitVariableDeclarationNode(
|
override fun visitVariableDeclarationNode(
|
||||||
@@ -230,31 +235,52 @@ object UnusedChecker : FirControlFlowChecker() {
|
|||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
if (node.fir.source?.kind is FirFakeSourceElementKind) return dataForNode
|
return visitQualifiedAccesses(dataForNode, node.fir)
|
||||||
val reference = node.fir.calleeReference as? FirResolvedNamedReference ?: return dataForNode
|
}
|
||||||
val symbol = reference.resolvedSymbol as? FirPropertySymbol ?: return dataForNode
|
|
||||||
|
|
||||||
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
|
val status = VariableStatus.READ
|
||||||
status.isRead = true
|
status.isRead = true
|
||||||
return update(dataForNode, symbol) { status }
|
|
||||||
|
return update(dataForNode, *symbols) { status }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun update(
|
private fun update(
|
||||||
pathAwareInfo: PathAwareVariableStatusInfo,
|
pathAwareInfo: PathAwareVariableStatusInfo,
|
||||||
symbol: FirPropertySymbol,
|
vararg symbols: FirPropertySymbol,
|
||||||
updater: (VariableStatus?) -> VariableStatus?,
|
updater: (VariableStatus?) -> VariableStatus?,
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
var resultMap = persistentMapOf<EdgeLabel, VariableStatusInfo>()
|
var resultMap = persistentMapOf<EdgeLabel, VariableStatusInfo>()
|
||||||
var changed = false
|
var changed = false
|
||||||
for ((label, dataPerLabel) in pathAwareInfo) {
|
for ((label, dataPerLabel) in pathAwareInfo) {
|
||||||
val v = updater.invoke(dataPerLabel[symbol])
|
for (symbol in symbols) {
|
||||||
if (v != null) {
|
val v = updater.invoke(dataPerLabel[symbol])
|
||||||
resultMap = resultMap.put(label, dataPerLabel.put(symbol, v))
|
if (v != null) {
|
||||||
changed = true
|
resultMap = resultMap.put(label, dataPerLabel.put(symbol, v))
|
||||||
} else {
|
changed = true
|
||||||
resultMap = resultMap.put(label, dataPerLabel)
|
} else {
|
||||||
|
resultMap = resultMap.put(label, dataPerLabel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if (changed) PathAwareVariableStatusInfo(resultMap) else pathAwareInfo
|
return if (changed) PathAwareVariableStatusInfo(resultMap) else pathAwareInfo
|
||||||
|
|||||||
Reference in New Issue
Block a user