From a931a457f04647772dbee19805d9c956ba237953 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 4 Sep 2019 20:21:03 +0000 Subject: [PATCH] [nnbd_migration] support untyped set & map literals Change-Id: Iaf2c72cfe9415e89e8d9000f473aa364bbcf012a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115267 Reviewed-by: Paul Berry Commit-Queue: Mike Fairhurst --- pkg/nnbd_migration/lib/src/edge_builder.dart | 58 ++++++++----- pkg/nnbd_migration/test/api_test.dart | 32 +++++++ .../test/edge_builder_test.dart | 86 +++++++++++-------- 3 files changed, 118 insertions(+), 58 deletions(-) diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart index a13e0cb2ed8..a680df9ef16 100644 --- a/pkg/nnbd_migration/lib/src/edge_builder.dart +++ b/pkg/nnbd_migration/lib/src/edge_builder.dart @@ -1023,34 +1023,50 @@ class EdgeBuilder extends GeneralizingAstVisitor @override DecoratedType visitSetOrMapLiteral(SetOrMapLiteral node) { - var listType = node.staticType as InterfaceType; + var setOrMapType = node.staticType as InterfaceType; var typeArguments = node.typeArguments?.arguments; - if (typeArguments == null) { - // TODO(brianwilkerson) We might want to create fake nodes in the graph to - // represent the type arguments so that we can still create edges from - // the elements to them. - // TODO(brianwilkerson) - _unimplemented(node, 'Set or map literal with no type arguments'); - } else if (typeArguments.length == 1) { - var elementType = - _variables.decoratedTypeAnnotation(source, typeArguments[0]); + + if (node.isSet) { + DecoratedType elementType; + if (typeArguments == null) { + assert(setOrMapType.typeArguments.length == 1); + elementType = DecoratedType.forImplicitType( + _typeProvider, setOrMapType.typeArguments[0], _graph); + } else { + assert(typeArguments.length == 1); + elementType = + _variables.decoratedTypeAnnotation(source, typeArguments[0]); + } for (var element in node.elements) { if (element is Expression) { _handleAssignment(element, destinationType: elementType); } else { // Handle spread and control flow elements. element.accept(this); - // TODO(brianwilkerson) + // TODO(mfairhurst) _unimplemented(node, 'Spread or control flow element'); } } - return DecoratedType(listType, _graph.never, + return DecoratedType(setOrMapType, _graph.never, typeArguments: [elementType]); - } else if (typeArguments.length == 2) { - var keyType = - _variables.decoratedTypeAnnotation(source, typeArguments[0]); - var valueType = - _variables.decoratedTypeAnnotation(source, typeArguments[1]); + } else { + assert(node.isMap); + DecoratedType keyType; + DecoratedType valueType; + + if (typeArguments == null) { + assert(setOrMapType.typeArguments.length == 2); + keyType = DecoratedType.forImplicitType( + _typeProvider, setOrMapType.typeArguments[0], _graph); + valueType = DecoratedType.forImplicitType( + _typeProvider, setOrMapType.typeArguments[1], _graph); + } else { + assert(typeArguments.length == 2); + keyType = _variables.decoratedTypeAnnotation(source, typeArguments[0]); + valueType = + _variables.decoratedTypeAnnotation(source, typeArguments[1]); + } + for (var element in node.elements) { if (element is MapLiteralEntry) { _handleAssignment(element.key, destinationType: keyType); @@ -1058,16 +1074,12 @@ class EdgeBuilder extends GeneralizingAstVisitor } else { // Handle spread and control flow elements. element.accept(this); - // TODO(brianwilkerson) + // TODO(mfairhurst) _unimplemented(node, 'Spread or control flow element'); } } - return DecoratedType(listType, _graph.never, + return DecoratedType(setOrMapType, _graph.never, typeArguments: [keyType, valueType]); - } else { - // TODO(brianwilkerson) - _unimplemented( - node, 'Set or map literal with more than two type arguments'); } } diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart index 12c0bb3c8bf..8098133a8ee 100644 --- a/pkg/nnbd_migration/test/api_test.dart +++ b/pkg/nnbd_migration/test/api_test.dart @@ -1017,6 +1017,38 @@ class C { await _checkSingleFileChanges(content, expected); } + test_field_initializer_untyped_map_literal() async { + var content = ''' +class C { + Map f; + C() : f = {"foo": null}; +} +'''; + var expected = ''' +class C { + Map f; + C() : f = {"foo": null}; +} +'''; + await _checkSingleFileChanges(content, expected); + } + + test_field_initializer_untyped_set_literal() async { + var content = ''' +class C { + Set f; + C() : f = {null}; +} +'''; + var expected = ''' +class C { + Set f; + C() : f = {null}; +} +'''; + await _checkSingleFileChanges(content, expected); + } + test_field_type_inferred() async { var content = ''' int f() => null; diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart index 83098069656..b757da740f2 100644 --- a/pkg/nnbd_migration/test/edge_builder_test.dart +++ b/pkg/nnbd_migration/test/edge_builder_test.dart @@ -3757,60 +3757,74 @@ class C { checkExpression('null'), assertEdge(always, tNode, hard: false)); } - @failingTest test_setOrMapLiteral_map_noTypeArgument_noNullableKeysAndValues() async { - // Failing because we're not yet handling collection literals without a - // type argument. await analyze(''' Map f() { return {'a' : 1, 'b' : 2}; } '''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - // TODO(brianwilkerson) Add an assertion that there is an edge from the set - // literal's fake type argument to the return type's type argument. + var keyNode = decoratedTypeAnnotation('String').node; + var valueNode = decoratedTypeAnnotation('int').node; + var mapNode = decoratedTypeAnnotation('Map').node; + + assertNoUpstreamNullability(mapNode); + assertNoUpstreamNullability( + assertEdge(anyNode, keyNode, hard: false).primarySource); + assertNoUpstreamNullability( + assertEdge(anyNode, valueNode, hard: false).primarySource); } - @failingTest test_setOrMapLiteral_map_noTypeArgument_nullableKey() async { - // Failing because we're not yet handling collection literals without a - // type argument. await analyze(''' Map f() { return {'a' : 1, null : 2, 'c' : 3}; } '''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - assertEdge(always, decoratedTypeAnnotation('String').node, hard: false); - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); + var keyNode = decoratedTypeAnnotation('String').node; + var valueNode = decoratedTypeAnnotation('int').node; + var mapNode = decoratedTypeAnnotation('Map').node; + + assertNoUpstreamNullability(mapNode); + assertEdge(always, assertEdge(anyNode, keyNode, hard: false).primarySource, + hard: false); + assertNoUpstreamNullability( + assertEdge(anyNode, valueNode, hard: false).primarySource); } - @failingTest test_setOrMapLiteral_map_noTypeArgument_nullableKeyAndValue() async { - // Failing because we're not yet handling collection literals without a - // type argument. await analyze(''' Map f() { return {'a' : 1, null : null, 'c' : 3}; } '''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - assertEdge(always, decoratedTypeAnnotation('String').node, hard: false); - assertEdge(always, decoratedTypeAnnotation('int').node, hard: false); + var keyNode = decoratedTypeAnnotation('String').node; + var valueNode = decoratedTypeAnnotation('int').node; + var mapNode = decoratedTypeAnnotation('Map').node; + + assertNoUpstreamNullability(mapNode); + assertEdge(always, assertEdge(anyNode, keyNode, hard: false).primarySource, + hard: false); + assertEdge( + always, assertEdge(anyNode, valueNode, hard: false).primarySource, + hard: false); } - @failingTest test_setOrMapLiteral_map_noTypeArgument_nullableValue() async { - // Failing because we're not yet handling collection literals without a - // type argument. await analyze(''' Map f() { return {'a' : 1, 'b' : null, 'c' : 3}; } '''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - assertNoUpstreamNullability(decoratedTypeAnnotation('String').node); - assertEdge(always, decoratedTypeAnnotation('int').node, hard: false); + var keyNode = decoratedTypeAnnotation('String').node; + var valueNode = decoratedTypeAnnotation('int').node; + var mapNode = decoratedTypeAnnotation('Map').node; + + assertNoUpstreamNullability(mapNode); + assertNoUpstreamNullability( + assertEdge(anyNode, keyNode, hard: false).primarySource); + assertEdge( + always, assertEdge(anyNode, valueNode, hard: false).primarySource, + hard: false); } test_setOrMapLiteral_map_typeArguments_noNullableKeysAndValues() async { @@ -3867,31 +3881,33 @@ Map f() { assertEdge(always, decoratedTypeAnnotation('int>{').node, hard: false); } - @failingTest test_setOrMapLiteral_set_noTypeArgument_noNullableElements() async { - // Failing because we're not yet handling collection literals without a - // type argument. await analyze(''' Set f() { return {'a', 'b'}; } '''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node); - // TODO(brianwilkerson) Add an assertion that there is an edge from the set - // literal's fake type argument to the return type's type argument. + var valueNode = decoratedTypeAnnotation('String').node; + var setNode = decoratedTypeAnnotation('Set').node; + + assertNoUpstreamNullability(setNode); + assertNoUpstreamNullability( + assertEdge(anyNode, valueNode, hard: false).primarySource); } - @failingTest test_setOrMapLiteral_set_noTypeArgument_nullableElement() async { - // Failing because we're not yet handling collection literals without a - // type argument. await analyze(''' Set f() { return {'a', null, 'c'}; } '''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node); - assertEdge(always, decoratedTypeAnnotation('String').node, hard: false); + var valueNode = decoratedTypeAnnotation('String').node; + var setNode = decoratedTypeAnnotation('Set').node; + + assertNoUpstreamNullability(setNode); + assertEdge( + always, assertEdge(anyNode, valueNode, hard: false).primarySource, + hard: false); } test_setOrMapLiteral_set_typeArgument_noNullableElements() async {