Migration: Split up migration_visitor_test.dart.
This will make it easier to re-use the test infrastructure for some
other tests I plan to introduce in a follow-up CL.
Change-Id: I0a243f00f342674fe38274eb46e7c1e943817785
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106442
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/nnbd_migration/test/migration_visitor_test.dart b/pkg/nnbd_migration/test/graph_builder_test.dart
similarity index 68%
rename from pkg/nnbd_migration/test/migration_visitor_test.dart
rename to pkg/nnbd_migration/test/graph_builder_test.dart
index cb2e1e0..e5305ea 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test.dart
+++ b/pkg/nnbd_migration/test/graph_builder_test.dart
@@ -3,25 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/source.dart';
-import 'package:meta/meta.dart';
-import 'package:nnbd_migration/src/conditional_discard.dart';
import 'package:nnbd_migration/src/decorated_type.dart';
import 'package:nnbd_migration/src/expression_checks.dart';
import 'package:nnbd_migration/src/graph_builder.dart';
-import 'package:nnbd_migration/src/node_builder.dart';
import 'package:nnbd_migration/src/nullability_node.dart';
-import 'package:nnbd_migration/src/variables.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import 'abstract_single_unit.dart';
+import 'migration_visitor_test_base.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(GraphBuilderTest);
- defineReflectiveTests(NodeBuilderTest);
});
}
@@ -32,8 +25,7 @@
@override
Future<CompilationUnit> analyze(String code) async {
var unit = await super.analyze(code);
- unit.accept(
- GraphBuilder(typeProvider, _variables, graph, testSource, null));
+ unit.accept(GraphBuilder(typeProvider, variables, graph, testSource, null));
return unit;
}
@@ -69,14 +61,14 @@
/// representation is [text], or `null` if the expression has no
/// [ExpressionChecks] associated with it.
ExpressionChecks checkExpression(String text) {
- return _variables.checkExpression(findNode.expression(text));
+ return variables.checkExpression(findNode.expression(text));
}
/// Gets the [DecoratedType] associated with the expression whose text
/// representation is [text], or `null` if the expression has no
/// [DecoratedType] associated with it.
DecoratedType decoratedExpressionType(String text) {
- return _variables.decoratedExpressionType(findNode.expression(text));
+ return variables.decoratedExpressionType(findNode.expression(text));
}
test_assert_demonstrates_non_null_intent() async {
@@ -1552,492 +1544,3 @@
hard: true);
}
}
-
-class MigrationVisitorTestBase extends AbstractSingleUnitTest {
- final _Variables _variables;
-
- final NullabilityGraphForTesting graph;
-
- MigrationVisitorTestBase() : this._(NullabilityGraphForTesting());
-
- MigrationVisitorTestBase._(this.graph) : _variables = _Variables(graph);
-
- NullabilityNode get always => graph.always;
-
- NullabilityNode get never => graph.never;
-
- TypeProvider get typeProvider => testAnalysisResult.typeProvider;
-
- Future<CompilationUnit> analyze(String code) async {
- await resolveTestUnit(code);
- testUnit
- .accept(NodeBuilder(_variables, testSource, null, graph, typeProvider));
- return testUnit;
- }
-
- NullabilityEdge assertEdge(
- NullabilityNode source, NullabilityNode destination,
- {@required bool hard, List<NullabilityNode> guards = const []}) {
- var edges = getEdges(source, destination);
- if (edges.length == 0) {
- fail('Expected edge $source -> $destination, found none');
- } else if (edges.length != 1) {
- fail('Found multiple edges $source -> $destination');
- } else {
- var edge = edges[0];
- expect(edge.hard, hard);
- expect(edge.guards, unorderedEquals(guards));
- return edge;
- }
- }
-
- void assertNoEdge(NullabilityNode source, NullabilityNode destination) {
- var edges = getEdges(source, destination);
- if (edges.isNotEmpty) {
- fail('Expected no edge $source -> $destination, found ${edges.length}');
- }
- }
-
- void assertUnion(NullabilityNode x, NullabilityNode y) {
- var edges = getEdges(x, y);
- for (var edge in edges) {
- if (edge.isUnion) {
- expect(edge.sources, hasLength(1));
- return;
- }
- }
- fail('Expected union between $x and $y, not found');
- }
-
- /// Gets the [DecoratedType] associated with the generic function type
- /// annotation whose text is [text].
- DecoratedType decoratedGenericFunctionTypeAnnotation(String text) {
- return _variables.decoratedTypeAnnotation(
- testSource, findNode.genericFunctionType(text));
- }
-
- /// Gets the [DecoratedType] associated with the type annotation whose text
- /// is [text].
- DecoratedType decoratedTypeAnnotation(String text) {
- return _variables.decoratedTypeAnnotation(
- testSource, findNode.typeAnnotation(text));
- }
-
- List<NullabilityEdge> getEdges(
- NullabilityNode source, NullabilityNode destination) =>
- graph
- .getUpstreamEdges(destination)
- .where((e) => e.primarySource == source)
- .toList();
-
- NullabilityNode possiblyOptionalParameter(String text) {
- return _variables
- .possiblyOptionalParameter(findNode.defaultParameter(text));
- }
-
- /// Gets the [ConditionalDiscard] information associated with the statement
- /// whose text is [text].
- ConditionalDiscard statementDiscard(String text) {
- return _variables.conditionalDiscard(findNode.statement(text));
- }
-}
-
-@reflectiveTest
-class NodeBuilderTest extends MigrationVisitorTestBase {
- /// Gets the [DecoratedType] associated with the constructor declaration whose
- /// name matches [search].
- DecoratedType decoratedConstructorDeclaration(String search) => _variables
- .decoratedElementType(findNode.constructor(search).declaredElement);
-
- /// Gets the [DecoratedType] associated with the function declaration whose
- /// name matches [search].
- DecoratedType decoratedFunctionType(String search) =>
- _variables.decoratedElementType(
- findNode.functionDeclaration(search).declaredElement);
-
- DecoratedType decoratedTypeParameterBound(String search) => _variables
- .decoratedElementType(findNode.typeParameter(search).declaredElement);
-
- test_constructor_returnType_implicit_dynamic() async {
- await analyze('''
-class C {
- C();
-}
-''');
- var decoratedType = decoratedConstructorDeclaration('C(').returnType;
- expect(decoratedType.node, same(never));
- }
-
- test_dynamic_type() async {
- await analyze('''
-dynamic f() {}
-''');
- var decoratedType = decoratedTypeAnnotation('dynamic');
- expect(decoratedFunctionType('f').returnType, same(decoratedType));
- assertEdge(always, decoratedType.node, hard: false);
- }
-
- test_field_type_simple() async {
- await analyze('''
-class C {
- int f = 0;
-}
-''');
- var decoratedType = decoratedTypeAnnotation('int');
- expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(
- _variables.decoratedElementType(
- findNode.fieldDeclaration('f').fields.variables[0].declaredElement),
- same(decoratedType));
- }
-
- test_genericFunctionType_namedParameterType() async {
- await analyze('''
-void f(void Function({int y}) x) {}
-''');
- var decoratedType =
- decoratedGenericFunctionTypeAnnotation('void Function({int y})');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedType));
- expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
- var decoratedIntType = decoratedTypeAnnotation('int');
- expect(decoratedType.namedParameters['y'], same(decoratedIntType));
- expect(decoratedIntType.node, isNotNull);
- expect(decoratedIntType.node, isNot(never));
- }
-
- test_genericFunctionType_returnType() async {
- await analyze('''
-void f(int Function() x) {}
-''');
- var decoratedType =
- decoratedGenericFunctionTypeAnnotation('int Function()');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedType));
- expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
- var decoratedIntType = decoratedTypeAnnotation('int');
- expect(decoratedType.returnType, same(decoratedIntType));
- expect(decoratedIntType.node, isNotNull);
- expect(decoratedIntType.node, isNot(never));
- }
-
- test_genericFunctionType_unnamedParameterType() async {
- await analyze('''
-void f(void Function(int) x) {}
-''');
- var decoratedType =
- decoratedGenericFunctionTypeAnnotation('void Function(int)');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedType));
- expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
- var decoratedIntType = decoratedTypeAnnotation('int');
- expect(decoratedType.positionalParameters[0], same(decoratedIntType));
- expect(decoratedIntType.node, isNotNull);
- expect(decoratedIntType.node, isNot(never));
- }
-
- test_interfaceType_generic_instantiate_to_dynamic() async {
- await analyze('''
-void f(List x) {}
-''');
- var decoratedListType = decoratedTypeAnnotation('List');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedListType));
- expect(decoratedListType.node, isNotNull);
- expect(decoratedListType.node, isNot(never));
- var decoratedArgType = decoratedListType.typeArguments[0];
- expect(decoratedArgType.node, same(always));
- }
-
- test_interfaceType_generic_instantiate_to_generic_type() async {
- await analyze('''
-class C<T> {}
-class D<T extends C<int>> {}
-void f(D x) {}
-''');
- var decoratedListType = decoratedTypeAnnotation('D x');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedListType));
- expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedListType.typeArguments, hasLength(1));
- var decoratedArgType = decoratedListType.typeArguments[0];
- expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArgType.typeArguments, hasLength(1));
- var decoratedArgArgType = decoratedArgType.typeArguments[0];
- expect(decoratedArgArgType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArgArgType.typeArguments, isEmpty);
- }
-
- test_interfaceType_generic_instantiate_to_generic_type_2() async {
- await analyze('''
-class C<T, U> {}
-class D<T extends C<int, String>, U extends C<num, double>> {}
-void f(D x) {}
-''');
- var decoratedDType = decoratedTypeAnnotation('D x');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedDType));
- expect(decoratedDType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedDType.typeArguments, hasLength(2));
- var decoratedArg0Type = decoratedDType.typeArguments[0];
- expect(decoratedArg0Type.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArg0Type.typeArguments, hasLength(2));
- var decoratedArg0Arg0Type = decoratedArg0Type.typeArguments[0];
- expect(decoratedArg0Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArg0Arg0Type.typeArguments, isEmpty);
- var decoratedArg0Arg1Type = decoratedArg0Type.typeArguments[1];
- expect(decoratedArg0Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArg0Arg1Type.typeArguments, isEmpty);
- var decoratedArg1Type = decoratedDType.typeArguments[1];
- expect(decoratedArg1Type.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArg1Type.typeArguments, hasLength(2));
- var decoratedArg1Arg0Type = decoratedArg1Type.typeArguments[0];
- expect(decoratedArg1Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArg1Arg0Type.typeArguments, isEmpty);
- var decoratedArg1Arg1Type = decoratedArg1Type.typeArguments[1];
- expect(decoratedArg1Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArg1Arg1Type.typeArguments, isEmpty);
- }
-
- test_interfaceType_generic_instantiate_to_object() async {
- await analyze('''
-class C<T extends Object> {}
-void f(C x) {}
-''');
- var decoratedListType = decoratedTypeAnnotation('C x');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedListType));
- expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedListType.typeArguments, hasLength(1));
- var decoratedArgType = decoratedListType.typeArguments[0];
- expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedArgType.typeArguments, isEmpty);
- }
-
- test_interfaceType_typeParameter() async {
- await analyze('''
-void f(List<int> x) {}
-''');
- var decoratedListType = decoratedTypeAnnotation('List<int>');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedListType));
- expect(decoratedListType.node, isNotNull);
- expect(decoratedListType.node, isNot(never));
- var decoratedIntType = decoratedTypeAnnotation('int');
- expect(decoratedListType.typeArguments[0], same(decoratedIntType));
- expect(decoratedIntType.node, isNotNull);
- expect(decoratedIntType.node, isNot(never));
- }
-
- test_topLevelFunction_parameterType_implicit_dynamic() async {
- await analyze('''
-void f(x) {}
-''');
- var decoratedType =
- _variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedType));
- expect(decoratedType.type.isDynamic, isTrue);
- assertUnion(always, decoratedType.node);
- }
-
- test_topLevelFunction_parameterType_named_no_default() async {
- await analyze('''
-void f({String s}) {}
-''');
- var decoratedType = decoratedTypeAnnotation('String');
- var functionType = decoratedFunctionType('f');
- expect(functionType.namedParameters['s'], same(decoratedType));
- expect(decoratedType.node, isNotNull);
- expect(decoratedType.node, isNot(never));
- expect(decoratedType.node, isNot(always));
- expect(functionType.namedParameters['s'].node.isPossiblyOptional, true);
- }
-
- test_topLevelFunction_parameterType_named_no_default_required() async {
- addMetaPackage();
- await analyze('''
-import 'package:meta/meta.dart';
-void f({@required String s}) {}
-''');
- var decoratedType = decoratedTypeAnnotation('String');
- var functionType = decoratedFunctionType('f');
- expect(functionType.namedParameters['s'], same(decoratedType));
- expect(decoratedType.node, isNotNull);
- expect(decoratedType.node, isNot(never));
- expect(decoratedType.node, isNot(always));
- expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
- }
-
- test_topLevelFunction_parameterType_named_with_default() async {
- await analyze('''
-void f({String s: 'x'}) {}
-''');
- var decoratedType = decoratedTypeAnnotation('String');
- var functionType = decoratedFunctionType('f');
- expect(functionType.namedParameters['s'], same(decoratedType));
- expect(decoratedType.node, isNotNull);
- expect(decoratedType.node, isNot(never));
- expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
- }
-
- test_topLevelFunction_parameterType_positionalOptional() async {
- await analyze('''
-void f([int i]) {}
-''');
- var decoratedType = decoratedTypeAnnotation('int');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedType));
- expect(decoratedType.node, isNotNull);
- expect(decoratedType.node, isNot(never));
- }
-
- test_topLevelFunction_parameterType_simple() async {
- await analyze('''
-void f(int i) {}
-''');
- var decoratedType = decoratedTypeAnnotation('int');
- expect(decoratedFunctionType('f').positionalParameters[0],
- same(decoratedType));
- expect(decoratedType.node, isNotNull);
- expect(decoratedType.node, isNot(never));
- }
-
- test_topLevelFunction_returnType_implicit_dynamic() async {
- await analyze('''
-f() {}
-''');
- var decoratedType = decoratedFunctionType('f').returnType;
- expect(decoratedType.type.isDynamic, isTrue);
- assertUnion(always, decoratedType.node);
- }
-
- test_topLevelFunction_returnType_simple() async {
- await analyze('''
-int f() => 0;
-''');
- var decoratedType = decoratedTypeAnnotation('int');
- expect(decoratedFunctionType('f').returnType, same(decoratedType));
- expect(decoratedType.node, isNotNull);
- expect(decoratedType.node, isNot(never));
- }
-
- test_type_comment_bang() async {
- await analyze('''
-void f(int/*!*/ i) {}
-''');
- assertEdge(decoratedTypeAnnotation('int').node, never, hard: true);
- }
-
- test_type_comment_question() async {
- await analyze('''
-void f(int/*?*/ i) {}
-''');
- assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
- }
-
- test_type_parameter_explicit_bound() async {
- await analyze('''
-class C<T extends Object> {}
-''');
- var bound = decoratedTypeParameterBound('T');
- expect(decoratedTypeAnnotation('Object'), same(bound));
- expect(bound.node, isNot(always));
- expect(bound.type, typeProvider.objectType);
- }
-
- test_type_parameter_implicit_bound() async {
- // The implicit bound of `T` is automatically `Object?`. TODO(paulberry):
- // consider making it possible for type inference to infer an explicit bound
- // of `Object`.
- await analyze('''
-class C<T> {}
-''');
- var bound = decoratedTypeParameterBound('T');
- assertUnion(always, bound.node);
- expect(bound.type, same(typeProvider.objectType));
- }
-
- test_variableDeclaration_type_simple() async {
- await analyze('''
-main() {
- int i;
-}
-''');
- var decoratedType = decoratedTypeAnnotation('int');
- expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
- }
-
- test_void_type() async {
- await analyze('''
-void f() {}
-''');
- var decoratedType = decoratedTypeAnnotation('void');
- expect(decoratedFunctionType('f').returnType, same(decoratedType));
- assertEdge(always, decoratedType.node, hard: false);
- }
-}
-
-/// Mock representation of constraint variables.
-class _Variables extends Variables {
- final _conditionalDiscard = <AstNode, ConditionalDiscard>{};
-
- final _decoratedExpressionTypes = <Expression, DecoratedType>{};
-
- final _expressionChecks = <Expression, ExpressionChecks>{};
-
- final _possiblyOptional = <DefaultFormalParameter, NullabilityNode>{};
-
- _Variables(NullabilityGraph graph) : super(graph);
-
- /// Gets the [ExpressionChecks] associated with the given [expression].
- ExpressionChecks checkExpression(Expression expression) =>
- _expressionChecks[_normalizeExpression(expression)];
-
- /// Gets the [conditionalDiscard] associated with the given [expression].
- ConditionalDiscard conditionalDiscard(AstNode node) =>
- _conditionalDiscard[node];
-
- /// Gets the [DecoratedType] associated with the given [expression].
- DecoratedType decoratedExpressionType(Expression expression) =>
- _decoratedExpressionTypes[_normalizeExpression(expression)];
-
- /// Gets the [NullabilityNode] associated with the possibility that
- /// [parameter] may be optional.
- NullabilityNode possiblyOptionalParameter(DefaultFormalParameter parameter) =>
- _possiblyOptional[parameter];
-
- @override
- void recordConditionalDiscard(
- Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
- _conditionalDiscard[node] = conditionalDiscard;
- super.recordConditionalDiscard(source, node, conditionalDiscard);
- }
-
- void recordDecoratedExpressionType(Expression node, DecoratedType type) {
- super.recordDecoratedExpressionType(node, type);
- _decoratedExpressionTypes[_normalizeExpression(node)] = type;
- }
-
- @override
- void recordExpressionChecks(
- Source source, Expression expression, ExpressionChecks checks) {
- super.recordExpressionChecks(source, expression, checks);
- _expressionChecks[_normalizeExpression(expression)] = checks;
- }
-
- @override
- void recordPossiblyOptional(
- Source source, DefaultFormalParameter parameter, NullabilityNode node) {
- _possiblyOptional[parameter] = node;
- super.recordPossiblyOptional(source, parameter, node);
- }
-
- /// Unwraps any parentheses surrounding [expression].
- Expression _normalizeExpression(Expression expression) {
- while (expression is ParenthesizedExpression) {
- expression = (expression as ParenthesizedExpression).expression;
- }
- return expression;
- }
-}
diff --git a/pkg/nnbd_migration/test/migration_visitor_test_base.dart b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
new file mode 100644
index 0000000..2e2e9d1
--- /dev/null
+++ b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
@@ -0,0 +1,170 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:meta/meta.dart';
+import 'package:nnbd_migration/src/conditional_discard.dart';
+import 'package:nnbd_migration/src/decorated_type.dart';
+import 'package:nnbd_migration/src/expression_checks.dart';
+import 'package:nnbd_migration/src/node_builder.dart';
+import 'package:nnbd_migration/src/nullability_node.dart';
+import 'package:nnbd_migration/src/variables.dart';
+import 'package:test/test.dart';
+
+import 'abstract_single_unit.dart';
+
+/// Mock representation of constraint variables.
+class InstrumentedVariables extends Variables {
+ final _conditionalDiscard = <AstNode, ConditionalDiscard>{};
+
+ final _decoratedExpressionTypes = <Expression, DecoratedType>{};
+
+ final _expressionChecks = <Expression, ExpressionChecks>{};
+
+ final _possiblyOptional = <DefaultFormalParameter, NullabilityNode>{};
+
+ InstrumentedVariables(NullabilityGraph graph) : super(graph);
+
+ /// Gets the [ExpressionChecks] associated with the given [expression].
+ ExpressionChecks checkExpression(Expression expression) =>
+ _expressionChecks[_normalizeExpression(expression)];
+
+ /// Gets the [conditionalDiscard] associated with the given [expression].
+ ConditionalDiscard conditionalDiscard(AstNode node) =>
+ _conditionalDiscard[node];
+
+ /// Gets the [DecoratedType] associated with the given [expression].
+ DecoratedType decoratedExpressionType(Expression expression) =>
+ _decoratedExpressionTypes[_normalizeExpression(expression)];
+
+ /// Gets the [NullabilityNode] associated with the possibility that
+ /// [parameter] may be optional.
+ NullabilityNode possiblyOptionalParameter(DefaultFormalParameter parameter) =>
+ _possiblyOptional[parameter];
+
+ @override
+ void recordConditionalDiscard(
+ Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
+ _conditionalDiscard[node] = conditionalDiscard;
+ super.recordConditionalDiscard(source, node, conditionalDiscard);
+ }
+
+ void recordDecoratedExpressionType(Expression node, DecoratedType type) {
+ super.recordDecoratedExpressionType(node, type);
+ _decoratedExpressionTypes[_normalizeExpression(node)] = type;
+ }
+
+ @override
+ void recordExpressionChecks(
+ Source source, Expression expression, ExpressionChecks checks) {
+ super.recordExpressionChecks(source, expression, checks);
+ _expressionChecks[_normalizeExpression(expression)] = checks;
+ }
+
+ @override
+ void recordPossiblyOptional(
+ Source source, DefaultFormalParameter parameter, NullabilityNode node) {
+ _possiblyOptional[parameter] = node;
+ super.recordPossiblyOptional(source, parameter, node);
+ }
+
+ /// Unwraps any parentheses surrounding [expression].
+ Expression _normalizeExpression(Expression expression) {
+ while (expression is ParenthesizedExpression) {
+ expression = (expression as ParenthesizedExpression).expression;
+ }
+ return expression;
+ }
+}
+
+class MigrationVisitorTestBase extends AbstractSingleUnitTest {
+ final InstrumentedVariables variables;
+
+ final NullabilityGraphForTesting graph;
+
+ MigrationVisitorTestBase() : this._(NullabilityGraphForTesting());
+
+ MigrationVisitorTestBase._(this.graph)
+ : variables = InstrumentedVariables(graph);
+
+ NullabilityNode get always => graph.always;
+
+ NullabilityNode get never => graph.never;
+
+ TypeProvider get typeProvider => testAnalysisResult.typeProvider;
+
+ Future<CompilationUnit> analyze(String code) async {
+ await resolveTestUnit(code);
+ testUnit
+ .accept(NodeBuilder(variables, testSource, null, graph, typeProvider));
+ return testUnit;
+ }
+
+ NullabilityEdge assertEdge(
+ NullabilityNode source, NullabilityNode destination,
+ {@required bool hard, List<NullabilityNode> guards = const []}) {
+ var edges = getEdges(source, destination);
+ if (edges.length == 0) {
+ fail('Expected edge $source -> $destination, found none');
+ } else if (edges.length != 1) {
+ fail('Found multiple edges $source -> $destination');
+ } else {
+ var edge = edges[0];
+ expect(edge.hard, hard);
+ expect(edge.guards, unorderedEquals(guards));
+ return edge;
+ }
+ }
+
+ void assertNoEdge(NullabilityNode source, NullabilityNode destination) {
+ var edges = getEdges(source, destination);
+ if (edges.isNotEmpty) {
+ fail('Expected no edge $source -> $destination, found ${edges.length}');
+ }
+ }
+
+ void assertUnion(NullabilityNode x, NullabilityNode y) {
+ var edges = getEdges(x, y);
+ for (var edge in edges) {
+ if (edge.isUnion) {
+ expect(edge.sources, hasLength(1));
+ return;
+ }
+ }
+ fail('Expected union between $x and $y, not found');
+ }
+
+ /// Gets the [DecoratedType] associated with the generic function type
+ /// annotation whose text is [text].
+ DecoratedType decoratedGenericFunctionTypeAnnotation(String text) {
+ return variables.decoratedTypeAnnotation(
+ testSource, findNode.genericFunctionType(text));
+ }
+
+ /// Gets the [DecoratedType] associated with the type annotation whose text
+ /// is [text].
+ DecoratedType decoratedTypeAnnotation(String text) {
+ return variables.decoratedTypeAnnotation(
+ testSource, findNode.typeAnnotation(text));
+ }
+
+ List<NullabilityEdge> getEdges(
+ NullabilityNode source, NullabilityNode destination) =>
+ graph
+ .getUpstreamEdges(destination)
+ .where((e) => e.primarySource == source)
+ .toList();
+
+ NullabilityNode possiblyOptionalParameter(String text) {
+ return variables.possiblyOptionalParameter(findNode.defaultParameter(text));
+ }
+
+ /// Gets the [ConditionalDiscard] information associated with the statement
+ /// whose text is [text].
+ ConditionalDiscard statementDiscard(String text) {
+ return variables.conditionalDiscard(findNode.statement(text));
+ }
+}
diff --git a/pkg/nnbd_migration/test/node_builder_test.dart b/pkg/nnbd_migration/test/node_builder_test.dart
new file mode 100644
index 0000000..ef4476f
--- /dev/null
+++ b/pkg/nnbd_migration/test/node_builder_test.dart
@@ -0,0 +1,352 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:nnbd_migration/src/decorated_type.dart';
+import 'package:nnbd_migration/src/nullability_node.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'migration_visitor_test_base.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(NodeBuilderTest);
+ });
+}
+
+@reflectiveTest
+class NodeBuilderTest extends MigrationVisitorTestBase {
+ /// Gets the [DecoratedType] associated with the constructor declaration whose
+ /// name matches [search].
+ DecoratedType decoratedConstructorDeclaration(String search) => variables
+ .decoratedElementType(findNode.constructor(search).declaredElement);
+
+ /// Gets the [DecoratedType] associated with the function declaration whose
+ /// name matches [search].
+ DecoratedType decoratedFunctionType(String search) =>
+ variables.decoratedElementType(
+ findNode.functionDeclaration(search).declaredElement);
+
+ DecoratedType decoratedTypeParameterBound(String search) => variables
+ .decoratedElementType(findNode.typeParameter(search).declaredElement);
+
+ test_constructor_returnType_implicit_dynamic() async {
+ await analyze('''
+class C {
+ C();
+}
+''');
+ var decoratedType = decoratedConstructorDeclaration('C(').returnType;
+ expect(decoratedType.node, same(never));
+ }
+
+ test_dynamic_type() async {
+ await analyze('''
+dynamic f() {}
+''');
+ var decoratedType = decoratedTypeAnnotation('dynamic');
+ expect(decoratedFunctionType('f').returnType, same(decoratedType));
+ assertEdge(always, decoratedType.node, hard: false);
+ }
+
+ test_field_type_simple() async {
+ await analyze('''
+class C {
+ int f = 0;
+}
+''');
+ var decoratedType = decoratedTypeAnnotation('int');
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(
+ variables.decoratedElementType(
+ findNode.fieldDeclaration('f').fields.variables[0].declaredElement),
+ same(decoratedType));
+ }
+
+ test_genericFunctionType_namedParameterType() async {
+ await analyze('''
+void f(void Function({int y}) x) {}
+''');
+ var decoratedType =
+ decoratedGenericFunctionTypeAnnotation('void Function({int y})');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ var decoratedIntType = decoratedTypeAnnotation('int');
+ expect(decoratedType.namedParameters['y'], same(decoratedIntType));
+ expect(decoratedIntType.node, isNotNull);
+ expect(decoratedIntType.node, isNot(never));
+ }
+
+ test_genericFunctionType_returnType() async {
+ await analyze('''
+void f(int Function() x) {}
+''');
+ var decoratedType =
+ decoratedGenericFunctionTypeAnnotation('int Function()');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ var decoratedIntType = decoratedTypeAnnotation('int');
+ expect(decoratedType.returnType, same(decoratedIntType));
+ expect(decoratedIntType.node, isNotNull);
+ expect(decoratedIntType.node, isNot(never));
+ }
+
+ test_genericFunctionType_unnamedParameterType() async {
+ await analyze('''
+void f(void Function(int) x) {}
+''');
+ var decoratedType =
+ decoratedGenericFunctionTypeAnnotation('void Function(int)');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ var decoratedIntType = decoratedTypeAnnotation('int');
+ expect(decoratedType.positionalParameters[0], same(decoratedIntType));
+ expect(decoratedIntType.node, isNotNull);
+ expect(decoratedIntType.node, isNot(never));
+ }
+
+ test_interfaceType_generic_instantiate_to_dynamic() async {
+ await analyze('''
+void f(List x) {}
+''');
+ var decoratedListType = decoratedTypeAnnotation('List');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedListType));
+ expect(decoratedListType.node, isNotNull);
+ expect(decoratedListType.node, isNot(never));
+ var decoratedArgType = decoratedListType.typeArguments[0];
+ expect(decoratedArgType.node, same(always));
+ }
+
+ test_interfaceType_generic_instantiate_to_generic_type() async {
+ await analyze('''
+class C<T> {}
+class D<T extends C<int>> {}
+void f(D x) {}
+''');
+ var decoratedListType = decoratedTypeAnnotation('D x');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedListType));
+ expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedListType.typeArguments, hasLength(1));
+ var decoratedArgType = decoratedListType.typeArguments[0];
+ expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArgType.typeArguments, hasLength(1));
+ var decoratedArgArgType = decoratedArgType.typeArguments[0];
+ expect(decoratedArgArgType.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArgArgType.typeArguments, isEmpty);
+ }
+
+ test_interfaceType_generic_instantiate_to_generic_type_2() async {
+ await analyze('''
+class C<T, U> {}
+class D<T extends C<int, String>, U extends C<num, double>> {}
+void f(D x) {}
+''');
+ var decoratedDType = decoratedTypeAnnotation('D x');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedDType));
+ expect(decoratedDType.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedDType.typeArguments, hasLength(2));
+ var decoratedArg0Type = decoratedDType.typeArguments[0];
+ expect(decoratedArg0Type.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArg0Type.typeArguments, hasLength(2));
+ var decoratedArg0Arg0Type = decoratedArg0Type.typeArguments[0];
+ expect(decoratedArg0Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArg0Arg0Type.typeArguments, isEmpty);
+ var decoratedArg0Arg1Type = decoratedArg0Type.typeArguments[1];
+ expect(decoratedArg0Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArg0Arg1Type.typeArguments, isEmpty);
+ var decoratedArg1Type = decoratedDType.typeArguments[1];
+ expect(decoratedArg1Type.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArg1Type.typeArguments, hasLength(2));
+ var decoratedArg1Arg0Type = decoratedArg1Type.typeArguments[0];
+ expect(decoratedArg1Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArg1Arg0Type.typeArguments, isEmpty);
+ var decoratedArg1Arg1Type = decoratedArg1Type.typeArguments[1];
+ expect(decoratedArg1Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArg1Arg1Type.typeArguments, isEmpty);
+ }
+
+ test_interfaceType_generic_instantiate_to_object() async {
+ await analyze('''
+class C<T extends Object> {}
+void f(C x) {}
+''');
+ var decoratedListType = decoratedTypeAnnotation('C x');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedListType));
+ expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedListType.typeArguments, hasLength(1));
+ var decoratedArgType = decoratedListType.typeArguments[0];
+ expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedArgType.typeArguments, isEmpty);
+ }
+
+ test_interfaceType_typeParameter() async {
+ await analyze('''
+void f(List<int> x) {}
+''');
+ var decoratedListType = decoratedTypeAnnotation('List<int>');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedListType));
+ expect(decoratedListType.node, isNotNull);
+ expect(decoratedListType.node, isNot(never));
+ var decoratedIntType = decoratedTypeAnnotation('int');
+ expect(decoratedListType.typeArguments[0], same(decoratedIntType));
+ expect(decoratedIntType.node, isNotNull);
+ expect(decoratedIntType.node, isNot(never));
+ }
+
+ test_topLevelFunction_parameterType_implicit_dynamic() async {
+ await analyze('''
+void f(x) {}
+''');
+ var decoratedType =
+ variables.decoratedElementType(findNode.simple('x').staticElement);
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.type.isDynamic, isTrue);
+ assertUnion(always, decoratedType.node);
+ }
+
+ test_topLevelFunction_parameterType_named_no_default() async {
+ await analyze('''
+void f({String s}) {}
+''');
+ var decoratedType = decoratedTypeAnnotation('String');
+ var functionType = decoratedFunctionType('f');
+ expect(functionType.namedParameters['s'], same(decoratedType));
+ expect(decoratedType.node, isNotNull);
+ expect(decoratedType.node, isNot(never));
+ expect(decoratedType.node, isNot(always));
+ expect(functionType.namedParameters['s'].node.isPossiblyOptional, true);
+ }
+
+ test_topLevelFunction_parameterType_named_no_default_required() async {
+ addMetaPackage();
+ await analyze('''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+''');
+ var decoratedType = decoratedTypeAnnotation('String');
+ var functionType = decoratedFunctionType('f');
+ expect(functionType.namedParameters['s'], same(decoratedType));
+ expect(decoratedType.node, isNotNull);
+ expect(decoratedType.node, isNot(never));
+ expect(decoratedType.node, isNot(always));
+ expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
+ }
+
+ test_topLevelFunction_parameterType_named_with_default() async {
+ await analyze('''
+void f({String s: 'x'}) {}
+''');
+ var decoratedType = decoratedTypeAnnotation('String');
+ var functionType = decoratedFunctionType('f');
+ expect(functionType.namedParameters['s'], same(decoratedType));
+ expect(decoratedType.node, isNotNull);
+ expect(decoratedType.node, isNot(never));
+ expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
+ }
+
+ test_topLevelFunction_parameterType_positionalOptional() async {
+ await analyze('''
+void f([int i]) {}
+''');
+ var decoratedType = decoratedTypeAnnotation('int');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, isNotNull);
+ expect(decoratedType.node, isNot(never));
+ }
+
+ test_topLevelFunction_parameterType_simple() async {
+ await analyze('''
+void f(int i) {}
+''');
+ var decoratedType = decoratedTypeAnnotation('int');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, isNotNull);
+ expect(decoratedType.node, isNot(never));
+ }
+
+ test_topLevelFunction_returnType_implicit_dynamic() async {
+ await analyze('''
+f() {}
+''');
+ var decoratedType = decoratedFunctionType('f').returnType;
+ expect(decoratedType.type.isDynamic, isTrue);
+ assertUnion(always, decoratedType.node);
+ }
+
+ test_topLevelFunction_returnType_simple() async {
+ await analyze('''
+int f() => 0;
+''');
+ var decoratedType = decoratedTypeAnnotation('int');
+ expect(decoratedFunctionType('f').returnType, same(decoratedType));
+ expect(decoratedType.node, isNotNull);
+ expect(decoratedType.node, isNot(never));
+ }
+
+ test_type_comment_bang() async {
+ await analyze('''
+void f(int/*!*/ i) {}
+''');
+ assertEdge(decoratedTypeAnnotation('int').node, never, hard: true);
+ }
+
+ test_type_comment_question() async {
+ await analyze('''
+void f(int/*?*/ i) {}
+''');
+ assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+ }
+
+ test_type_parameter_explicit_bound() async {
+ await analyze('''
+class C<T extends Object> {}
+''');
+ var bound = decoratedTypeParameterBound('T');
+ expect(decoratedTypeAnnotation('Object'), same(bound));
+ expect(bound.node, isNot(always));
+ expect(bound.type, typeProvider.objectType);
+ }
+
+ test_type_parameter_implicit_bound() async {
+ // The implicit bound of `T` is automatically `Object?`. TODO(paulberry):
+ // consider making it possible for type inference to infer an explicit bound
+ // of `Object`.
+ await analyze('''
+class C<T> {}
+''');
+ var bound = decoratedTypeParameterBound('T');
+ assertUnion(always, bound.node);
+ expect(bound.type, same(typeProvider.objectType));
+ }
+
+ test_variableDeclaration_type_simple() async {
+ await analyze('''
+main() {
+ int i;
+}
+''');
+ var decoratedType = decoratedTypeAnnotation('int');
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ }
+
+ test_void_type() async {
+ await analyze('''
+void f() {}
+''');
+ var decoratedType = decoratedTypeAnnotation('void');
+ expect(decoratedFunctionType('f').returnType, same(decoratedType));
+ assertEdge(always, decoratedType.node, hard: false);
+ }
+}
diff --git a/pkg/nnbd_migration/test/test_all.dart b/pkg/nnbd_migration/test/test_all.dart
index af35c52..4241cf2 100644
--- a/pkg/nnbd_migration/test/test_all.dart
+++ b/pkg/nnbd_migration/test/test_all.dart
@@ -5,13 +5,15 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'api_test.dart' as api_test;
-import 'migration_visitor_test.dart' as migration_visitor_test;
+import 'graph_builder_test.dart' as graph_builder_test;
+import 'node_builder_test.dart' as node_builder_test;
import 'nullability_node_test.dart' as nullability_node_test;
main() {
defineReflectiveSuite(() {
api_test.main();
- migration_visitor_test.main();
+ graph_builder_test.main();
+ node_builder_test.main();
nullability_node_test.main();
});
}