Skip to content

Commit 0bf5456

Browse files
author
John Messerly
committed
partial fix for #25220, handles inference for list/map literals
[email protected] Review URL: https://codereview.chromium.org/1717803003 .
1 parent 5322692 commit 0bf5456

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

pkg/analyzer/lib/src/generated/static_type_analyzer.dart

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -626,14 +626,18 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
626626
staticType = argumentType;
627627
}
628628
}
629-
} else {
629+
} else if (_strongMode) {
630630
DartType contextType = InferenceContext.getType(node);
631-
if (_strongMode &&
632-
contextType is InterfaceType &&
631+
if (contextType is InterfaceType &&
633632
contextType.typeArguments.length == 1 &&
634633
contextType.element == _typeProvider.listType.element) {
635634
staticType = contextType.typeArguments[0];
636635
_resolver.inferenceContext.recordInference(node, contextType);
636+
} else if (node.elements.isNotEmpty) {
637+
// Infer the list type from the arguments.
638+
staticType =
639+
node.elements.map((e) => e.staticType).reduce(_leastUpperBound);
640+
// TODO(jmesserly): record inference here?
637641
}
638642
}
639643
_recordStaticType(
@@ -672,15 +676,22 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
672676
staticValueType = entryValueType;
673677
}
674678
}
675-
} else {
679+
} else if (_strongMode) {
676680
DartType contextType = InferenceContext.getType(node);
677-
if (_strongMode &&
678-
contextType is InterfaceType &&
681+
if (contextType is InterfaceType &&
679682
contextType.typeArguments.length == 2 &&
680683
contextType.element == _typeProvider.mapType.element) {
681684
staticKeyType = contextType.typeArguments[0] ?? staticKeyType;
682685
staticValueType = contextType.typeArguments[1] ?? staticValueType;
683686
_resolver.inferenceContext.recordInference(node, contextType);
687+
} else if (node.entries.isNotEmpty) {
688+
// Infer the list type from the arguments.
689+
staticKeyType =
690+
node.entries.map((e) => e.key.staticType).reduce(_leastUpperBound);
691+
staticValueType = node.entries
692+
.map((e) => e.value.staticType)
693+
.reduce(_leastUpperBound);
694+
// TODO(jmesserly): record inference here?
684695
}
685696
}
686697
_recordStaticType(
@@ -1535,8 +1546,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
15351546
}
15361547
}
15371548

1538-
// TODO(vsm): Use leafp's matchType here?
15391549
DartType _findIteratedType(DartType type, DartType targetType) {
1550+
// TODO(vsm): Use leafp's matchType here?
15401551
// Set by _find if match is found
15411552
DartType result;
15421553
// Elements we've already visited on a given inheritance path.
@@ -2061,6 +2072,14 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
20612072
parent.operator.type == TokenType.PERIOD);
20622073
}
20632074

