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.eval.AccessTarget
|
||||
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.quickfix.ChangeVariableMutabilityFix
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
@@ -78,10 +80,7 @@ class CanBeValInspection : AbstractKotlinInspection() {
|
||||
val pseudocode = pseudocode(declaration, bindingContext) ?: return false
|
||||
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return false
|
||||
|
||||
val writeInstructions = pseudocode.instructionsIncludingDeadCode
|
||||
.filterIsInstance<WriteValueInstruction>()
|
||||
.filter { (it.target as? AccessTarget.Call)?.resolvedCall?.resultingDescriptor == descriptor }
|
||||
.toSet()
|
||||
val writeInstructions = pseudocode.collectWriteInstructions(descriptor)
|
||||
if (writeInstructions.isEmpty()) return false // incorrect code - do not report
|
||||
|
||||
return writeInstructions.none { canReach(it, writeInstructions) }
|
||||
@@ -93,10 +92,24 @@ class CanBeValInspection : AbstractKotlinInspection() {
|
||||
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 {
|
||||
// special algorithm for linear code to avoid too deep recursion
|
||||
var instruction = from
|
||||
while (instruction is InstructionWithNext) {
|
||||
if (instruction is LocalFunctionDeclarationInstruction) {
|
||||
if (canReach(instruction.body.enterInstruction, targets, visited)) return true
|
||||
}
|
||||
val next = instruction.next ?: return false
|
||||
if (next in visited) return false
|
||||
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