Skip to content

Commit 0c3a1a9

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Migration: integrate flow analysis of try/catch/finally statements.
Change-Id: I2e826fc28c8acc5abae23e318cc21ec8053251a8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114552 Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 69c6bea commit 0c3a1a9

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

pkg/nnbd_migration/lib/src/edge_builder.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
391391

392392
@override
393393
DecoratedType visitCatchClause(CatchClause node) {
394+
_flowAnalysis.tryCatchStatement_catchBegin();
394395
node.exceptionType?.accept(this);
395396
for (var identifier in [
396397
node.exceptionParameter,
@@ -404,6 +405,7 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
404405
// The catch clause may not execute, so create a new scope for
405406
// post-dominators.
406407
_postDominatedLocals.doScoped(action: () => node.body.accept(this));
408+
_flowAnalysis.tryCatchStatement_catchEnd();
407409
return null;
408410
}
409411

@@ -1134,6 +1136,32 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
11341136
return null;
11351137
}
11361138

1139+
@override
1140+
DecoratedType visitTryStatement(TryStatement node) {
1141+
var finallyBlock = node.finallyBlock;
1142+
if (finallyBlock != null) {
1143+
_flowAnalysis.tryFinallyStatement_bodyBegin();
1144+
}
1145+
var catchClauses = node.catchClauses;
1146+
if (catchClauses.isNotEmpty) {
1147+
_flowAnalysis.tryCatchStatement_bodyBegin();
1148+
}
1149+
var body = node.body;
1150+
body.accept(this);
1151+
var assignedInBody = _assignedVariables[body];
1152+
if (catchClauses.isNotEmpty) {
1153+
_flowAnalysis.tryCatchStatement_bodyEnd(assignedInBody);
1154+
catchClauses.accept(this);
1155+
_flowAnalysis.tryCatchStatement_end();
1156+
}
1157+
if (finallyBlock != null) {
1158+
_flowAnalysis.tryFinallyStatement_finallyBegin(assignedInBody);
1159+
finallyBlock.accept(this);
1160+
_flowAnalysis.tryFinallyStatement_end(_assignedVariables[finallyBlock]);
1161+
}
1162+
return null;
1163+
}
1164+
11371165
@override
11381166
DecoratedType visitTypeName(TypeName typeName) {
11391167
var typeArguments = typeName.typeArguments?.arguments;

pkg/nnbd_migration/test/edge_builder_flow_analysis_test.dart

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,78 @@ void h(int k) {}
230230
assertEdge(iNode, jNode, hard: false);
231231
}
232232

233+
test_catch_cancels_promotions_based_on_assignments_in_body() async {
234+
await analyze('''
235+
void f(int i) {
236+
if (i == null) return;
237+
try {
238+
g(i);
239+
i = null;
240+
if (i == null) return;
241+
g(i);
242+
} catch (_) {
243+
h(i);
244+
}
245+
}
246+
void g(int j) {}
247+
void h(int k) {}
248+
''');
249+
var iNode = decoratedTypeAnnotation('int i').node;
250+
var jNode = decoratedTypeAnnotation('int j').node;
251+
var kNode = decoratedTypeAnnotation('int k').node;
252+
// No edge from i to j because i is promoted at the time of both calls to g.
253+
assertNoEdge(iNode, jNode);
254+
// But there is an edge from i to k, because there is no guarantee that i is
255+
// promoted at all times during the execution of the try block.
256+
assertEdge(iNode, kNode, hard: false);
257+
}
258+
259+
test_catch_falls_through_to_after_try() async {
260+
await analyze('''
261+
void f(int i) {
262+
try {
263+
g(i);
264+
return;
265+
} catch (_) {
266+
if (i == null) return;
267+
}
268+
h(i);
269+
}
270+
void g(int j) {}
271+
void h(int k) {}
272+
''');
273+
var iNode = decoratedTypeAnnotation('int i').node;
274+
var jNode = decoratedTypeAnnotation('int j').node;
275+
var kNode = decoratedTypeAnnotation('int k').node;
276+
// No edge from i to k because i's type is promoted to non-nullable
277+
assertNoEdge(iNode, kNode);
278+
// But there is an edge from i to j.
279+
assertEdge(iNode, jNode, hard: true);
280+
}
281+
282+
test_catch_resets_to_state_before_try() async {
283+
await analyze('''
284+
void f(int i) {
285+
try {
286+
if (i == null) return;
287+
g(i);
288+
} catch (_) {
289+
h(i);
290+
}
291+
}
292+
void g(int j) {}
293+
void h(int k) {}
294+
''');
295+
var iNode = decoratedTypeAnnotation('int i').node;
296+
var jNode = decoratedTypeAnnotation('int j').node;
297+
var kNode = decoratedTypeAnnotation('int k').node;
298+
// No edge from i to j because i's type is promoted to non-nullable
299+
assertNoEdge(iNode, jNode);
300+
// But there is an edge from i to k, since we assume an exception might
301+
// occur at any time during the body of the try.
302+
assertEdge(iNode, kNode, hard: false);
303+
}
304+
233305
test_conditionalExpression() async {
234306
await analyze('''
235307
int f(int i) => i == null ? g(i) : h(i);
@@ -455,6 +527,53 @@ class C {
455527
// field doesn't cause flow analysis to crash.
456528
}
457529

530+
test_finally_promotions_are_preserved() async {
531+
await analyze('''
532+
void f(int i) {
533+
try {
534+
g(i);
535+
} finally {
536+
if (i == null) return;
537+
}
538+
h(i);
539+
}
540+
void g(int j) {}
541+
void h(int k) {}
542+
''');
543+
var iNode = decoratedTypeAnnotation('int i').node;
544+
var jNode = decoratedTypeAnnotation('int j').node;
545+
var kNode = decoratedTypeAnnotation('int k').node;
546+
// No edge from i to k because i's type is promoted to non-nullable in the
547+
// finally block.
548+
assertNoEdge(iNode, kNode);
549+
// But there is an edge from i to j.
550+
assertEdge(iNode, jNode, hard: true);
551+
}
552+
553+
test_finally_temporarily_resets_to_state_before_try() async {
554+
await analyze('''
555+
void f(int i) {
556+
try {
557+
if (i == null) return;
558+
g(i);
559+
} finally {
560+
h(i);
561+
}
562+
}
563+
void g(int j) {}
564+
void h(int k) {}
565+
''');
566+
var iNode = decoratedTypeAnnotation('int i').node;
567+
var jNode = decoratedTypeAnnotation('int j').node;
568+
var kNode = decoratedTypeAnnotation('int k').node;
569+
// No edge from i to j because i's type is promoted to non-nullable in the
570+
// try-block.
571+
assertNoEdge(iNode, jNode);
572+
// But there is an edge from i to k, since we assume an exception might
573+
// occur at any time during the body of the try.
574+
assertEdge(iNode, kNode, hard: false);
575+
}
576+
458577
test_for_break_target() async {
459578
await analyze('''
460579
void f(int i) {
@@ -995,6 +1114,29 @@ bool b3 = b1 || b2;
9951114
// top level variable doesn't cause flow analysis to crash.
9961115
}
9971116

1117+
test_try_falls_through_to_after_try() async {
1118+
await analyze('''
1119+
void f(int i) {
1120+
try {
1121+
g(i);
1122+
if (i == null) return;
1123+
} catch (_) {
1124+
return;
1125+
}
1126+
h(i);
1127+
}
1128+
void g(int j) {}
1129+
void h(int k) {}
1130+
''');
1131+
var iNode = decoratedTypeAnnotation('int i').node;
1132+
var jNode = decoratedTypeAnnotation('int j').node;
1133+
var kNode = decoratedTypeAnnotation('int k').node;
1134+
// No edge from i to k because i's type is promoted to non-nullable
1135+
assertNoEdge(iNode, kNode);
1136+
// But there is an edge from i to j.
1137+
assertEdge(iNode, jNode, hard: true);
1138+
}
1139+
9981140
test_while_break_target() async {
9991141
await analyze('''
10001142
void f(int i) {

0 commit comments

Comments
 (0)