diff --git a/[refs] b/[refs] index 5673c01feb48..6dd03509dd5d 100644 --- a/[refs] +++ b/[refs] @@ -911,7 +911,7 @@ refs/tags/2.3.0-flutter-1.5.4-hotfix.1: a1668566e563aef64025d0af88a099cbbe847b7e refs/tags/2.3.1: 929b013ddc83a013b49a98fc28b6b503a972bddd refs/tags/2.3.1-dev.0.0: 1d1742efd39cd4762b844b510acf8c2f1fa6604e refs/tags/2.3.2-dev.0.0: c567183bac8a895014d79cd3bf1d8908d45547d6 -refs/heads/beta: c7ac27bf504cca030881208eb0b056a5a7ff93c2 +refs/heads/beta: c604e58a9b387856a630391aa0ec88acc010b88e refs/heads/sjindel.mep: b113b36c157cf54b257e82550e9bbde16f05ad8d refs/tags/2.3.2: f7ab96133aa79301daf812ef40b33c99d8ad1495 refs/tags/2.3.2-dev.0.1: ef57e27c9798b54a54e9a1f74b1bd1f9be7290b1 diff --git a/branches/beta/pkg/nnbd_migration/lib/src/edge_builder.dart b/branches/beta/pkg/nnbd_migration/lib/src/edge_builder.dart index 77ef414f6ae7..64c73356ec78 100644 --- a/branches/beta/pkg/nnbd_migration/lib/src/edge_builder.dart +++ b/branches/beta/pkg/nnbd_migration/lib/src/edge_builder.dart @@ -124,6 +124,9 @@ class EdgeBuilder extends GeneralizingAstVisitor /// For convenience, a [DecoratedType] representing `Null`. final DecoratedType _nullType; + /// For convenience, a [DecoratedType] representing `dynamic`. + final DecoratedType _dynamicType; + /// The [DecoratedType] of the innermost function or method being visited, or /// `null` if the visitor is not inside any function or method. /// @@ -175,7 +178,8 @@ class EdgeBuilder extends GeneralizingAstVisitor DecoratedType(_typeProvider.boolType, _graph.never), _nonNullableTypeType = DecoratedType(_typeProvider.typeType, _graph.never), - _nullType = DecoratedType(_typeProvider.nullType, _graph.always); + _nullType = DecoratedType(_typeProvider.nullType, _graph.always), + _dynamicType = DecoratedType(_typeProvider.dynamicType, _graph.always); /// Gets the decorated type of [element] from [_variables], performing any /// necessary substitutions. @@ -852,8 +856,10 @@ class EdgeBuilder extends GeneralizingAstVisitor } var callee = node.methodName.staticElement; if (callee == null) { - // TODO(paulberry) - _unimplemented(node, 'Unresolved method name'); + // Dynamic dispatch. The return type is `dynamic`. + // TODO(paulberry): would it be better to assume a return type of `Never` + // so that we don't unnecessarily propagate nullabilities everywhere? + return _dynamicType; } var calleeType = getOrComputeElementType(callee, targetType: targetType); if (callee is PropertyAccessorElement) { @@ -1450,7 +1456,7 @@ class EdgeBuilder extends GeneralizingAstVisitor '(${expression.toSource()}) offset=${expression.offset}'); } ExpressionChecks expressionChecks; - if (canInsertChecks) { + if (canInsertChecks && !sourceType.type.isDynamic) { expressionChecks = ExpressionChecks(expression.end); _variables.recordExpressionChecks(source, expression, expressionChecks); } diff --git a/branches/beta/pkg/nnbd_migration/test/api_test.dart b/branches/beta/pkg/nnbd_migration/test/api_test.dart index 286e462294cb..4a4e2660d3fe 100644 --- a/branches/beta/pkg/nnbd_migration/test/api_test.dart +++ b/branches/beta/pkg/nnbd_migration/test/api_test.dart @@ -821,6 +821,42 @@ int f(int i) { await _checkSingleFileChanges(content, expected); } + test_dynamic_method_call() async { + var content = ''' +class C { + int g(int i) => i; +} +int f(bool b, dynamic d) { + if (b) return 0; + return d.g(null); +} +main() { + f(true, null); + f(false, C()); +} +'''; + // `d.g(null)` is a dynamic call, so we can't tell that it will target `C.g` + // at runtime. So we can't figure out that we need to make g's argument and + // return types nullable. + // + // We do, however, make f's return type nullable, since there is no way of + // knowing whether a dynamic call will return `null`. + var expected = ''' +class C { + int g(int i) => i; +} +int? f(bool b, dynamic d) { + if (b) return 0; + return d.g(null); +} +main() { + f(true, null); + f(false, C()); +} +'''; + await _checkSingleFileChanges(content, expected); + } + test_field_formal_param_typed() async { var content = ''' class C { diff --git a/branches/beta/pkg/nnbd_migration/test/edge_builder_test.dart b/branches/beta/pkg/nnbd_migration/test/edge_builder_test.dart index 4f1a2c5bdd98..f2dab1f5b245 100644 --- a/branches/beta/pkg/nnbd_migration/test/edge_builder_test.dart +++ b/branches/beta/pkg/nnbd_migration/test/edge_builder_test.dart @@ -384,6 +384,20 @@ class C> { hard: false); } + test_assign_dynamic_to_other_type() async { + await analyze(''' +int f(dynamic d) => d; +'''); + // There is no explicit null check necessary, since `dynamic` is + // downcastable to any type, nullable or not. + expect(checkExpression('d;'), isNull); + // But we still create an edge, to make sure that the possibility of `null` + // propagates to callees. + assertEdge(decoratedTypeAnnotation('dynamic').node, + decoratedTypeAnnotation('int').node, + hard: true); + } + test_assign_null_to_generic_type() async { await analyze(''' main() { @@ -2204,6 +2218,25 @@ class C { assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true); } + test_methodInvocation_dynamic() async { + await analyze(''' +class C { + int g(int i) => i; +} +int f(dynamic d, int j) { + return d.g(j); +} +'''); + // The call `d.g(j)` is dynamic, so we can't tell what method it resolves + // to. There's no reason to assume it resolves to `C.g`. + assertNoEdge(decoratedTypeAnnotation('int j').node, + decoratedTypeAnnotation('int i').node); + assertNoEdge(decoratedTypeAnnotation('int g').node, + decoratedTypeAnnotation('int f').node); + // We do, however, assume that it might return anything, including `null`. + assertEdge(always, decoratedTypeAnnotation('int f').node, hard: false); + } + test_methodInvocation_parameter_contravariant() async { await analyze(''' class C {