Implicit exhaustive when check for definite variable initialization (KT-8700)

This commit is contained in:
Mikhail Glukhikh
2015-12-25 14:25:38 +03:00
committed by Mikhail Glukhikh
parent 52c3fb03a2
commit 011a9f23b9
10 changed files with 47 additions and 16 deletions
@@ -38,6 +38,19 @@ open class ControlFlowInfo<D> internal constructor(protected val map: MutableMap
class InitControlFlowInfo(map: MutableMap<VariableDescriptor, VariableControlFlowState> = hashMapOf()) :
ControlFlowInfo<VariableControlFlowState>(map) {
override fun copy() = InitControlFlowInfo(HashMap(map))
// this = output of EXHAUSTIVE_WHEN_ELSE instruction
// merge = input of MergeInstruction
// returns true if definite initialization in when happens here
fun checkDefiniteInitializationInWhen(merge: InitControlFlowInfo): Boolean {
for (entry in entries) {
if (entry.value.initState == InitState.INITIALIZED_EXHAUSTIVELY &&
merge[entry.key]?.initState == InitState.INITIALIZED) {
return true
}
}
return false
}
}
class UseControlFlowInfo(map: MutableMap<VariableDescriptor, VariableUseState> = hashMapOf()) :
@@ -713,10 +713,28 @@ public class ControlFlowInformationProvider {
}
public void markWhenWithoutElse() {
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
PseudocodeTraverserKt.traverse(
pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
@Override
public void execute(@NotNull Instruction instruction) {
if (instruction instanceof MagicInstruction) {
MagicInstruction magicInstruction = (MagicInstruction) instruction;
if (magicInstruction.getKind() == MagicKind.EXHAUSTIVE_WHEN_ELSE) {
Instruction next = magicInstruction.getNext();
if (next instanceof MergeInstruction) {
MergeInstruction mergeInstruction = (MergeInstruction) next;
if (initializers.containsKey(mergeInstruction) && initializers.containsKey(magicInstruction)) {
InitControlFlowInfo mergeInfo = initializers.get(mergeInstruction).getIncoming();
InitControlFlowInfo magicInfo = initializers.get(magicInstruction).getOutgoing();
if (mergeInstruction.getElement() instanceof KtWhenExpression &&
magicInfo.checkDefiniteInitializationInWhen(mergeInfo)) {
trace.record(IMPLICIT_EXHAUSTIVE_WHEN, (KtWhenExpression) mergeInstruction.getElement());
}
}
}
}
}
PseudoValue value = instruction instanceof InstructionWithValue
? ((InstructionWithValue) instruction).getOutputValue()
: null;
@@ -3,10 +3,10 @@ enum class My { A, B }
fun test(a: My): String {
val q: String?
when (a) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (a) {
My.A -> q = "1"
My.B -> q = "2"
}
}<!>
// When is exhaustive
return <!DEBUG_INFO_SMARTCAST!>q<!>
}
@@ -5,11 +5,11 @@ enum class Direction {
fun foo(dir: Direction): Int {
val res: Int
// See KT-6046: res is always initialized
when (dir) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (dir) {
Direction.NORTH -> res = 1
Direction.SOUTH -> res = 2
Direction.WEST -> res = 3
Direction.EAST -> res = 4
}
}<!>
return res
}
@@ -1,10 +1,10 @@
fun foo(b: Boolean): Int {
val x: Int
val y: Int
when (b) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
true -> y = 1
false -> y = 0
}
}<!>
// x is initialized here
x = 3
return x + y
@@ -5,10 +5,10 @@ enum class MyEnum {
fun foo(x: MyEnum?): Int {
val y: Int
// See KT-6046: y is always initialized
when (x) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (x) {
MyEnum.A -> y = 1
MyEnum.B -> y = 2
null -> y = 0
}
}<!>
return y
}
@@ -3,10 +3,10 @@ fun foo(a: Boolean, b: Boolean): Int {
if (a) {
x = 1
}
when (b) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
true -> <!VAL_REASSIGNMENT!>x<!> = 2
false -> x = 3
}
}<!>
return x
}
@@ -3,10 +3,10 @@ fun foo(a: Boolean, b: Boolean): Int {
if (a) {
x = 1
}
when (b) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
true -> x = 2
false -> x = 3
}
}<!>
return x
}
@@ -3,11 +3,11 @@ enum class X { A, B, C, D }
fun foo(arg: X): String {
val res: String
when (arg) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (arg) {
X.A -> res = "A"
X.B -> res = "B"
X.C -> res = "C"
X.D -> res = "D"
}
}<!>
return res
}
@@ -7,10 +7,10 @@ enum class E {
class Outer(e: E) {
private val prop: Int
init {
when(e ) {
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when(e ) {
// When is exhaustive, property is always initialized
E.A -> prop = 1
E.B -> prop = 2
}
}<!>
}
}