Skip to content

Commit 61790a1

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Flow analysis: add support for non-null assertion operator.
Change-Id: I7a60944a4389b6f7c8e182d5c9b402fcbb05b624 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121100 Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
1 parent 4e9220e commit 61790a1

File tree

6 files changed

+53
-1
lines changed

6 files changed

+53
-1
lines changed

pkg/analyzer/lib/src/generated/resolver.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4348,6 +4348,15 @@ class ResolverVisitor extends ScopedVisitor {
43484348
_flowAnalysis?.flow?.parenthesizedExpression(node, node.expression);
43494349
}
43504350

4351+
@override
4352+
void visitPostfixExpression(PostfixExpression node) {
4353+
super.visitPostfixExpression(node);
4354+
4355+
if (node.operator.type == TokenType.BANG) {
4356+
_flowAnalysis?.flow?.nonNullAssert_end(node.operand);
4357+
}
4358+
}
4359+
43514360
@override
43524361
void visitPrefixedIdentifier(PrefixedIdentifier node) {
43534362
//

pkg/front_end/lib/src/fasta/flow_analysis/flow_analysis.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,16 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
543543
conditionInfo._ifTrue));
544544
}
545545

546+
/// Call this method just after visiting a non-null assertion (`x!`)
547+
/// expression.
548+
void nonNullAssert_end(Expression operand) {
549+
_ExpressionInfo<Variable, Type> operandInfo = _getExpressionInfo(operand);
550+
if (operandInfo is _VariableReadInfo<Variable, Type>) {
551+
_current =
552+
_current.markNonNullable(typeOperations, operandInfo._variable);
553+
}
554+
}
555+
546556
/// Call this method when encountering an expression that is a `null` literal.
547557
void nullLiteral(Expression expression) {
548558
_storeExpressionInfo(expression, new _NullInfo(_current));
@@ -730,7 +740,9 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
730740
/// returned.
731741
_ExpressionInfo<Variable, Type> _getExpressionInfo(Expression expression) {
732742
if (identical(expression, _expressionWithInfo)) {
733-
return _expressionInfo;
743+
_ExpressionInfo<Variable, Type> expressionInfo = _expressionInfo;
744+
_expressionInfo = null;
745+
return expressionInfo;
734746
} else {
735747
return null;
736748
}

pkg/front_end/test/fasta/flow_analysis/flow_analysis_test.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,18 @@ main() {
768768
});
769769
});
770770

771+
test('nonNullAssert_end(x) promotes', () {
772+
var h = _Harness();
773+
var x = h.addVar('x', 'int?');
774+
h.run((flow) {
775+
h.declare(x, initialized: true);
776+
var varExpr = _Expression();
777+
flow.variableRead(varExpr, x);
778+
flow.nonNullAssert_end(varExpr);
779+
expect(flow.promotedType(x).type, 'int');
780+
});
781+
});
782+
771783
test('parenthesizedExpression preserves promotion behaviors', () {
772784
var h = _Harness();
773785
var x = h.addVar('x', 'int?');
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
void bang_promotes(int? x) {
6+
x!;
7+
/*nonNullable*/ x;
8+
}

pkg/nnbd_migration/lib/src/fix_builder.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ abstract class FixBuilder extends GeneralizingAstVisitor<DartType> {
277277
var type = subexpression.accept(this);
278278
if (_doesAssignmentNeedCheck(from: type, to: contextType)) {
279279
addNullCheck(subexpression);
280+
_flowAnalysis.nonNullAssert_end(subexpression);
280281
return _typeSystem.promoteToNonNull(type as TypeImpl);
281282
} else {
282283
return type;

pkg/nnbd_migration/test/fix_builder_test.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,16 @@ f() => 1;
529529
visitSubexpression(findNode.integerLiteral('1'), 'int');
530530
}
531531

532+
test_nullAssertion_promotes() async {
533+
await analyze('''
534+
_f(bool/*?*/ x) => x && x;
535+
''');
536+
// Only the first `x` is null-checked because thereafter, the type of `x` is
537+
// promoted to `bool`.
538+
visitSubexpression(findNode.binary('&&'), 'bool',
539+
nullChecked: {findNode.simple('x &&')});
540+
}
541+
532542
test_nullLiteral() async {
533543
await analyze('''
534544
f() => null;

0 commit comments

Comments
 (0)