2075+
/**
2076+
* Computes the least upper bound between two types.
2077+
*
2078+
* See [TypeSystem.getLeastUpperBound].
2079+
*/
2080+
DartType _leastUpperBound(DartType s, DartType t) =>
2081+
_typeSystem.getLeastUpperBound(_typeProvider, s, t);
2082+
20642083
/**
20652084
* Record that the propagated type of the given node is the given type.
20662085
*

pkg/analyzer/test/src/context/mock_sdk.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ abstract class String implements Comparable<String>, Pattern {
9797
String toUpperCase();
9898
List<int> get codeUnits;
9999
}
100+
abstract class RegExp implements Pattern {
101+
external factory RegExp(String source);
102+
}
100103
101104
class bool extends Object {}
102105
abstract class num implements Comparable<num> {
@@ -154,6 +157,8 @@ abstract class List<E> implements Iterable<E> {
154157
155158
abstract class Map<K, V> extends Object {
156159
Iterable<K> get keys;
160+
V operator [](K key);
161+
void operator []=(K key, V value);
157162
}
158163
159164
external bool identical(Object a, Object b);

pkg/analyzer/test/src/summary/summary_common.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7077,11 +7077,12 @@ var v;''';
70777077

70787078
test_variable_initializer_withLocals() {
70797079
String text =
7080-
'var v = {"1": () { f1() {} var v1; }, "2": () { f2() {} var v2; }};';
7080+
'var v = <dynamic, dynamic>{"1": () { f1() {} var v1; }, '
7081+
'"2": () { f2() {} var v2; }};';
70817082
UnlinkedVariable variable = serializeVariableText(text);
70827083
UnlinkedExecutable initializer = variable.initializer;
70837084
expect(initializer, isNotNull);
7084-
expect(initializer.nameOffset, text.indexOf('{"1'));
7085+
expect(initializer.nameOffset, text.indexOf('<dynamic, dynamic>{"1'));
70857086
expect(initializer.name, isEmpty);
70867087
expect(initializer.localFunctions, hasLength(2));
70877088
// closure: () { f1() {} var v1; }

pkg/analyzer/test/src/task/strong/inferred_type_test.dart

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ void main() {
339339
var b = new B(/*warning:UNDEFINED_IDENTIFIER*/x); // allocations
340340
var c1 = [/*warning:UNDEFINED_IDENTIFIER*/x]; // list literals
341341
var c2 = const [];
342-
var d = {'a': 'b'}; // map literals
342+
var d = <dynamic, dynamic>{'a': 'b'}; // map literals
343343
var e = new A()..x = 3; // cascades
344344
var f = 2 + 3; // binary expressions are OK if the left operand
345345
// is from a library in a different strongest
@@ -1580,8 +1580,9 @@ main() {
15801580
checkFile('''
15811581
import 'dart:async';
15821582
Future<int> test() async {
1583-
List<int> l0 = /*warning:DOWN_CAST_COMPOSITE should be pass*/await /*pass should be info:INFERRED_TYPE_LITERAL*/[3];
1584-
List<int> l1 = await /*info:INFERRED_TYPE_ALLOCATION*/new Future.value(/*info:INFERRED_TYPE_LITERAL*/[3]);
1583+
dynamic d;
1584+
List<int> l0 = /*warning:DOWN_CAST_COMPOSITE should be pass*/await /*pass should be info:INFERRED_TYPE_LITERAL*/[d];
1585+
List<int> l1 = await /*info:INFERRED_TYPE_ALLOCATION*/new Future.value(/*info:INFERRED_TYPE_LITERAL*/[/*info:DYNAMIC_CAST*/d]);
15851586
}
15861587
''');
15871588
});
@@ -1877,4 +1878,45 @@ main() {
18771878
''');
18781879
});
18791880

1881+
test('list literals', () {
1882+
checkFile(r'''
1883+
test1() {
1884+
var x = [1, 2, 3];
1885+
x.add(/*severe:STATIC_TYPE_ERROR*/'hi');
1886+
x.add(/*severe:STATIC_TYPE_ERROR*/4.0);
1887+
x.add(4);
1888+
List<num> y = x;
1889+
}
1890+
test2() {
1891+
var x = [1, 2.0, 3];
1892+
x.add(/*severe:STATIC_TYPE_ERROR*/'hi');
1893+
x.add(4.0);
1894+
List<int> y = /*info:ASSIGNMENT_CAST*/x;
1895+
}
1896+
''');
1897+
});
1898+
1899+
test('map literals', () {
1900+
checkFile(r'''
1901+
test1() {
1902+
var x = { 1: 'x', 2: 'y' };
1903+
x[3] = 'z';
1904+
x[/*severe:STATIC_TYPE_ERROR*/'hi'] = 'w';
1905+
x[/*severe:STATIC_TYPE_ERROR*/4.0] = 'u';
1906+
x[3] = /*severe:STATIC_TYPE_ERROR*/42;
1907+
Map<num, String> y = x;
1908+
}
1909+
1910+
test2() {
1911+
var x = { 1: 'x', 2: 'y', 3.0: new RegExp('.') };
1912+
x[3] = 'z';
1913+
x[/*severe:STATIC_TYPE_ERROR*/'hi'] = 'w';
1914+
x[4.0] = 'u';
1915+
x[3] = /*severe:STATIC_TYPE_ERROR*/42;
1916+
Pattern p = null;
1917+
x[2] = p;
1918+
Map<int, String> y = /*info:ASSIGNMENT_CAST*/x;
1919+
}
1920+
''');
1921+
});
18801922
}

0 commit comments

Comments
 (0)