Skip to content

Commit 14d4b93

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Migration: update FixBuilder to handle simple statements.
We now handle expression statements and variable declaration statements where the variable type is an interface type. Change-Id: I00f53a326495bcbeeba785b25c46abf1f3e9ab49 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121260 Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
1 parent 61790a1 commit 14d4b93

File tree

2 files changed

+245
-11
lines changed

2 files changed

+245
-11
lines changed

pkg/nnbd_migration/lib/src/fix_builder.dart

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:analyzer/src/dart/element/type_algebra.dart';
1414
import 'package:analyzer/src/dart/element/type_provider.dart';
1515
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
1616
import 'package:analyzer/src/generated/resolver.dart';
17+
import 'package:analyzer/src/generated/source.dart';
1718
import 'package:front_end/src/fasta/flow_analysis/flow_analysis.dart';
1819
import 'package:meta/meta.dart';
1920
import 'package:nnbd_migration/src/decorated_class_hierarchy.dart';
@@ -91,11 +92,18 @@ abstract class FixBuilder extends GeneralizingAstVisitor<DartType> {
9192
/// inference. This is used to determine when `!` needs to be inserted.
9293
DartType _contextType;
9394

94-
FixBuilder(this._decoratedClassHierarchy, TypeProvider typeProvider,
95-
this._typeSystem, this._variables)
95+
/// The file being analyzed.
96+
final Source source;
97+
98+
FixBuilder(this.source, this._decoratedClassHierarchy,
99+
TypeProvider typeProvider, this._typeSystem, this._variables)
96100
: _typeProvider = (typeProvider as TypeProviderImpl)
97101
.withNullability(NullabilitySuffix.none);
98102

103+
/// Called whenever a type annotation is found for which a `?` needs to be
104+
/// inserted.
105+
void addNullable(TypeAnnotation node);
106+
99107
/// Called whenever an expression is found for which a `!` needs to be
100108
/// inserted.
101109
void addNullCheck(Expression subexpression);
@@ -225,9 +233,9 @@ abstract class FixBuilder extends GeneralizingAstVisitor<DartType> {
225233
}
226234

227235
@override
228-
DartType visitExpression(Expression node) {
229-
// Every expression type needs its own visit method.
230-
throw UnimplementedError('No visit method for ${node.runtimeType}');
236+
DartType visitExpressionStatement(ExpressionStatement node) {
237+
visitSubexpression(node.expression, UnknownInferredType.instance);
238+
return null;
231239
}
232240

233241
@override
@@ -243,6 +251,12 @@ abstract class FixBuilder extends GeneralizingAstVisitor<DartType> {
243251
.withNullability(NullabilitySuffix.none);
244252
}
245253

254+
@override
255+
DartType visitNode(AstNode node) {
256+
// Every node type needs its own visit method.
257+
throw UnimplementedError('No visit method for ${node.runtimeType}');
258+
}
259+
246260
@override
247261
DartType visitNullLiteral(NullLiteral node) {
248262
_flowAnalysis.nullLiteral(node);
@@ -287,6 +301,57 @@ abstract class FixBuilder extends GeneralizingAstVisitor<DartType> {
287301
}
288302
}
289303

304+
@override
305+
DartType visitTypeName(TypeName node) {
306+
var decoratedType = _variables.decoratedTypeAnnotation(source, node);
307+
assert(decoratedType != null);
308+
List<DartType> arguments = [];
309+
if (node.typeArguments != null) {
310+
for (var argument in node.typeArguments.arguments) {
311+
arguments.add(argument.accept(this));
312+
}
313+
}
314+
if (decoratedType.type.isDynamic || decoratedType.type.isVoid) {
315+
// Already nullable. Nothing to do.
316+
return decoratedType.type;
317+
} else {
318+
var element = decoratedType.type.element as ClassElement;
319+
bool isNullable = decoratedType.node.isNullable;
320+
if (isNullable) {
321+
addNullable(node);
322+
}
323+
return InterfaceTypeImpl.explicit(element, arguments,
324+
nullabilitySuffix:
325+
isNullable ? NullabilitySuffix.question : NullabilitySuffix.none);
326+
}
327+
}
328+
329+
@override
330+
DartType visitVariableDeclarationList(VariableDeclarationList node) {
331+
node.metadata.accept(this);
332+
DartType contextType;
333+
var typeAnnotation = node.type;
334+
if (typeAnnotation != null) {
335+
contextType = typeAnnotation.accept(this);
336+
assert(contextType != null);
337+
} else {
338+
contextType = UnknownInferredType.instance;
339+
}
340+
for (var variable in node.variables) {
341+
if (variable.initializer != null) {
342+
visitSubexpression(variable.initializer, contextType);
343+
}
344+
}
345+
return null;
346+
}
347+
348+
@override
349+
DartType visitVariableDeclarationStatement(
350+
VariableDeclarationStatement node) {
351+
node.variables.accept(this);
352+
return null;
353+
}
354+
290355
/// Computes the type that [element] will have after migration.
291356
///
292357
/// If [targetType] is present, and [element] is a class member, it is the

pkg/nnbd_migration/test/fix_builder_test.dart

Lines changed: 175 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:analyzer/dart/element/type.dart';
88
import 'package:analyzer/src/dart/element/type.dart';
99
import 'package:analyzer/src/dart/element/type_provider.dart';
1010
import 'package:analyzer/src/generated/resolver.dart';
11+
import 'package:analyzer/src/generated/source.dart';
1112
import 'package:analyzer/src/generated/type_system.dart';
1213
import 'package:nnbd_migration/src/decorated_class_hierarchy.dart';
1314
import 'package:nnbd_migration/src/fix_builder.dart';
@@ -522,6 +523,16 @@ f() => 1.0;
522523
visitSubexpression(findNode.doubleLiteral('1.0'), 'double');
523524
}
524525

526+
test_expressionStatement() async {
527+
await analyze('''
528+
_f(int/*!*/ x, int/*?*/ y) {
529+
x = y;
530+
}
531+
''');
532+
visitStatement(findNode.statement('x = y'),
533+
nullChecked: {findNode.simple('y;')});
534+
}
535+
525536
test_integerLiteral() async {
526537
await analyze('''
527538
f() => 1;
@@ -649,6 +660,73 @@ f() => #foo;
649660
visitSubexpression(findNode.symbolLiteral('#foo'), 'Symbol');
650661
}
651662

663+
test_typeName_dynamic() async {
664+
await analyze('''
665+
void _f() {
666+
dynamic d = null;
667+
}
668+
''');
669+
visitTypeAnnotation(findNode.typeAnnotation('dynamic'), 'dynamic');
670+
}
671+
672+
test_typeName_generic_nonNullable() async {
673+
await analyze('''
674+
void _f() {
675+
List<int> i = [0];
676+
}
677+
''');
678+
visitTypeAnnotation(findNode.typeAnnotation('List<int>'), 'List<int>');
679+
}
680+
681+
test_typeName_generic_nullable() async {
682+
await analyze('''
683+
void _f() {
684+
List<int> i = null;
685+
}
686+
''');
687+
var listIntAnnotation = findNode.typeAnnotation('List<int>');
688+
visitTypeAnnotation(listIntAnnotation, 'List<int>?',
689+
nullable: {listIntAnnotation});
690+
}
691+
692+
test_typeName_generic_nullable_arg() async {
693+
await analyze('''
694+
void _f() {
695+
List<int> i = [null];
696+
}
697+
''');
698+
visitTypeAnnotation(findNode.typeAnnotation('List<int>'), 'List<int?>',
699+
nullable: {findNode.typeAnnotation('int')});
700+
}
701+
702+
test_typeName_simple_nonNullable() async {
703+
await analyze('''
704+
void _f() {
705+
int i = 0;
706+
}
707+
''');
708+
visitTypeAnnotation(findNode.typeAnnotation('int'), 'int');
709+
}
710+
711+
test_typeName_simple_nullable() async {
712+
await analyze('''
713+
void _f() {
714+
int i = null;
715+
}
716+
''');
717+
var intAnnotation = findNode.typeAnnotation('int');
718+
visitTypeAnnotation((intAnnotation), 'int?', nullable: {intAnnotation});
719+
}
720+
721+
test_typeName_void() async {
722+
await analyze('''
723+
void _f() {
724+
void v;
725+
}
726+
''');
727+
visitTypeAnnotation(findNode.typeAnnotation('void v'), 'void');
728+
}
729+
652730
test_use_of_dynamic() async {
653731
// Use of `dynamic` in a context requiring non-null is not explicitly null
654732
// checked.
@@ -658,6 +736,62 @@ bool _f(dynamic d, bool b) => d && b;
658736
visitSubexpression(findNode.binary('&&'), 'bool');
659737
}
660738

739+
test_variableDeclaration_typed_initialized_nonNullable() async {
740+
await analyze('''
741+
void _f() {
742+
int x = 0;
743+
}
744+
''');
745+
visitStatement(findNode.statement('int x'));
746+
}
747+
748+
test_variableDeclaration_typed_initialized_nullable() async {
749+
await analyze('''
750+
void _f() {
751+
int x = null;
752+
}
753+
''');
754+
visitStatement(findNode.statement('int x'),
755+
nullable: {findNode.typeAnnotation('int')});
756+
}
757+
758+
test_variableDeclaration_typed_uninitialized() async {
759+
await analyze('''
760+
void _f() {
761+
int x;
762+
}
763+
''');
764+
visitStatement(findNode.statement('int x'));
765+
}
766+
767+
test_variableDeclaration_untyped_initialized() async {
768+
await analyze('''
769+
void _f() {
770+
var x = 0;
771+
}
772+
''');
773+
visitStatement(findNode.statement('var x'));
774+
}
775+
776+
test_variableDeclaration_untyped_uninitialized() async {
777+
await analyze('''
778+
void _f() {
779+
var x;
780+
}
781+
''');
782+
visitStatement(findNode.statement('var x'));
783+
}
784+
785+
test_variableDeclaration_visit_initializer() async {
786+
await analyze('''
787+
void _f(bool/*?*/ x, bool/*?*/ y) {
788+
bool z = x && y;
789+
}
790+
''');
791+
visitStatement(findNode.statement('bool z'),
792+
nullChecked: {findNode.simple('x &&'), findNode.simple('y;')});
793+
}
794+
661795
void visitAssignmentTarget(
662796
Expression node, String expectedReadType, String expectedWriteType,
663797
{Set<Expression> nullChecked = const <Expression>{},
@@ -672,21 +806,47 @@ bool _f(dynamic d, bool b) => d && b;
672806
expect(fixBuilder.problems, problems);
673807
}
674808

809+
void visitStatement(Statement node,
810+
{Set<Expression> nullChecked = const <Expression>{},
811+
Map<AstNode, Set<Problem>> problems = const <AstNode, Set<Problem>>{},
812+
Set<TypeAnnotation> nullable = const <TypeAnnotation>{}}) {
813+
_FixBuilder fixBuilder = _createFixBuilder(node);
814+
var type = node.accept(fixBuilder);
815+
expect(type, null);
816+
expect(fixBuilder.nullCheckedExpressions, nullChecked);
817+
expect(fixBuilder.problems, problems);
818+
expect(fixBuilder.nullable, nullable);
819+
}
820+
675821
void visitSubexpression(Expression node, String expectedType,
676822
{DartType contextType,
677823
Set<Expression> nullChecked = const <Expression>{},
678-
Map<AstNode, Set<Problem>> problems = const <AstNode, Set<Problem>>{}}) {
824+
Map<AstNode, Set<Problem>> problems = const <AstNode, Set<Problem>>{},
825+
Set<TypeAnnotation> nullable = const <TypeAnnotation>{}}) {
679826
contextType ??= dynamicType;
680827
_FixBuilder fixBuilder = _createFixBuilder(node);
681828
var type = fixBuilder.visitSubexpression(node, contextType);
682829
expect((type as TypeImpl).toString(withNullability: true), expectedType);
683830
expect(fixBuilder.nullCheckedExpressions, nullChecked);
684831
expect(fixBuilder.problems, problems);
832+
expect(fixBuilder.nullable, nullable);
833+
}
834+
835+
void visitTypeAnnotation(TypeAnnotation node, String expectedType,
836+
{Set<Expression> nullChecked = const <Expression>{},
837+
Map<AstNode, Set<Problem>> problems = const <AstNode, Set<Problem>>{},
838+
Set<TypeAnnotation> nullable = const <TypeAnnotation>{}}) {
839+
_FixBuilder fixBuilder = _createFixBuilder(node);
840+
var type = node.accept(fixBuilder);
841+
expect((type as TypeImpl).toString(withNullability: true), expectedType);
842+
expect(fixBuilder.nullCheckedExpressions, nullChecked);
843+
expect(fixBuilder.problems, problems);
844+
expect(fixBuilder.nullable, nullable);
685845
}
686846

687-
_FixBuilder _createFixBuilder(Expression node) {
688-
var fixBuilder = _FixBuilder(
689-
decoratedClassHierarchy, typeProvider, typeSystem, variables);
847+
_FixBuilder _createFixBuilder(AstNode node) {
848+
var fixBuilder = _FixBuilder(testSource, decoratedClassHierarchy,
849+
typeProvider, typeSystem, variables);
690850
var body = node.thisOrAncestorOfType<FunctionBody>();
691851
var declaration = body.thisOrAncestorOfType<Declaration>();
692852
fixBuilder.createFlowAnalysis(declaration, null);
@@ -697,11 +857,20 @@ bool _f(dynamic d, bool b) => d && b;
697857
class _FixBuilder extends FixBuilder {
698858
final Set<Expression> nullCheckedExpressions = {};
699859

860+
final Set<TypeAnnotation> nullable = {};
861+
700862
final Map<AstNode, Set<Problem>> problems = {};
701863

702-
_FixBuilder(DecoratedClassHierarchy decoratedClassHierarchy,
864+
_FixBuilder(Source source, DecoratedClassHierarchy decoratedClassHierarchy,
703865
TypeProvider typeProvider, TypeSystem typeSystem, Variables variables)
704-
: super(decoratedClassHierarchy, typeProvider, typeSystem, variables);
866+
: super(source, decoratedClassHierarchy, typeProvider, typeSystem,
867+
variables);
868+
869+
@override
870+
void addNullable(TypeAnnotation node) {
871+
var newlyAdded = nullable.add(node);
872+
expect(newlyAdded, true);
873+
}
705874

706875
@override
707876
void addNullCheck(Expression subexpression) {

0 commit comments

Comments
 (0)