KT-64832 KT-61032 [FIR] Correctly handle extension receiver from implicit invoke calls in UnusedChecker
- use `FirImplicitInvokeCall` instance check to detect implicit calls, it is cleaner than checking particular resolve type and "invoke" name - reuse `visitQualifiedAccesses` to update the CFG instead of manually searching through the `localProperties` (because it didn't work in the case of overloads) ^KT-64832 Fixed ^KT-61032 Fixed
This commit is contained in:
committed by
Space Team
parent
0cad4c9632
commit
c3a61f539b
Vendored
+1
-1
@@ -3,7 +3,7 @@ class Foo
|
|||||||
operator fun Foo.invoke() {}
|
operator fun Foo.invoke() {}
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
val <!UNUSED_VARIABLE!>x<!> = Foo()
|
val x = Foo()
|
||||||
|
|
||||||
x()
|
x()
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ class Foo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
val <!UNUSED_VARIABLE!>x<!> = Foo()
|
val x = Foo()
|
||||||
|
|
||||||
x()
|
x()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import kotlin.reflect.KFunction1
|
import kotlin.reflect.KFunction1
|
||||||
|
|
||||||
fun foo(action: KFunction1<String, Int>): Int {
|
fun foo(action: KFunction1<String, Int>): Int {
|
||||||
val <!UNUSED_VARIABLE!>localAction<!> = action
|
val localAction = action
|
||||||
|
|
||||||
return localAction("hello")
|
return localAction("hello")
|
||||||
}
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
fun foo(): Int {
|
fun foo(): Int {
|
||||||
fun action(s: String): Int = s.toInt()
|
fun action(s: String): Int = s.toInt()
|
||||||
|
|
||||||
val <!UNUSED_VARIABLE!>localAction<!> = ::action
|
val localAction = ::action
|
||||||
|
|
||||||
return localAction("hello")
|
return localAction("hello")
|
||||||
}
|
}
|
||||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import kotlin.reflect.KSuspendFunction1
|
import kotlin.reflect.KSuspendFunction1
|
||||||
|
|
||||||
suspend fun foo(action: KSuspendFunction1<String, Int>): Int {
|
suspend fun foo(action: KSuspendFunction1<String, Int>): Int {
|
||||||
val <!UNUSED_VARIABLE!>localAction<!> = action
|
val localAction = action
|
||||||
|
|
||||||
return localAction("hello")
|
return localAction("hello")
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
fun foo() {
|
fun foo() {
|
||||||
val x = fun(s: String) {}
|
val <!UNUSED_VARIABLE!>x<!> = fun(s: String) {}
|
||||||
|
|
||||||
fun nested() {
|
fun nested() {
|
||||||
val <!UNUSED_VARIABLE!>x<!> = fun(i: Int) {}
|
val x = fun(i: Int) {}
|
||||||
|
|
||||||
x(10)
|
x(10)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
suspend fun foo(action: suspend () -> Unit) {
|
suspend fun foo(action: suspend () -> Unit) {
|
||||||
val <!UNUSED_VARIABLE!>x<!> = action
|
val x = action
|
||||||
|
|
||||||
x()
|
x()
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-15
@@ -20,20 +20,16 @@ import org.jetbrains.kotlin.fir.analysis.checkers.isIterator
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
import org.jetbrains.kotlin.fir.references.resolved
|
|
||||||
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
|
||||||
import org.jetbrains.kotlin.fir.types.isBasicFunctionType
|
|
||||||
import org.jetbrains.kotlin.name.SpecialNames
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
|
|
||||||
object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
||||||
override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) {
|
override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) {
|
||||||
val ownData = ValueWritesWithoutReading(context.session, data.properties).getData(data.graph)
|
val ownData = ValueWritesWithoutReading(data.properties).getData(data.graph)
|
||||||
data.graph.traverse(CfaVisitor(ownData, reporter, context))
|
data.graph.traverse(CfaVisitor(ownData, reporter, context))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +145,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ValueWritesWithoutReading(
|
private class ValueWritesWithoutReading(
|
||||||
private val session: FirSession,
|
|
||||||
private val localProperties: Set<FirPropertySymbol>
|
private val localProperties: Set<FirPropertySymbol>
|
||||||
) : PathAwareControlFlowGraphVisitor<VariableStatusInfo>() {
|
) : PathAwareControlFlowGraphVisitor<VariableStatusInfo>() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -274,16 +269,16 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
data: PathAwareVariableStatusInfo
|
data: PathAwareVariableStatusInfo
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
val reference = node.fir.calleeReference.resolved ?: return dataForNode
|
|
||||||
val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode
|
|
||||||
val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") {
|
|
||||||
localProperties.find { it.name == reference.name && it.resolvedReturnTypeRef.coneType.isBasicFunctionType(session) }
|
|
||||||
} else null
|
|
||||||
symbol ?: return dataForNode
|
|
||||||
|
|
||||||
val status = VariableStatus.READ
|
val functionCall = node.fir
|
||||||
status.isRead = true
|
if (functionCall is FirImplicitInvokeCall) {
|
||||||
return update(dataForNode, symbol) { status }
|
val invokeReceiver = functionCall.explicitReceiver as? FirQualifiedAccessExpression
|
||||||
|
if (invokeReceiver != null) {
|
||||||
|
return visitQualifiedAccesses(dataForNode, invokeReceiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataForNode
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun update(
|
private fun update(
|
||||||
|
|||||||
Reference in New Issue
Block a user