Skip to content

Commit fa39d72

Browse files
committed
Fix missing downwards inference on fields, report more inference
Use the element type on the variable declaration to set the downwards inference context, so that we get downwards inference from inferred types. Also eliminated some TODOs about places where we were not recording inferences. Fixes #25546 . BUG= [email protected] Review URL: https://codereview.chromium.org/1906413004 .
1 parent 0108a21 commit fa39d72

File tree

5 files changed

+135
-50
lines changed

5 files changed

+135
-50
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6797,7 +6797,7 @@ class ResolverVisitor extends ScopedVisitor {
67976797
@override
67986798
visitVariableDeclarationList(VariableDeclarationList node) {
67996799
for (VariableDeclaration decl in node.variables) {
6800-
InferenceContext.setType(decl, node.type?.type);
6800+
InferenceContext.setType(decl, decl.element?.type);
68016801
}
68026802
super.visitVariableDeclarationList(node);
68036803
}

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

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -617,9 +617,11 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
617617
*/
618618
@override
619619
Object visitListLiteral(ListLiteral node) {
620-
DartType staticType = _dynamicType;
621620
TypeArgumentList typeArguments = node.typeArguments;
621+
622+
// If we have explicit arguments, use them
622623
if (typeArguments != null) {
624+
DartType staticType = _dynamicType;
623625
NodeList<TypeName> arguments = typeArguments.arguments;
624626
if (arguments != null && arguments.length == 1) {
625627
TypeName argumentTypeName = arguments[0];
@@ -628,25 +630,47 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
628630
staticType = argumentType;
629631
}
630632
}
631-
} else if (_strongMode) {
633+
_recordStaticType(
634+
node, _typeProvider.listType.instantiate(<DartType>[staticType]));
635+
return null;
636+
}
637+
638+
// If there are no type arguments and we are in strong mode, try to infer
639+
// some arguments.
640+
if (_strongMode) {
632641
DartType contextType = InferenceContext.getType(node);
642+
643+
// If we have a type from the context, use it.
633644
if (contextType is InterfaceType &&
634645
contextType.typeArguments.length == 1 &&
635646
contextType.element == _typeProvider.listType.element) {
636-
staticType = contextType.typeArguments[0];
637647
_resolver.inferenceContext.recordInference(node, contextType);
638-
} else if (node.elements.isNotEmpty) {
648+
_recordStaticType(node, contextType);
649+
return null;
650+
}
651+
652+
// If we don't have a type from the context, try to infer from the
653+
// elements
654+
if (node.elements.isNotEmpty) {
639655
// Infer the list type from the arguments.
640-
// TODO(jmesserly): record inference here?
641-
staticType =
656+
DartType staticType =
642657
node.elements.map((e) => e.staticType).reduce(_leastUpperBound);
643658
if (staticType.isBottom) {
644659
staticType = _dynamicType;
645660
}
661+
DartType listLiteralType =
662+
_typeProvider.listType.instantiate(<DartType>[staticType]);
663+
if (!staticType.isDynamic) {
664+
_resolver.inferenceContext.recordInference(node, listLiteralType);
665+
}
666+
_recordStaticType(node, listLiteralType);
667+
return null;
646668
}
647669
}
670+
671+
// If we have no type arguments and couldn't infer any, use dynamic.
648672
_recordStaticType(
649-
node, _typeProvider.listType.instantiate(<DartType>[staticType]));
673+
node, _typeProvider.listType.instantiate(<DartType>[_dynamicType]));
650674
return null;
651675
}
652676

