[FIR] Only track assignment nodes for uncaught exception edges in CFG

UncaughtExceptionPath edges are used to influence smart-casting within
catch and finally blocks. Previously these edges were added from every
node which could throw an exception. But only assignment nodes influence
smart-casts by resetting inference back to some less specific type.
Therefore, instead of tracking every possible node which could throw an
exception - even though almost every statement node can - only add edges
from assignment nodes to catch and finally blocks. This fixes many
missing exception cases and also reduces the total number of incoming
edges to catch and finally blocks.

#KT-56872 Fixed
This commit is contained in:
Brian Norman
2023-07-21 10:10:07 -05:00
committed by Space Team
parent 2a4681b91d
commit f51b7faa1b
14 changed files with 650 additions and 105 deletions
@@ -97,13 +97,11 @@ digraph initializationInTry_kt {
17 -> {18};
18 -> {19};
19 -> {20};
19 -> {26} [label="onUncaughtException"];
20 -> {21};
20 -> {26} [label="onUncaughtException"];
21 -> {22};
22 -> {23};
22 -> {26} [label="onUncaughtException"];
23 -> {24};
23 -> {26} [label="onUncaughtException"];
24 -> {25};
25 -> {26};
26 -> {27};
@@ -167,11 +165,10 @@ digraph initializationInTry_kt {
40 -> {41};
41 -> {42};
42 -> {43};
42 -> {48} [label="onUncaughtException"];
43 -> {44};
44 -> {45};
44 -> {48} [label="onUncaughtException"];
45 -> {46};
45 -> {48} [label="onUncaughtException"];
46 -> {47};
47 -> {48};
48 -> {49};
@@ -151,7 +151,7 @@ digraph complex_kt {
23 -> {24 29};
24 -> {25};
25 -> {26};
26 -> {27 29};
26 -> {27};
27 -> {28};
28 -> {29 38};
29 -> {30};
@@ -184,12 +184,12 @@ digraph inplaceLambdaInControlFlowExpressions_kt {
45 -> {46} [style=dashed];
46 -> {47};
47 -> {48};
48 -> {49 55};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52} [color=green];
51 -> {61} [color=red];
52 -> {53 55};
52 -> {53};
53 -> {54};
54 -> {55 61};
55 -> {56};
@@ -333,7 +333,7 @@ digraph postponedLambdaInReturn_kt {
88 -> {89};
89 -> {90};
90 -> {91} [color=green];
91 -> {92 96};
91 -> {92};
92 -> {109};
92 -> {93} [style=dotted];
93 -> {94} [style=dotted];
@@ -228,11 +228,11 @@ digraph tryCatch_kt {
subgraph cluster_32 {
color=blue
86 [label="Catch enter"];
87 [label="Variable declaration: e: R|kotlin/RuntimeException|"];
87 [label="Variable declaration: e: R|kotlin/Exception|"];
subgraph cluster_33 {
color=blue
88 [label="Enter block"];
89 [label="Jump: break@@@[Boolean(true)] "];
89 [label="Jump: continue@@@[Boolean(true)] "];
90 [label="Stub" style="filled" fillcolor=gray];
91 [label="Exit block" style="filled" fillcolor=gray];
}
@@ -241,11 +241,11 @@ digraph tryCatch_kt {
subgraph cluster_34 {
color=blue
93 [label="Catch enter"];
94 [label="Variable declaration: e: R|kotlin/Exception|"];
94 [label="Variable declaration: e: R|kotlin/RuntimeException|"];
subgraph cluster_35 {
color=blue
95 [label="Enter block"];
96 [label="Jump: continue@@@[Boolean(true)] "];
96 [label="Jump: break@@@[Boolean(true)] "];
97 [label="Stub" style="filled" fillcolor=gray];
98 [label="Exit block" style="filled" fillcolor=gray];
}
@@ -297,7 +297,7 @@ digraph tryCatch_kt {
71 -> {72};
72 -> {73};
73 -> {74};
74 -> {75 86 93};
74 -> {75};
75 -> {76 77};
76 -> {83};
77 -> {78};
@@ -313,7 +313,7 @@ digraph tryCatch_kt {
86 -> {87};
87 -> {88};
88 -> {89};
89 -> {105};
89 -> {49} [color=green style=dashed];
89 -> {90} [style=dotted];
90 -> {91} [style=dotted];
91 -> {92} [style=dotted];
@@ -321,7 +321,7 @@ digraph tryCatch_kt {
93 -> {94};
94 -> {95};
95 -> {96};
96 -> {49} [color=green style=dashed];
96 -> {105};
96 -> {97} [style=dotted];
97 -> {98} [style=dotted];
98 -> {99} [style=dotted];
@@ -70,7 +70,6 @@ digraph variableInitializedInTryBlock_kt {
6 -> {7};
7 -> {8};
8 -> {9};
8 -> {23} [label="onUncaughtException"];
9 -> {10 13};
10 -> {11};
11 -> {23} [label="return@/test"];
@@ -80,13 +79,11 @@ digraph variableInitializedInTryBlock_kt {
14 -> {15};
15 -> {16};
16 -> {17};
16 -> {23} [label="onUncaughtException"];
17 -> {18};
17 -> {23} [label="onUncaughtException"];
18 -> {19};
19 -> {20};
19 -> {23} [label="onUncaughtException"];
20 -> {21};
20 -> {23} [label="onUncaughtException"];
21 -> {22};
22 -> {23};
23 -> {24};
@@ -123,7 +123,6 @@ finally {
25 -> {26} [color=green];
25 -> {33} [color=red];
26 -> {27};
26 -> {29} [label="onUncaughtException"];
27 -> {28};
28 -> {29};
29 -> {30};
@@ -212,7 +211,6 @@ finally {
50 -> {51} [color=green];
50 -> {58} [color=red];
51 -> {52};
51 -> {54} [label="onUncaughtException"];
52 -> {53};
53 -> {54};
54 -> {55};
@@ -1103,21 +1103,6 @@ class ControlFlowGraphBuilder {
return node
}
private fun addExceptionEdgesFrom(node: CFGNode<*>) {
if (!node.canThrow) return
val nextCatch = catchNodes.topOrNull()
if (!nextCatch.isNullOrEmpty() && nextCatch.first().level > levelOfNextExceptionCatchingGraph()) {
for (catchEnterNode in nextCatch) {
addEdge(node, catchEnterNode, propagateDeadness = false)
}
}
val nextFinally = finallyEnterNodes.topOrNull()
if (nextFinally != null && nextFinally.level > levelOfNextExceptionCatchingGraph()) {
addEdge(node, nextFinally, propagateDeadness = false, label = UncaughtExceptionPath)
}
}
// Called-in-place function graphs are effectively inlined, exceptions go to enclosing function.
private fun levelOfNextExceptionCatchingGraph(): Int =
graphs.all().first { it.kind != ControlFlowGraph.Kind.AnonymousFunctionCalledInPlace }.exitNode.level
@@ -1228,7 +1213,25 @@ class ControlFlowGraphBuilder {
}
fun exitVariableAssignment(assignment: FirVariableAssignment): VariableAssignmentNode {
return createVariableAssignmentNode(assignment).also { addNewSimpleNode(it) }
val node = createVariableAssignmentNode(assignment).also { addNewSimpleNode(it) }
// Create edges from each assignment to any catch and/or finally blocks. Assignment is the only thing which can downgrade
// smart-casting, and exceptions can be thrown almost anywhere, so it is easier to track assignment than exceptions. This makes sure
// that any smart-cast downgrade which happens in a try block is recognized in catch and finally blocks.
val nextCatch = catchNodes.topOrNull()
if (!nextCatch.isNullOrEmpty()) {
val kind = if (nextCatch.first().level > levelOfNextExceptionCatchingGraph()) EdgeKind.Forward else EdgeKind.DfgForward
for (catchEnterNode in nextCatch) {
addEdge(node, catchEnterNode, preferredKind = kind, propagateDeadness = false)
}
}
val nextFinally = finallyEnterNodes.topOrNull()
if (nextFinally != null) {
val kind = if (nextFinally.level > levelOfNextExceptionCatchingGraph()) EdgeKind.Forward else EdgeKind.DfgForward
addEdge(node, nextFinally, preferredKind = kind, propagateDeadness = false, label = UncaughtExceptionPath)
}
return node
}
fun exitThrowExceptionNode(throwExpression: FirThrowExpression): ThrowExceptionNode {
@@ -1380,7 +1383,6 @@ class ControlFlowGraphBuilder {
private fun addNewSimpleNode(node: CFGNode<*>, isDead: Boolean = false) {
addEdge(lastNodes.pop(), node, preferredKind = if (isDead) EdgeKind.DeadForward else EdgeKind.Forward)
lastNodes.push(node)
addExceptionEdgesFrom(node)
}
private fun addNonSuccessfullyTerminatingNode(node: CFGNode<*>) {
@@ -1388,7 +1390,6 @@ class ControlFlowGraphBuilder {
val stub = createStubNode()
addEdge(node, stub)
lastNodes.push(stub)
addExceptionEdgesFrom(node)
}
private fun popAndAddEdge(to: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) {
@@ -32,8 +32,6 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
@OptIn(CfgInternals::class)
val id = owner.nodeCount++
open val canThrow: Boolean get() = false
// a ---> b ---> d
// \-> c -/
// Normal CFG semantics: a, then either b or c, then d
@@ -653,27 +651,18 @@ class BinaryOrExitNode(owner: ControlFlowGraph, override val fir: FirBinaryLogic
// ----------------------------------- Operator call -----------------------------------
class TypeOperatorCallNode(owner: ControlFlowGraph, override val fir: FirTypeOperatorCall, level: Int) : CFGNode<FirTypeOperatorCall>(owner, level) {
override val canThrow: Boolean
get() = fir.operation == FirOperation.AS
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitTypeOperatorCallNode(this, data)
}
}
class ComparisonExpressionNode(owner: ControlFlowGraph, override val fir: FirComparisonExpression, level: Int) : CFGNode<FirComparisonExpression>(owner, level) {
override val canThrow: Boolean
get() = true // TODO? only overridden compareTo
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitComparisonExpressionNode(this, data)
}
}
class EqualityOperatorCallNode(owner: ControlFlowGraph, override val fir: FirEqualityOperatorCall, level: Int) : AbstractBinaryExitNode<FirEqualityOperatorCall>(owner, level) {
override val canThrow: Boolean
get() = true // TODO? only overridden equals
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitEqualityOperatorCallNode(this, data)
}
@@ -696,9 +685,6 @@ class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExp
class CheckNotNullCallNode(owner: ControlFlowGraph, override val fir: FirCheckNotNullCall, level: Int)
: CFGNode<FirCheckNotNullCall>(owner, level) {
override val canThrow: Boolean
get() = true
override val isUnion: Boolean
get() = true
@@ -714,9 +700,6 @@ class QualifiedAccessNode(
override val fir: FirQualifiedAccessExpression,
level: Int
) : CFGNode<FirQualifiedAccessExpression>(owner, level) {
override val canThrow: Boolean
get() = fir.toResolvedCallableSymbol() is FirPropertySymbol
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitQualifiedAccessNode(this, data)
}
@@ -734,10 +717,6 @@ class ResolvedQualifierNode(
class FunctionCallNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int)
: CFGNode<FirFunctionCall>(owner, level) {
override val canThrow: Boolean
get() = true
override val isUnion: Boolean
get() = true
@@ -768,9 +747,6 @@ class DelegatedConstructorCallNode(owner: ControlFlowGraph, override val fir: Fi
override val isUnion: Boolean
get() = true
override val canThrow: Boolean
get() = true // shouldn't matter since delegated constructor calls aren't wrapped in try-finally, but still
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitDelegatedConstructorCallNode(this, data)
}
@@ -792,9 +768,6 @@ class ThrowExceptionNode(
override val fir: FirThrowExpression,
level: Int
) : CFGNode<FirThrowExpression>(owner, level) {
override val canThrow: Boolean
get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitThrowExceptionNode(this, data)
}
@@ -910,9 +883,6 @@ class FakeExpressionEnterNode(owner: ControlFlowGraph, level: Int) : CFGNode<Fir
// ----------------------------------- Smart-cast node -----------------------------------
class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: FirSmartCastExpression, level: Int) : CFGNode<FirSmartCastExpression>(owner, level) {
override val canThrow: Boolean
get() = fir.resolvedType.isNothing
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitSmartCastExpressionExitNode(this, data)
}
@@ -206,7 +206,6 @@ digraph singleReturnFromTry_kt {
54 -> {55} [color=green];
54 -> {63} [style=dotted];
55 -> {56} [style=dotted];
56 -> {59} [style=dotted label="onUncaughtException"];
56 -> {57} [style=dotted];
57 -> {58} [style=dotted];
58 -> {59} [style=dotted];
@@ -384,8 +383,7 @@ digraph singleReturnFromTry_kt {
104 -> {105} [color=green];
104 -> {119} [color=red];
105 -> {106} [style=dotted];
106 -> {115} [style=dotted label="onUncaughtException"];
106 -> {107 109} [style=dotted];
106 -> {107} [style=dotted];
107 -> {108} [style=dotted];
108 -> {109 115} [style=dotted];
109 -> {110};
@@ -0,0 +1,502 @@
digraph smartCastInCatch_fir_kt {
graph [nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter file smartCastInCatch.fir.kt" style="filled" fillcolor=red];
1 [label="Exit file smartCastInCatch.fir.kt" style="filled" fillcolor=red];
}
0 -> {1} [color=green];
subgraph cluster_1 {
color=red
2 [label="Enter function exc" style="filled" fillcolor=red];
subgraph cluster_2 {
color=blue
3 [label="Enter block"];
subgraph cluster_3 {
color=blue
4 [label="Enter when"];
subgraph cluster_4 {
color=blue
5 [label="Enter when branch condition "];
6 [label="Access variable R|<local>/flag|"];
7 [label="Exit when branch condition"];
}
8 [label="Synthetic else branch"];
9 [label="Enter when branch result"];
subgraph cluster_5 {
color=blue
10 [label="Enter block"];
11 [label="Function call: R|java/lang/RuntimeException.RuntimeException|()" style="filled" fillcolor=yellow];
12 [label="Throw: throw R|java/lang/RuntimeException.RuntimeException|()"];
13 [label="Stub" style="filled" fillcolor=gray];
14 [label="Exit block" style="filled" fillcolor=gray];
}
15 [label="Exit when branch result" style="filled" fillcolor=gray];
16 [label="Exit when"];
}
17 [label="Exit block"];
}
18 [label="Exit function exc" style="filled" fillcolor=red];
}
2 -> {3};
3 -> {4};
4 -> {5};
5 -> {6};
6 -> {7};
7 -> {8 9};
8 -> {16};
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {13} [style=dotted];
13 -> {14} [style=dotted];
14 -> {15} [style=dotted];
15 -> {16} [style=dotted];
16 -> {17};
17 -> {18};
subgraph cluster_6 {
color=red
19 [label="Enter function notNull" style="filled" fillcolor=red];
subgraph cluster_7 {
color=blue
20 [label="Enter block"];
21 [label="Function call: this@R|/notNull|.R|kotlin/Any.toString|()" style="filled" fillcolor=yellow];
22 [label="Jump: ^notNull this@R|/notNull|.R|kotlin/Any.toString|()"];
23 [label="Stub" style="filled" fillcolor=gray];
24 [label="Exit block" style="filled" fillcolor=gray];
}
25 [label="Exit function notNull" style="filled" fillcolor=red];
}
19 -> {20};
20 -> {21};
21 -> {22};
22 -> {25};
22 -> {23} [style=dotted];
23 -> {24} [style=dotted];
24 -> {25} [style=dotted];
subgraph cluster_8 {
color=red
26 [label="Enter function test" style="filled" fillcolor=red];
subgraph cluster_9 {
color=blue
27 [label="Enter block"];
28 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
29 [label="Const: String()"];
30 [label="Assignment: R|<local>/x|"];
subgraph cluster_10 {
color=blue
31 [label="Try expression enter"];
subgraph cluster_11 {
color=blue
32 [label="Try main block enter"];
subgraph cluster_12 {
color=blue
33 [label="Enter block"];
34 [label="Const: Null(null)"];
35 [label="Assignment: R|<local>/x|"];
36 [label="Access variable R|<local>/flag|"];
37 [label="Function call: R|/exc|(...)" style="filled" fillcolor=yellow];
38 [label="Const: Int(1)"];
39 [label="Assignment: R|<local>/x|"];
40 [label="Access variable R|<local>/flag|"];
41 [label="Function call: R|<local>/flag|.R|kotlin/Boolean.not|()" style="filled" fillcolor=yellow];
42 [label="Function call: R|/exc|(...)" style="filled" fillcolor=yellow];
43 [label="Const: String()"];
44 [label="Assignment: R|<local>/x|"];
45 [label="Exit block"];
}
46 [label="Try main block exit"];
}
subgraph cluster_13 {
color=blue
47 [label="Catch enter"];
48 [label="Variable declaration: e: R|kotlin/Throwable|"];
subgraph cluster_14 {
color=blue
49 [label="Enter block"];
50 [label="Access variable R|<local>/x|"];
51 [label="Smart cast: R|<local>/x|"];
52 [label="Access variable <Unresolved name: length>#"];
53 [label="Access variable R|<local>/x|"];
54 [label="Smart cast: R|<local>/x|"];
55 [label="Function call: R|<local>/x|.<Unresolved name: inc>#()" style="filled" fillcolor=yellow];
56 [label="Exit block"];
}
57 [label="Catch exit"];
}
58 [label="Try expression exit"];
}
59 [label="Exit block"];
}
60 [label="Exit function test" style="filled" fillcolor=red];
}
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {31};
31 -> {32 47};
32 -> {33};
33 -> {34};
34 -> {35};
35 -> {36 47};
36 -> {37};
37 -> {38};
38 -> {39};
39 -> {40 47};
40 -> {41};
41 -> {42};
42 -> {43};
43 -> {44};
44 -> {45 47};
45 -> {46};
46 -> {47 58};
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52};
52 -> {53};
53 -> {54};
54 -> {55};
55 -> {56};
56 -> {57};
57 -> {58};
58 -> {59};
59 -> {60};
subgraph cluster_15 {
color=red
61 [label="Enter function testGetClassThrows" style="filled" fillcolor=red];
subgraph cluster_16 {
color=blue
62 [label="Enter block"];
63 [label="Access qualifier kotlin/String"];
64 [label="::class call"];
65 [label="Variable declaration: lvar x: R|kotlin/reflect/KClass<kotlin/String>?|"];
66 [label="Access variable R|<local>/x|"];
67 [label="Type operator: (R|<local>/x| as R|kotlin/reflect/KClass<kotlin/String>|)"];
subgraph cluster_17 {
color=blue
68 [label="Try expression enter"];
subgraph cluster_18 {
color=blue
69 [label="Try main block enter"];
subgraph cluster_19 {
color=blue
70 [label="Enter block"];
71 [label="Const: Null(null)"];
72 [label="Assignment: R|<local>/x|"];
73 [label="Access qualifier kotlin/String"];
74 [label="::class call"];
75 [label="Assignment: R|<local>/x|"];
76 [label="Exit block"];
}
77 [label="Try main block exit"];
}
subgraph cluster_20 {
color=blue
78 [label="Catch enter"];
79 [label="Variable declaration: e: R|kotlin/Throwable|"];
subgraph cluster_21 {
color=blue
80 [label="Enter block"];
81 [label="Access variable R|<local>/x|"];
82 [label="Function call: R|<local>/x|.R|/notNull<Inapplicable(UNSAFE_CALL): /notNull>#|()" style="filled" fillcolor=yellow];
83 [label="Exit block"];
}
84 [label="Catch exit"];
}
85 [label="Try expression exit"];
}
86 [label="Exit block"];
}
87 [label="Exit function testGetClassThrows" style="filled" fillcolor=red];
}
61 -> {62};
62 -> {63};
63 -> {64};
64 -> {65};
65 -> {66};
66 -> {67};
67 -> {68};
68 -> {69 78};
69 -> {70};
70 -> {71};
71 -> {72};
72 -> {73 78};
73 -> {74};
74 -> {75};
75 -> {76 78};
76 -> {77};
77 -> {78 85};
78 -> {79};
79 -> {80};
80 -> {81};
81 -> {82};
82 -> {83};
83 -> {84};
84 -> {85};
85 -> {86};
86 -> {87};
subgraph cluster_22 {
color=red
88 [label="Enter function testMemberReferenceThrows" style="filled" fillcolor=red];
subgraph cluster_23 {
color=blue
89 [label="Enter block"];
90 [label="Const: String()"];
91 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
92 [label="Access variable R|<local>/x|"];
93 [label="Type operator: (R|<local>/x| as R|kotlin/Any|)"];
subgraph cluster_24 {
color=blue
94 [label="Try expression enter"];
subgraph cluster_25 {
color=blue
95 [label="Try main block enter"];
subgraph cluster_26 {
color=blue
96 [label="Enter block"];
97 [label="Const: Null(null)"];
98 [label="Assignment: R|<local>/x|"];
99 [label="Access qualifier kotlin/String"];
100 [label="Callable reference: Q|kotlin/String|::R|kotlin/String.length|"];
101 [label="Assignment: R|<local>/x|"];
102 [label="Exit block"];
}
103 [label="Try main block exit"];
}
subgraph cluster_27 {
color=blue
104 [label="Catch enter"];
105 [label="Variable declaration: ex: R|kotlin/Throwable|"];
subgraph cluster_28 {
color=blue
106 [label="Enter block"];
107 [label="Access variable R|<local>/x|"];
108 [label="Function call: R|<local>/x|.R|/notNull<Inapplicable(UNSAFE_CALL): /notNull>#|()" style="filled" fillcolor=yellow];
109 [label="Exit block"];
}
110 [label="Catch exit"];
}
111 [label="Try expression exit"];
}
112 [label="Exit block"];
}
113 [label="Exit function testMemberReferenceThrows" style="filled" fillcolor=red];
}
88 -> {89};
89 -> {90};
90 -> {91};
91 -> {92};
92 -> {93};
93 -> {94};
94 -> {95 104};
95 -> {96};
96 -> {97};
97 -> {98};
98 -> {99 104};
99 -> {100};
100 -> {101};
101 -> {102 104};
102 -> {103};
103 -> {104 111};
104 -> {105};
105 -> {106};
106 -> {107};
107 -> {108};
108 -> {109};
109 -> {110};
110 -> {111};
111 -> {112};
112 -> {113};
subgraph cluster_29 {
color=red
114 [label="Enter function testExceptionBeforeLambda" style="filled" fillcolor=red];
subgraph cluster_30 {
color=blue
115 [label="Enter block"];
116 [label="Const: String()"];
117 [label="Variable declaration: lvar x: R|kotlin/String?|"];
118 [label="Access variable R|<local>/x|"];
119 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
subgraph cluster_31 {
color=blue
120 [label="Try expression enter"];
subgraph cluster_32 {
color=blue
121 [label="Try main block enter"];
subgraph cluster_33 {
color=blue
122 [label="Enter block"];
123 [label="Const: Null(null)"];
124 [label="Assignment: R|<local>/x|"];
125 [label="Postponed enter to lambda"];
subgraph cluster_34 {
color=blue
126 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_35 {
color=blue
127 [label="Enter block"];
128 [label="Const: String()"];
129 [label="Assignment: R|<local>/x|"];
130 [label="Exit block"];
}
131 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
132 [label="Postponed exit from lambda"];
133 [label="Function call: R|kotlin/run|<R|kotlin/Unit|>(...)" style="filled" fillcolor=yellow];
134 [label="Exit block"];
}
135 [label="Try main block exit"];
}
subgraph cluster_36 {
color=blue
136 [label="Catch enter"];
137 [label="Variable declaration: ex: R|kotlin/Throwable|"];
subgraph cluster_37 {
color=blue
138 [label="Enter block"];
139 [label="Access variable R|<local>/x|"];
140 [label="Function call: R|<local>/x|.R|/notNull<Inapplicable(UNSAFE_CALL): /notNull>#|()" style="filled" fillcolor=yellow];
141 [label="Exit block"];
}
142 [label="Catch exit"];
}
143 [label="Try expression exit"];
}
144 [label="Exit block"];
}
145 [label="Exit function testExceptionBeforeLambda" style="filled" fillcolor=red];
}
114 -> {115};
115 -> {116};
116 -> {117};
117 -> {118};
118 -> {119};
119 -> {120};
120 -> {121 136};
121 -> {122};
122 -> {123};
123 -> {124};
124 -> {125 136};
125 -> {126 133};
125 -> {132} [style=dotted];
125 -> {126} [style=dashed];
126 -> {127};
127 -> {128};
128 -> {129};
129 -> {130 136};
130 -> {131};
131 -> {132};
132 -> {133} [color=green];
132 -> {143} [color=red];
133 -> {134};
134 -> {135};
135 -> {136 143};
136 -> {137};
137 -> {138};
138 -> {139};
139 -> {140};
140 -> {141};
141 -> {142};
142 -> {143};
143 -> {144};
144 -> {145};
subgraph cluster_38 {
color=red
146 [label="Enter function testExceptionWithinLocalFunction" style="filled" fillcolor=red];
subgraph cluster_39 {
color=blue
147 [label="Enter block"];
148 [label="Const: String()"];
149 [label="Variable declaration: lvar x: R|kotlin/Any|"];
150 [label="Access variable R|<local>/x|"];
151 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
subgraph cluster_40 {
color=blue
152 [label="Try expression enter"];
subgraph cluster_41 {
color=blue
153 [label="Try main block enter"];
subgraph cluster_42 {
color=blue
154 [label="Enter block"];
155 [label="Local function declaration"];
156 [label="Exit block"];
}
157 [label="Try main block exit"];
}
subgraph cluster_43 {
color=blue
158 [label="Enter function local" style="filled" fillcolor=red];
subgraph cluster_44 {
color=blue
159 [label="Enter block"];
160 [label="Const: Int(1)"];
161 [label="Assignment: R|<local>/x|"];
162 [label="Exit block"];
}
163 [label="Exit function local" style="filled" fillcolor=red];
}
subgraph cluster_45 {
color=blue
164 [label="Catch enter"];
165 [label="Variable declaration: e: R|kotlin/Exception|"];
subgraph cluster_46 {
color=blue
166 [label="Enter block"];
167 [label="Access variable R|<local>/x|"];
168 [label="Smart cast: R|<local>/x|"];
169 [label="Access variable <Unresolved name: length>#"];
170 [label="Exit block"];
}
171 [label="Catch exit"];
}
172 [label="Try expression exit"];
}
173 [label="Exit block"];
}
174 [label="Exit function testExceptionWithinLocalFunction" style="filled" fillcolor=red];
}
146 -> {147};
147 -> {148};
148 -> {149};
149 -> {150};
150 -> {151};
151 -> {152};
152 -> {153 164};
153 -> {154};
154 -> {155};
155 -> {156 158};
155 -> {158} [style=dashed];
156 -> {157};
157 -> {164 172};
158 -> {159};
159 -> {160};
160 -> {161};
161 -> {162};
161 -> {164} [color=red];
162 -> {163};
164 -> {165};
165 -> {166};
166 -> {167};
167 -> {168};
168 -> {169};
169 -> {170};
170 -> {171};
171 -> {172};
172 -> {173};
173 -> {174};
}
@@ -1,9 +1,14 @@
// !SKIP_TXT
// DUMP_CFG
import kotlin.reflect.KClass
fun exc(flag: Boolean) {
if (flag) throw RuntimeException()
}
fun Any.notNull() = toString()
fun test(flag: Boolean) {
var x: Any?
x = ""
@@ -19,3 +24,52 @@ fun test(flag: Boolean) {
x.<!UNRESOLVED_REFERENCE!>inc<!>()
}
}
fun testGetClassThrows() {
var x: KClass<String>? = String::class
x as KClass<String>
try {
x = null
x = String::class
} catch (e: Throwable) {
// bad - get class call can throw
x<!UNSAFE_CALL!>.<!>notNull()
}
}
fun testMemberReferenceThrows() {
var x: Any? = ""
x as Any
try {
x = null
x = String::length
} catch (ex: Throwable) {
// bad - get callable reference throw
x<!UNSAFE_CALL!>.<!>notNull()
}
}
fun testExceptionBeforeLambda() {
var x: String? = ""
x as String
try {
x = null
run { x = "" }
} catch (ex: Throwable) {
// bad - `run` could throw before running lambda
x<!UNSAFE_CALL!>.<!>notNull()
}
}
fun testExceptionWithinLocalFunction() {
var x: Any = ""
x as String
try {
fun local() {
x = 1
}
} catch (e: Exception) {
// bad - `local` could be run and reset smartcasting
x.<!UNRESOLVED_REFERENCE!>length<!>
}
}
@@ -1,9 +1,14 @@
// !SKIP_TXT
// DUMP_CFG
import kotlin.reflect.KClass
fun exc(flag: Boolean) {
if (flag) throw RuntimeException()
}
fun Any.notNull() = toString()
fun test(flag: Boolean) {
var x: Any?
x = ""
@@ -19,3 +24,52 @@ fun test(flag: Boolean) {
x.<!UNRESOLVED_REFERENCE!>inc<!>()
}
}
fun testGetClassThrows() {
var x: KClass<String>? = String::class
x as KClass<String>
try {
<!UNUSED_VALUE!>x =<!> null
x = String::class
} catch (e: Throwable) {
// bad - get class call can throw
<!DEBUG_INFO_SMARTCAST!>x<!>.notNull()
}
}
fun testMemberReferenceThrows() {
var x: Any? = ""
x as Any
try {
<!UNUSED_VALUE!>x =<!> null
x = String::length
} catch (ex: Throwable) {
// bad - get callable reference throw
<!DEBUG_INFO_SMARTCAST!>x<!>.notNull()
}
}
fun testExceptionBeforeLambda() {
var x: String? = ""
x as String
try {
x = null
run { x = "" }
} catch (ex: Throwable) {
// bad - `run` could throw before running lambda
<!SMARTCAST_IMPOSSIBLE!>x<!>.notNull()
}
}
fun testExceptionWithinLocalFunction() {
var x: Any = ""
x as String
try {
fun local() {
x = 1
}
} catch (e: Exception) {
// bad - `local` could be run and reset smartcasting
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
}
}
@@ -179,9 +179,7 @@ digraph castInTryWithJump_fir_kt {
25 -> {26};
26 -> {27};
27 -> {28};
27 -> {33} [label="onUncaughtException"];
28 -> {29};
28 -> {33} [label="onUncaughtException"];
29 -> {33} [label="break"];
29 -> {30} [style=dotted];
30 -> {31} [style=dotted];
@@ -194,9 +192,7 @@ digraph castInTryWithJump_fir_kt {
36 -> {37};
37 -> {38};
38 -> {39};
38 -> {42} [label="onUncaughtException"];
39 -> {40};
39 -> {42} [label="onUncaughtException"];
40 -> {41};
41 -> {42};
42 -> {43};
@@ -307,10 +303,8 @@ digraph castInTryWithJump_fir_kt {
81 -> {97} [label="onUncaughtException"];
82 -> {83};
83 -> {84};
84 -> {85 88};
84 -> {97} [label="onUncaughtException"];
85 -> {86 88};
85 -> {97} [label="onUncaughtException"];
84 -> {85};
85 -> {86};
86 -> {87};
87 -> {88 97};
88 -> {89};
@@ -318,9 +312,7 @@ digraph castInTryWithJump_fir_kt {
89 -> {90};
90 -> {91};
91 -> {92};
91 -> {97} [label="onUncaughtException"];
92 -> {93};
92 -> {97} [label="onUncaughtException"];
93 -> {97} [label="return@/returnInCatch"];
93 -> {94} [style=dotted];
94 -> {95} [style=dotted];
@@ -458,9 +450,7 @@ digraph castInTryWithJump_fir_kt {
119 -> {120};
120 -> {121};
121 -> {122};
121 -> {125} [label="onUncaughtException"];
122 -> {123};
122 -> {125} [label="onUncaughtException"];
123 -> {124};
124 -> {125};
125 -> {126};
@@ -469,10 +459,8 @@ digraph castInTryWithJump_fir_kt {
127 -> {143} [label="onUncaughtException"];
128 -> {129};
129 -> {130};
130 -> {131 134};
130 -> {143} [label="onUncaughtException"];
131 -> {132 134};
131 -> {143} [label="onUncaughtException"];
130 -> {131};
131 -> {132};
132 -> {133};
133 -> {134 143};
134 -> {135};
@@ -480,9 +468,7 @@ digraph castInTryWithJump_fir_kt {
135 -> {136};
136 -> {137};
137 -> {138};
137 -> {143} [label="onUncaughtException"];
138 -> {139};
138 -> {143} [label="onUncaughtException"];
139 -> {143} [label="return@/returnInCatch_insideFinally"];
139 -> {140} [style=dotted];
140 -> {141} [style=dotted];
@@ -629,10 +615,8 @@ digraph castInTryWithJump_fir_kt {
187 -> {203} [label="onUncaughtException"];
188 -> {189};
189 -> {190};
190 -> {191 194};
190 -> {203} [label="onUncaughtException"];
191 -> {192 194};
191 -> {203} [label="onUncaughtException"];
190 -> {191};
191 -> {192};
192 -> {193};
193 -> {194 203};
194 -> {195};
@@ -640,9 +624,7 @@ digraph castInTryWithJump_fir_kt {
195 -> {196};
196 -> {197};
197 -> {198};
197 -> {203} [label="onUncaughtException"];
198 -> {199};
198 -> {203} [label="onUncaughtException"];
199 -> {203} [label="break"];
199 -> {200} [style=dotted];
200 -> {201} [style=dotted];
@@ -789,32 +771,24 @@ digraph castInTryWithJump_fir_kt {
237 -> {238};
238 -> {239};
239 -> {240};
239 -> {264} [label="onUncaughtException"];
240 -> {241};
240 -> {264} [label="onUncaughtException"];
241 -> {242};
241 -> {249} [label="onUncaughtException"];
242 -> {243};
243 -> {244};
244 -> {245};
244 -> {249} [label="onUncaughtException"];
245 -> {246};
246 -> {247};
246 -> {249} [label="onUncaughtException"];
247 -> {248};
248 -> {249};
249 -> {250};
250 -> {251};
251 -> {252};
251 -> {264} [label="onUncaughtException"];
252 -> {253};
253 -> {254};
253 -> {264} [label="onUncaughtException"];
254 -> {255};
254 -> {264} [label="onUncaughtException"];
255 -> {256};
256 -> {257};
256 -> {264} [label="onUncaughtException"];
257 -> {264} [label="return@/returnInFinally_insideTry_nonLocal"];
257 -> {258} [style=dotted];
258 -> {259} [style=dotted];