Implicit exhaustive when check for definite variable initialization (KT-8700)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
52c3fb03a2
commit
011a9f23b9
@@ -38,6 +38,19 @@ open class ControlFlowInfo<D> internal constructor(protected val map: MutableMap
|
|||||||
class InitControlFlowInfo(map: MutableMap<VariableDescriptor, VariableControlFlowState> = hashMapOf()) :
|
class InitControlFlowInfo(map: MutableMap<VariableDescriptor, VariableControlFlowState> = hashMapOf()) :
|
||||||
ControlFlowInfo<VariableControlFlowState>(map) {
|
ControlFlowInfo<VariableControlFlowState>(map) {
|
||||||
override fun copy() = InitControlFlowInfo(HashMap(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()) :
|
class UseControlFlowInfo(map: MutableMap<VariableDescriptor, VariableUseState> = hashMapOf()) :
|
||||||
|
|||||||
@@ -713,10 +713,28 @@ public class ControlFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void markWhenWithoutElse() {
|
public void markWhenWithoutElse() {
|
||||||
|
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
|
||||||
PseudocodeTraverserKt.traverse(
|
PseudocodeTraverserKt.traverse(
|
||||||
pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
|
pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull Instruction instruction) {
|
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
|
PseudoValue value = instruction instanceof InstructionWithValue
|
||||||
? ((InstructionWithValue) instruction).getOutputValue()
|
? ((InstructionWithValue) instruction).getOutputValue()
|
||||||
: null;
|
: null;
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ enum class My { A, B }
|
|||||||
fun test(a: My): String {
|
fun test(a: My): String {
|
||||||
val q: String?
|
val q: String?
|
||||||
|
|
||||||
when (a) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (a) {
|
||||||
My.A -> q = "1"
|
My.A -> q = "1"
|
||||||
My.B -> q = "2"
|
My.B -> q = "2"
|
||||||
}
|
}<!>
|
||||||
// When is exhaustive
|
// When is exhaustive
|
||||||
return <!DEBUG_INFO_SMARTCAST!>q<!>
|
return <!DEBUG_INFO_SMARTCAST!>q<!>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ enum class Direction {
|
|||||||
fun foo(dir: Direction): Int {
|
fun foo(dir: Direction): Int {
|
||||||
val res: Int
|
val res: Int
|
||||||
// See KT-6046: res is always initialized
|
// See KT-6046: res is always initialized
|
||||||
when (dir) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (dir) {
|
||||||
Direction.NORTH -> res = 1
|
Direction.NORTH -> res = 1
|
||||||
Direction.SOUTH -> res = 2
|
Direction.SOUTH -> res = 2
|
||||||
Direction.WEST -> res = 3
|
Direction.WEST -> res = 3
|
||||||
Direction.EAST -> res = 4
|
Direction.EAST -> res = 4
|
||||||
}
|
}<!>
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
fun foo(b: Boolean): Int {
|
fun foo(b: Boolean): Int {
|
||||||
val x: Int
|
val x: Int
|
||||||
val y: Int
|
val y: Int
|
||||||
when (b) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
|
||||||
true -> y = 1
|
true -> y = 1
|
||||||
false -> y = 0
|
false -> y = 0
|
||||||
}
|
}<!>
|
||||||
// x is initialized here
|
// x is initialized here
|
||||||
x = 3
|
x = 3
|
||||||
return x + y
|
return x + y
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ enum class MyEnum {
|
|||||||
fun foo(x: MyEnum?): Int {
|
fun foo(x: MyEnum?): Int {
|
||||||
val y: Int
|
val y: Int
|
||||||
// See KT-6046: y is always initialized
|
// See KT-6046: y is always initialized
|
||||||
when (x) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (x) {
|
||||||
MyEnum.A -> y = 1
|
MyEnum.A -> y = 1
|
||||||
MyEnum.B -> y = 2
|
MyEnum.B -> y = 2
|
||||||
null -> y = 0
|
null -> y = 0
|
||||||
}
|
}<!>
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
+2
-2
@@ -3,10 +3,10 @@ fun foo(a: Boolean, b: Boolean): Int {
|
|||||||
if (a) {
|
if (a) {
|
||||||
x = 1
|
x = 1
|
||||||
}
|
}
|
||||||
when (b) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
|
||||||
true -> <!VAL_REASSIGNMENT!>x<!> = 2
|
true -> <!VAL_REASSIGNMENT!>x<!> = 2
|
||||||
false -> x = 3
|
false -> x = 3
|
||||||
}
|
}<!>
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -3,10 +3,10 @@ fun foo(a: Boolean, b: Boolean): Int {
|
|||||||
if (a) {
|
if (a) {
|
||||||
x = 1
|
x = 1
|
||||||
}
|
}
|
||||||
when (b) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
|
||||||
true -> x = 2
|
true -> x = 2
|
||||||
false -> x = 3
|
false -> x = 3
|
||||||
}
|
}<!>
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ enum class X { A, B, C, D }
|
|||||||
|
|
||||||
fun foo(arg: X): String {
|
fun foo(arg: X): String {
|
||||||
val res: String
|
val res: String
|
||||||
when (arg) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (arg) {
|
||||||
X.A -> res = "A"
|
X.A -> res = "A"
|
||||||
X.B -> res = "B"
|
X.B -> res = "B"
|
||||||
X.C -> res = "C"
|
X.C -> res = "C"
|
||||||
X.D -> res = "D"
|
X.D -> res = "D"
|
||||||
}
|
}<!>
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -7,10 +7,10 @@ enum class E {
|
|||||||
class Outer(e: E) {
|
class Outer(e: E) {
|
||||||
private val prop: Int
|
private val prop: Int
|
||||||
init {
|
init {
|
||||||
when(e ) {
|
<!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when(e ) {
|
||||||
// When is exhaustive, property is always initialized
|
// When is exhaustive, property is always initialized
|
||||||
E.A -> prop = 1
|
E.A -> prop = 1
|
||||||
E.B -> prop = 2
|
E.B -> prop = 2
|
||||||
}
|
}<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user