@@ -664,10 +688,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
664688
*/
665689
@override
666690
Object visitMapLiteral(MapLiteral node) {
667-
DartType staticKeyType = _dynamicType;
668-
DartType staticValueType = _dynamicType;
669691
TypeArgumentList typeArguments = node.typeArguments;
692+
693+
// If we have type arguments, use them
670694
if (typeArguments != null) {
695+
DartType staticKeyType = _dynamicType;
696+
DartType staticValueType = _dynamicType;
671697
NodeList<TypeName> arguments = typeArguments.arguments;
672698
if (arguments != null && arguments.length == 2) {
673699
TypeName entryKeyTypeName = arguments[0];
@@ -681,20 +707,31 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
681707
staticValueType = entryValueType;
682708
}
683709
}
684-
} else if (_strongMode) {
710+
_recordStaticType(
711+
node,
712+
_typeProvider.mapType
713+
.instantiate(<DartType>[staticKeyType, staticValueType]));
714+
return null;
715+
}
716+
717+
// If we have no explicit type arguments, and we are in strong mode
718+
// then try to infer type arguments.
719+
if (_strongMode) {
685720
DartType contextType = InferenceContext.getType(node);
721+
// If we have a context type, use that for inference.
686722
if (contextType is InterfaceType &&
687723
contextType.typeArguments.length == 2 &&
688724
contextType.element == _typeProvider.mapType.element) {
689-
staticKeyType = contextType.typeArguments[0] ?? staticKeyType;
690-
staticValueType = contextType.typeArguments[1] ?? staticValueType;
691725
_resolver.inferenceContext.recordInference(node, contextType);
692-
} else if (node.entries.isNotEmpty) {
693-
// Infer the list type from the arguments.
694-
// TODO(jmesserly): record inference here?
695-
staticKeyType =
726+
_recordStaticType(node, contextType);
727+
return null;
728+
}
729+
730+
// Otherwise, try to infer a type from the keys and values.
731+
if (node.entries.isNotEmpty) {
732+
DartType staticKeyType =
696733
node.entries.map((e) => e.key.staticType).reduce(_leastUpperBound);
697-
staticValueType = node.entries
734+
DartType staticValueType = node.entries
698735
.map((e) => e.value.staticType)
699736
.reduce(_leastUpperBound);
700737
if (staticKeyType.isBottom) {
@@ -703,12 +740,21 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
703740
if (staticValueType.isBottom) {
704741
staticValueType = _dynamicType;
705742
}
743+
DartType mapLiteralType = _typeProvider.mapType
744+
.instantiate(<DartType>[staticKeyType, staticValueType]);
745+
if (!(staticValueType.isDynamic && staticKeyType.isDynamic)) {
746+
_resolver.inferenceContext.recordInference(node, mapLiteralType);
747+
}
748+
_recordStaticType(node, mapLiteralType);
749+
return null;
706750
}
707751
}
752+
753+
// If no type arguments and no inference, use dynamic
708754
_recordStaticType(
709755
node,
710756
_typeProvider.mapType
711-
.instantiate(<DartType>[staticKeyType, staticValueType]));
757+
.instantiate(<DartType>[_dynamicType, _dynamicType]));
712758
return null;
713759
}
714760

pkg/analyzer/test/generated/strong_mode_test.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,41 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase {
681681
verify([source]);
682682
}
683683

