Flow analysis: unit test that join variables are usable and promotable.
These tests fill a coverage gap in the flow analysis unit
tests. Previously we tested that "join" variables were created by
logical-or patterns and switch cases that share a body, but we didn't
have any tests to verify that those variables could be used.
We now verify that those variables can be read from and promoted.
Change-Id: Ic8948cd307edff429aea9007183d65cc3a770ef3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/322360
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index 40935d9..9914372 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -7882,6 +7882,22 @@
]),
]);
});
+
+ test('Join variable is promotable', () {
+ var x1 = Var('x', identity: 'x1');
+ var x2 = Var('x', identity: 'x2');
+ var x = PatternVariableJoin('x', expectedComponents: [x1, x2]);
+ h.run([
+ ifCase(
+ expr('int?'),
+ x1.pattern(type: 'int?').nullCheck.or(x2.pattern(type: 'int?')),
+ [
+ checkNotPromoted(x),
+ x.nonNullAssert,
+ checkPromoted(x, 'int'),
+ ]),
+ ]);
+ });
});
group(
@@ -7894,9 +7910,13 @@
// it's not actually assigned on both sides of the or-pattern) because
// this avoids redundant errors.
h.run([
- ifCase(expr('int?'),
+ ifCase(expr('num?'),
(x1.pattern().nullCheck.or(wildcard()))..errorId = 'OR', [
checkAssigned(x, true),
+ // Also verify that the join variable is promotable
+ checkNotPromoted(x),
+ x.as_('int'),
+ checkPromoted(x, 'int'),
]),
], expectedErrors: {
'logicalOrPatternBranchMissingVariable(node: OR, hasInLeft: true, '
@@ -7914,6 +7934,10 @@
ifCase(expr('int?'),
(wildcard().nullCheck.or(x1.pattern()))..errorId = 'OR', [
checkAssigned(x, true),
+ // Also verify that the join variable is promotable
+ checkNotPromoted(x),
+ x.nonNullAssert,
+ checkPromoted(x, 'int'),
]),
], expectedErrors: {
'logicalOrPatternBranchMissingVariable(node: OR, hasInLeft: false, '
@@ -9783,6 +9807,24 @@
]),
]);
});
+
+ test('Join variable is promotable', () {
+ var x1 = Var('x', identity: 'x1');
+ var x2 = Var('x', identity: 'x2');
+ var x = PatternVariableJoin('x', expectedComponents: [x1, x2]);
+ h.run([
+ switch_(expr('int?'), [
+ switchStatementMember([
+ x1.pattern(type: 'int?').nullCheck,
+ x2.pattern(type: 'int?')
+ ], [
+ checkNotPromoted(x),
+ x.nonNullAssert,
+ checkPromoted(x, 'int'),
+ ]),
+ ])
+ ]);
+ });
});
group(
@@ -9795,12 +9837,16 @@
// not actually assigned by both patterns) because this avoids
// redundant errors.
h.run([
- switch_(expr('int?'), [
+ switch_(expr('num?'), [
switchStatementMember([
x1.pattern().nullCheck,
wildcard()
], [
checkAssigned(x, true),
+ // Also verify that the join variable is promotable
+ checkNotPromoted(x),
+ x.as_('int'),
+ checkPromoted(x, 'int'),
])
]),
]);
@@ -9819,6 +9865,10 @@
x1.pattern()
], [
checkAssigned(x, true),
+ // Also verify that the join variable is promotable
+ checkNotPromoted(x),
+ x.nonNullAssert,
+ checkPromoted(x, 'int'),
])
]),
]);
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 9ee37b7..46c619d 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -1208,7 +1208,7 @@
@override
void preVisit(PreVisitor visitor) {
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.casePatternStart();
pattern.preVisit(visitor, variableBinder, isInAssignment: false);
variableBinder.casePatternFinish();
@@ -1395,7 +1395,7 @@
void _preVisit(PreVisitor visitor) {
final guardedPattern = this.guardedPattern;
if (guardedPattern != null) {
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.casePatternStart();
guardedPattern.pattern
.preVisit(visitor, variableBinder, isInAssignment: false);
@@ -1904,7 +1904,7 @@
@override
void preVisit(PreVisitor visitor) {
expression.preVisit(visitor);
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.casePatternStart();
pattern.preVisit(visitor, variableBinder, isInAssignment: false);
_candidateVariables = variableBinder.casePatternFinish();
@@ -1950,7 +1950,7 @@
@override
void preVisit(PreVisitor visitor) {
expression.preVisit(visitor);
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.casePatternStart();
pattern.preVisit(visitor, variableBinder, isInAssignment: false);
_variables = variableBinder.casePatternFinish();
@@ -2581,6 +2581,7 @@
'List<int>': false,
'Never': false,
'num': false,
+ 'num?': false,
'Object': false,
'Object?': false,
'String': false,
@@ -3235,7 +3236,7 @@
@override
void preVisit(PreVisitor visitor) {
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.casePatternStart();
lhs.preVisit(visitor, variableBinder, isInAssignment: true);
variableBinder.casePatternFinish();
@@ -3266,7 +3267,7 @@
void preVisit(PreVisitor visitor) {
expression.preVisit(visitor);
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.casePatternStart();
pattern.preVisit(visitor, variableBinder, isInAssignment: false);
variableBinder.casePatternFinish();
@@ -3314,7 +3315,7 @@
void preVisit(PreVisitor visitor) {
expression.preVisit(visitor);
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.casePatternStart();
pattern.preVisit(visitor, variableBinder, isInAssignment: false);
variableBinder.casePatternFinish();
@@ -3401,6 +3402,7 @@
void _handleJoin({
required List<Var> components,
required JoinedPatternVariableInconsistency inconsistency,
+ required PreVisitor visitor,
}) {
expect(isJoined, false);
expect(components.map((c) => c.identity),
@@ -3409,6 +3411,7 @@
expect(components, expectedComponents, reason: 'at $location');
this.inconsistency = inconsistency;
this.isJoined = true;
+ visitor._assignedVariables.declare(this);
}
}
@@ -4192,7 +4195,7 @@
}) : super._();
void _preVisit(PreVisitor visitor) {
- var variableBinder = _VariableBinder(errors: visitor.errors);
+ var variableBinder = _VariableBinder(visitor);
variableBinder.switchStatementSharedCaseScopeStart(this);
for (SwitchHead element in elements) {
if (element is SwitchHeadCase) {
@@ -5863,9 +5866,9 @@
}
class _VariableBinder extends VariableBinder<Node, Var> {
- _VariableBinder({
- required super.errors,
- });
+ final PreVisitor visitor;
+
+ _VariableBinder(this.visitor) : super(errors: visitor.errors);
@override
Var joinPatternVariables({
@@ -5880,6 +5883,7 @@
joinedVariable._handleJoin(
components: components,
inconsistency: inconsistency,
+ visitor: visitor,
);
return joinedVariable;
}