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()) :
|
||||
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
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
}
|
||||
}<!>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user