684+
void test_inferredFieldDeclaration_propagation() {
685+
// Regression test for https://github.com/dart-lang/sdk/issues/25546
686+
String code = r'''
687+
abstract class A {
688+
Map<int, List<int>> get map;
689+
}
690+
class B extends A {
691+
var map = { 42: [] };
692+
}
693+
class C extends A {
694+
get map => { 43: [] };
695+
}
696+
''';
697+
CompilationUnit unit = resolveSource(code);
698+
699+
Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
700+
Asserter<InterfaceType> assertMapOfIntToListOfInt =
701+
_isMapOf(_isInt, assertListOfInt);
702+
703+
VariableDeclaration mapB = AstFinder.getFieldInClass(unit, "B", "map");
704+
MethodDeclaration mapC = AstFinder.getMethodInClass(unit, "C", "map");
705+
assertMapOfIntToListOfInt(mapB.element.type);
706+
assertMapOfIntToListOfInt(mapC.element.returnType);
707+
708+
MapLiteral mapLiteralB = mapB.initializer;
709+
MapLiteral mapLiteralC = (mapC.body as ExpressionFunctionBody).expression;
710+
assertMapOfIntToListOfInt(mapLiteralB.staticType);
711+
assertMapOfIntToListOfInt(mapLiteralC.staticType);
712+
713+
ListLiteral listLiteralB = mapLiteralB.entries[0].value;
714+
ListLiteral listLiteralC = mapLiteralC.entries[0].value;
715+
assertListOfInt(listLiteralB.staticType);
716+
assertListOfInt(listLiteralC.staticType);
717+
}
718+
684719
void test_instanceCreation() {
685720
String code = r'''
686721
class A<S, T> {

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,10 +1686,10 @@ void main() {
16861686
l = <int>[i, /*info:DOWN_CAST_IMPLICIT*/n, /*warning:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/s];
16871687
}
16881688
{
1689-
List l = [i];
1690-
l = [s];
1691-
l = [n];
1692-
l = [i, n, s];
1689+
List l = /*info:INFERRED_TYPE_LITERAL*/[i];
1690+
l = /*info:INFERRED_TYPE_LITERAL*/[s];
1691+
l = /*info:INFERRED_TYPE_LITERAL*/[n];
1692+
l = /*info:INFERRED_TYPE_LITERAL*/[i, n, s];
16931693
}
16941694
{
16951695
Map<String, int> m = <String, int>{s: i};
@@ -1702,13 +1702,15 @@ void main() {
17021702
// TODO(leafp): We can't currently test for key errors since the
17031703
// error marker binds to the entire entry.
17041704
{
1705-
Map m = {s: i};
1706-
m = {s: s};
1707-
m = {s: n};
1708-
m = {s: i,
1705+
Map m = /*info:INFERRED_TYPE_LITERAL*/{s: i};
1706+
m = /*info:INFERRED_TYPE_LITERAL*/{s: s};
1707+
m = /*info:INFERRED_TYPE_LITERAL*/{s: n};
1708+
m = /*info:INFERRED_TYPE_LITERAL*/
1709+
{s: i,
17091710
s: n,
17101711
s: s};
1711-
m = {i: s,
1712+
m = /*info:INFERRED_TYPE_LITERAL*/
1713+
{i: s,
17121714
n: s,
17131715
s: s};
17141716
}

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

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ test1() {
265265
main() {
266266
var f = /*info:INFERRED_TYPE_CLOSURE*/() sync* {
267267
yield 1;
268-
yield* [3, 4.0];
268+
yield* /*info:INFERRED_TYPE_LITERAL*/[3, 4.0];
269269
};
270270
Iterable<num> g = f();
271271
Iterable<int> h = /*info:ASSIGNMENT_CAST*/f();
@@ -695,14 +695,16 @@ void main() {
695695
/*info:INFERRED_TYPE_LITERAL*/[3]]);
696696
697697
new F3(/*info:INFERRED_TYPE_LITERAL*/[]);
698-
new F3(/*info:INFERRED_TYPE_LITERAL*/[[3]]);
699-
new F3(/*info:INFERRED_TYPE_LITERAL*/[["hello"]]);
700-
new F3(/*info:INFERRED_TYPE_LITERAL*/[["hello"], [3]]);
698+
new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
699+
new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"]]);
700+
new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"],
701+
/*info:INFERRED_TYPE_LITERAL*/[3]]);
701702
702703
new F4(a: /*info:INFERRED_TYPE_LITERAL*/[]);
703-
new F4(a: /*info:INFERRED_TYPE_LITERAL*/[[3]]);
704-
new F4(a: /*info:INFERRED_TYPE_LITERAL*/[["hello"]]);
705-
new F4(a: /*info:INFERRED_TYPE_LITERAL*/[["hello"], [3]]);
704+
new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
705+
new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"]]);
706+
new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"],
707+
/*info:INFERRED_TYPE_LITERAL*/[3]]);
706708
}
707709
''');
708710
}
@@ -884,9 +886,9 @@ void main() {
884886
}
885887
{
886888
List<dynamic> l0 = [];
887-
List<dynamic> l1 = [3];
888-
List<dynamic> l2 = ["hello"];
889-
List<dynamic> l3 = ["hello", 3];
889+
List<dynamic> l1 = /*info:INFERRED_TYPE_LITERAL*/[3];
890+
List<dynamic> l2 = /*info:INFERRED_TYPE_LITERAL*/["hello"];
891+
List<dynamic> l3 = /*info:INFERRED_TYPE_LITERAL*/["hello", 3];
890892
}
891893
{
892894
List<int> l0 = /*severe:STATIC_TYPE_ERROR*/<num>[];
@@ -997,10 +999,10 @@ void main() {
997999
}
9981000
{
9991001
Map<dynamic, dynamic> l0 = {};
1000-
Map<dynamic, dynamic> l1 = {3: "hello"};
1001-
Map<dynamic, dynamic> l2 = {"hello": "hello"};
1002-
Map<dynamic, dynamic> l3 = {3: 3};
1003-
Map<dynamic, dynamic> l4 = {3:"hello", "hello": 3};
1002+
Map<dynamic, dynamic> l1 = /*info:INFERRED_TYPE_LITERAL*/{3: "hello"};
1003+
Map<dynamic, dynamic> l2 = /*info:INFERRED_TYPE_LITERAL*/{"hello": "hello"};
1004+
Map<dynamic, dynamic> l3 = /*info:INFERRED_TYPE_LITERAL*/{3: 3};
1005+
Map<dynamic, dynamic> l4 = /*info:INFERRED_TYPE_LITERAL*/{3:"hello", "hello": 3};
10041006
}
10051007
{
10061008
Map<dynamic, String> l0 = /*info:INFERRED_TYPE_LITERAL*/{};
@@ -2438,14 +2440,14 @@ main() {
24382440
void test_listLiterals() {
24392441
checkFile(r'''
24402442
test1() {
2441-
var x = [1, 2, 3];
2443+
var x = /*info:INFERRED_TYPE_LITERAL*/[1, 2, 3];
24422444
x.add(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
24432445
x.add(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/4.0);
24442446
x.add(4);
24452447
List<num> y = x;
24462448
}
24472449
test2() {
2448-
var x = [1, 2.0, 3];
2450+
var x = /*info:INFERRED_TYPE_LITERAL*/[1, 2.0, 3];
24492451
x.add(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
24502452
x.add(4.0);
24512453
List<int> y = /*info:ASSIGNMENT_CAST*/x;
@@ -2455,14 +2457,14 @@ test2() {
24552457

24562458
void test_listLiterals_topLevel() {
24572459
checkFile(r'''
2458-
var x1 = [1, 2, 3];
2460+
var x1 = /*info:INFERRED_TYPE_LITERAL*/[1, 2, 3];
24592461
test1() {
24602462
x1.add(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
24612463
x1.add(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/4.0);
24622464
x1.add(4);
24632465
List<num> y = x1;
24642466
}
2465-
var x2 = [1, 2.0, 3];
2467+
var x2 = /*info:INFERRED_TYPE_LITERAL*/[1, 2.0, 3];
24662468
test2() {
24672469
x2.add(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
24682470
x2.add(4.0);
@@ -2485,7 +2487,7 @@ test1() {
24852487
void test_mapLiterals() {
24862488
checkFile(r'''
24872489
test1() {
2488-
var x = { 1: 'x', 2: 'y' };
2490+
var x = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y' };
24892491
x[3] = 'z';
24902492
x[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';
24912493
x[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/4.0] = 'u';
@@ -2494,7 +2496,7 @@ test1() {
24942496
}
24952497
24962498
test2() {
2497-
var x = { 1: 'x', 2: 'y', 3.0: new RegExp('.') };
2499+
var x = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y', 3.0: new RegExp('.') };
24982500
x[3] = 'z';
24992501
x[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';
25002502
x[4.0] = 'u';
@@ -2508,7 +2510,7 @@ test2() {
25082510

25092511
void test_mapLiterals_topLevel() {
25102512
checkFile(r'''
2511-
var x1 = { 1: 'x', 2: 'y' };
2513+
var x1 = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y' };
25122514
test1() {
25132515
x1[3] = 'z';
25142516
x1[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';
@@ -2517,7 +2519,7 @@ test1() {
25172519
Map<num, String> y = x1;
25182520
}
25192521
2520-
var x2 = { 1: 'x', 2: 'y', 3.0: new RegExp('.') };
2522+
var x2 = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y', 3.0: new RegExp('.') };
25212523
test2() {
25222524
x2[3] = 'z';
25232525
x2[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';

0 commit comments

Comments
 (0)