Fix type inference of map/set literals when the context is a type parameter.
Change-Id: Ic78cfd838e7fce4f362770c0827d71b351711e57
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96340
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/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 7d6bc69..ca1da1e 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -4775,9 +4775,30 @@
@override
void visitSetOrMapLiteral(SetOrMapLiteral node) {
- DartType literalType = _computeSetOrMapContextType(node);
- // TODO(brianwilkerson) Determine whether we need special handling for type
- // parameter types. (E-mail sent.)
+ var typeArguments = node.typeArguments?.arguments;
+ InterfaceType literalType;
+ var literalResolution = _computeSetOrMapResolution(node);
+ if (literalResolution.kind == _LiteralResolutionKind.set) {
+ if (typeArguments != null && typeArguments.length == 1) {
+ var elementType = typeArguments[0].type;
+ literalType = typeProvider.setType.instantiate([elementType]);
+ } else {
+ literalType = typeAnalyzer.inferSetTypeDownwards(
+ node, literalResolution.contextType);
+ }
+ } else if (literalResolution.kind == _LiteralResolutionKind.map) {
+ if (typeArguments != null && typeArguments.length == 2) {
+ var keyType = typeArguments[0].type;
+ var valueType = typeArguments[1].type;
+ literalType = typeProvider.mapType.instantiate([keyType, valueType]);
+ } else {
+ literalType = typeAnalyzer.inferMapTypeDownwards(
+ node, literalResolution.contextType);
+ }
+ } else {
+ assert(literalResolution.kind == _LiteralResolutionKind.ambiguous);
+ literalType = null;
+ }
if (literalType is InterfaceType) {
List<DartType> typeArguments = literalType.typeArguments;
if (typeArguments.length == 1) {
@@ -5010,7 +5031,7 @@
}
/// Compute the context type for the given set or map [literal].
- DartType _computeSetOrMapContextType(SetOrMapLiteral literal) {
+ _LiteralResolution _computeSetOrMapResolution(SetOrMapLiteral literal) {
_LiteralResolution typeArgumentsResolution =
_fromTypeArguments(literal.typeArguments);
DartType contextType = InferenceContext.getContext(literal);
@@ -5037,29 +5058,32 @@
// It looks like it needs to be both a map and a set. Attempt to recover.
if (elementResolution.kind == _LiteralResolutionKind.ambiguous &&
elementResolution.contextType != null) {
- return elementResolution.contextType;
+ return elementResolution;
} else if (typeArgumentsResolution.kind !=
_LiteralResolutionKind.ambiguous &&
typeArgumentsResolution.contextType != null) {
- return typeArgumentsResolution.contextType;
+ return typeArgumentsResolution;
} else if (contextResolution.kind != _LiteralResolutionKind.ambiguous &&
contextResolution.contextType != null) {
- return contextResolution.contextType;
+ return contextResolution;
}
} else if (unambiguousResolutions.length >= 2) {
// If there are three resolutions, the last resolution is guaranteed to be
// from the elements, which always has a context type of `null` (when it
// is not ambiguous). So, whether there are 2 or 3 resolutions only the
// first two are potentially interesting.
- return unambiguousResolutions[0].contextType ??
- unambiguousResolutions[1].contextType;
+ return unambiguousResolutions[0].contextType == null
+ ? unambiguousResolutions[1]
+ : unambiguousResolutions[0];
} else if (unambiguousResolutions.length == 1) {
- return unambiguousResolutions[0].contextType;
+ return unambiguousResolutions[0];
} else if (literal.elements2.isEmpty) {
- return typeProvider.mapType
- .instantiate([typeProvider.dynamicType, typeProvider.dynamicType]);
+ return _LiteralResolution(
+ _LiteralResolutionKind.map,
+ typeProvider.mapType.instantiate(
+ [typeProvider.dynamicType, typeProvider.dynamicType]));
}
- return null;
+ return _LiteralResolution(_LiteralResolutionKind.ambiguous, null);
}
/// Return a newly created cloner that can be used to clone constant
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index d9ba671..2891951 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -180,6 +180,35 @@
return inferred;
}
+ ParameterizedType inferMapTypeDownwards(
+ SetOrMapLiteral node, DartType contextType) {
+ if (contextType == null) {
+ return null;
+ }
+
+ var ts = _typeSystem as Dart2TypeSystem;
+ ParameterizedType inferred = ts.inferGenericFunctionOrType(
+ _typeProvider.mapType, [], [], contextType,
+ downwards: true,
+ errorReporter: _resolver.errorReporter,
+ errorNode: node);
+ return inferred;
+ }
+
+ DartType inferSetTypeDownwards(SetOrMapLiteral node, DartType contextType) {
+ if (contextType == null) {
+ return null;
+ }
+
+ var ts = _typeSystem as Dart2TypeSystem;
+ DartType inferred = ts.inferGenericFunctionOrType<InterfaceType>(
+ _typeProvider.setType, [], [], contextType,
+ downwards: true,
+ errorReporter: _resolver.errorReporter,
+ errorNode: node);
+ return inferred;
+ }
+
/**
* The Dart Language Specification, 12.5: <blockquote>The static type of a string literal is
* `String`.</blockquote>
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/list_literal_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/list_literal_test.dart
index 8a4f760..855600e 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/list_literal_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/list_literal_test.dart
@@ -51,6 +51,16 @@
assertType(findNode.listLiteral('['), 'List<dynamic>');
}
+ test_context_noTypeArgs_noElements_typeParameter_dynamic() async {
+ addTestFile('''
+class A<E extends List<dynamic>> {
+ E a = [];
+}
+''');
+ await resolveTestFile();
+ assertType(findNode.listLiteral('['), 'List<dynamic>');
+ }
+
test_context_typeArgs_expression_conflictingContext() async {
addTestFile('''
List<String> a = <int>[0];
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/map_literal_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/map_literal_test.dart
index 89f9942..389136f 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/map_literal_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/map_literal_test.dart
@@ -52,6 +52,26 @@
assertType(setOrMapLiteral('{'), 'Map<String, String>');
}
+ test_context_noTypeArgs_noEntries_typeParameters() async {
+ addTestFile('''
+class A<E extends Map<int, String>> {
+ E a = {};
+}
+''');
+ await resolveTestFile();
+ assertType(setOrMapLiteral('{}'), 'Map<dynamic, dynamic>');
+ }
+
+ test_context_noTypeArgs_noEntries_typeParameters_dynamic() async {
+ addTestFile('''
+class A<E extends Map<dynamic, dynamic>> {
+ E a = {};
+}
+''');
+ await resolveTestFile();
+ assertType(setOrMapLiteral('{}'), 'Map<dynamic, dynamic>');
+ }
+
test_context_typeArgs_entry_conflictingKey() async {
addTestFile('''
Map<String, String> a = <String, String>{0 : 'a'};
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/set_literal_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/set_literal_test.dart
index 8630d09..797764a 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/set_literal_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/set_literal_test.dart
@@ -44,9 +44,6 @@
assertType(setLiteral('{'), 'Set<String>');
}
- @FailingTest(
- issue: 'https://github.com/dart-lang/sdk/issues/35569',
- reason: 'Failing because Map<dynamic, dynamic> is being inferred.')
test_context_noTypeArgs_noElements_typeParameter() async {
addTestFile('''
class A<E extends Set<int>> {
@@ -57,6 +54,16 @@
assertType(setLiteral('{}'), 'Set<dynamic>');
}
+ test_context_noTypeArgs_noElements_typeParameter_dynamic() async {
+ addTestFile('''
+class A<E extends Set<dynamic>> {
+ E a = {};
+}
+''');
+ await resolveTestFile();
+ assertType(setLiteral('{}'), 'Set<dynamic>');
+ }
+
test_context_typeArgs_expression_conflictingExpression() async {
addTestFile('''
Set<String> a = <String>{0};