Improve for-in statement inference.

R=brianwilkerson@google.com, paulberry@google.com

Bug: https://github.com/dart-lang/sdk/issues/31638
Change-Id: Ie15486387a6b5955e42e9fe5fc00cdba4f2ba68a
     https://github.com/dart-lang/sdk/issues/31440
Reviewed-on: https://dart-review.googlesource.com/32861
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2018-01-07 10:21:35 -08:00 committed by commit-bot@chromium.org
parent 105b170ddb
commit bbd835f322
3 changed files with 117 additions and 14 deletions

View file

@ -5646,25 +5646,40 @@ class ResolverVisitor extends ScopedVisitor {
@override
void visitForEachStatementInScope(ForEachStatement node) {
Expression iterable = node.iterable;
DeclaredIdentifier loopVariable = node.loopVariable;
SimpleIdentifier identifier = node.identifier;
identifier?.accept(this);
DartType valueType;
if (loopVariable != null) {
TypeAnnotation typeAnnotation = loopVariable.type;
valueType = typeAnnotation?.type ?? typeProvider.dynamicType;
}
if (identifier != null) {
Element element = identifier.staticElement;
if (element is VariableElement) {
valueType = element.type;
} else if (element is PropertyAccessorElement) {
if (element.parameters.isNotEmpty) {
valueType = element.parameters[0].type;
}
}
}
if (valueType != null) {
InterfaceType targetType = (node.awaitKeyword == null)
? typeProvider.iterableType
: typeProvider.streamType;
InferenceContext.setType(iterable, targetType.instantiate([valueType]));
}
//
// We visit the iterator before the loop variable because the loop variable
// cannot be in scope while visiting the iterator.
//
Expression iterable = node.iterable;
DeclaredIdentifier loopVariable = node.loopVariable;
SimpleIdentifier identifier = node.identifier;
if (loopVariable?.type?.type != null) {
InterfaceType targetType = (node.awaitKeyword == null)
? typeProvider.iterableType
: typeProvider.streamType;
InferenceContext.setType(
iterable,
targetType
.instantiate([resolutionMap.typeForTypeName(loopVariable.type)]));
}
iterable?.accept(this);
loopVariable?.accept(this);
identifier?.accept(this);
Statement body = node.body;
if (body != null) {
_overrideManager.enterScope();

View file

@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:test/test.dart';
@ -28,6 +29,86 @@ class Dart2InferenceTest extends ResolverTestCase {
@override
bool get enableNewAnalysisDriver => true;
test_forIn() async {
var code = r'''
T f<T>() => null;
void test(Iterable<num> iter) {
for (var w in f()) {} // 1
for (var x in iter) {} // 2
for (num y in f()) {} // 3
}
''';
var source = addSource(code);
var analysisResult = await computeAnalysisResult(source);
var unit = analysisResult.unit;
{
var node = EngineTestCase.findSimpleIdentifier(unit, code, 'w in');
VariableElement element = node.staticElement;
expect(node.staticType, typeProvider.dynamicType);
expect(element.type, typeProvider.dynamicType);
var invocation = _findMethodInvocation(unit, code, 'f()) {} // 1');
expect(invocation.staticType.toString(), 'Iterable<dynamic>');
}
{
var node = EngineTestCase.findSimpleIdentifier(unit, code, 'x in');
VariableElement element = node.staticElement;
expect(node.staticType, typeProvider.numType);
expect(element.type, typeProvider.numType);
}
{
var node = EngineTestCase.findSimpleIdentifier(unit, code, 'y in');
VariableElement element = node.staticElement;
expect(node.staticType, typeProvider.numType);
expect(element.type, typeProvider.numType);
var invocation = _findMethodInvocation(unit, code, 'f()) {} // 3');
expect(invocation.staticType.toString(), 'Iterable<num>');
}
}
test_forIn_identifier() async {
var code = r'''
T f<T>() => null;
class A {}
A aTopLevel;
void set aTopLevelSetter(A value) {}
class C {
A aField;
void set aSetter(A value) {}
void test() {
A aLocal;
for (aLocal in f()) {} // local
for (aField in f()) {} // field
for (aSetter in f()) {} // setter
for (aTopLevel in f()) {} // top variable
for (aTopLevelSetter in f()) {} // top setter
}
}''';
var source = addSource(code);
var analysisResult = await computeAnalysisResult(source);
var unit = analysisResult.unit;
void assertType(String prefix) {
var invocation = _findMethodInvocation(unit, code, prefix);
expect(invocation.staticType.toString(), 'Iterable<A>');
}
assertType('f()) {} // local');
assertType('f()) {} // field');
assertType('f()) {} // setter');
assertType('f()) {} // top variable');
assertType('f()) {} // top setter');
}
test_inferObject_whenDownwardNull() async {
var code = r'''
int f(void Function(Null) f2) {}
@ -43,4 +124,11 @@ void main() {
expect(xNode.staticType, typeProvider.objectType);
expect(xElement.type, typeProvider.objectType);
}
MethodInvocation _findMethodInvocation(
AstNode root, String code, String prefix) {
return EngineTestCase.findNode(root, code, prefix, (n) {
return n is MethodInvocation;
});
}
}

View file

@ -345,6 +345,7 @@ integer_parsed_arith_vm_test: RuntimeError # Issue 29921
integer_to_radix_string_test: RuntimeError # Issue 29921
integer_to_string_test/01: RuntimeError # Issue 29921
iterable_fold_test/02: RuntimeError # different type inference problem
iterable_reduce_test/01: RuntimeError
iterable_reduce_test/none: RuntimeError
iterable_return_type_test/02: RuntimeError # Issue 29921
iterable_to_list_test/*: RuntimeError
@ -655,7 +656,6 @@ double_parse_test/02: Skip # Temporarily disable the following tests until we fi
double_parse_test/03: Skip # Temporarily disable the following tests until we figure out why they started failing.
double_parse_test/04: Skip # Temporarily disable the following tests until we figure out why they started failing.
double_parse_test/none: Skip # Temporarily disable the following tests until we figure out why they started failing.
iterable_reduce_test/01: CompileTimeError
[ $compiler == dartkp || $compiler == precompiler ]
apply3_test: SkipByDesign