Can be val inspection: local functions are taken into account #KT-13151 Fixed
(cherry picked from commit 57eb4c1)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
0380b6cd88
commit
8600e7348c
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
|||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionWithNext
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionWithNext
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstruction
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstruction
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.quickfix.ChangeVariableMutabilityFix
|
import org.jetbrains.kotlin.idea.quickfix.ChangeVariableMutabilityFix
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||||
@@ -78,10 +80,7 @@ class CanBeValInspection : AbstractKotlinInspection() {
|
|||||||
val pseudocode = pseudocode(declaration, bindingContext) ?: return false
|
val pseudocode = pseudocode(declaration, bindingContext) ?: return false
|
||||||
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return false
|
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return false
|
||||||
|
|
||||||
val writeInstructions = pseudocode.instructionsIncludingDeadCode
|
val writeInstructions = pseudocode.collectWriteInstructions(descriptor)
|
||||||
.filterIsInstance<WriteValueInstruction>()
|
|
||||||
.filter { (it.target as? AccessTarget.Call)?.resolvedCall?.resultingDescriptor == descriptor }
|
|
||||||
.toSet()
|
|
||||||
if (writeInstructions.isEmpty()) return false // incorrect code - do not report
|
if (writeInstructions.isEmpty()) return false // incorrect code - do not report
|
||||||
|
|
||||||
return writeInstructions.none { canReach(it, writeInstructions) }
|
return writeInstructions.none { canReach(it, writeInstructions) }
|
||||||
@@ -93,10 +92,24 @@ class CanBeValInspection : AbstractKotlinInspection() {
|
|||||||
return pseudocodeCache.getOrPut(declaration) { PseudocodeUtil.generatePseudocode(declaration, bindingContext) }
|
return pseudocodeCache.getOrPut(declaration) { PseudocodeUtil.generatePseudocode(declaration, bindingContext) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Pseudocode.collectWriteInstructions(descriptor: DeclarationDescriptor): Set<WriteValueInstruction> =
|
||||||
|
with (instructionsIncludingDeadCode) {
|
||||||
|
filterIsInstance<WriteValueInstruction>()
|
||||||
|
.filter { (it.target as? AccessTarget.Call)?.resolvedCall?.resultingDescriptor == descriptor }
|
||||||
|
.toSet() +
|
||||||
|
|
||||||
|
filterIsInstance<LocalFunctionDeclarationInstruction>()
|
||||||
|
.map { it.body.collectWriteInstructions(descriptor) }
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
private fun canReach(from: Instruction, targets: Set<Instruction>, visited: HashSet<Instruction> = HashSet<Instruction>()): Boolean {
|
private fun canReach(from: Instruction, targets: Set<Instruction>, visited: HashSet<Instruction> = HashSet<Instruction>()): Boolean {
|
||||||
// special algorithm for linear code to avoid too deep recursion
|
// special algorithm for linear code to avoid too deep recursion
|
||||||
var instruction = from
|
var instruction = from
|
||||||
while (instruction is InstructionWithNext) {
|
while (instruction is InstructionWithNext) {
|
||||||
|
if (instruction is LocalFunctionDeclarationInstruction) {
|
||||||
|
if (canReach(instruction.body.enterInstruction, targets, visited)) return true
|
||||||
|
}
|
||||||
val next = instruction.next ?: return false
|
val next = instruction.next ?: return false
|
||||||
if (next in visited) return false
|
if (next in visited) return false
|
||||||
if (next in targets) return true
|
if (next in targets) return true
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
fun run(f: () -> Unit) = f()
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
var a: Int
|
||||||
|
a = 10
|
||||||
|
|
||||||
|
run {
|
||||||
|
a = 20
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user