mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 21:50:11 +00:00
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:
parent
105b170ddb
commit
bbd835f322
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue