Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Unified Diff: pkg/analyzer/lib/src/task/strong/checker.dart

Issue 1823793002: Better casting of for-in loops. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Handle supertypes of Iterable and Stream. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/generated/error_verifier.dart ('k') | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/lib/src/task/strong/checker.dart
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 53164a3323806b8b8ef5967fc34baf2a1a27210e..79da6a86d9c37d27a0e93d685adf97e9649d2b36 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -327,19 +327,43 @@ class CodeChecker extends RecursiveAstVisitor {
@override
void visitForEachStatement(ForEachStatement node) {
- // Check that the expression is an Iterable.
- var expr = node.iterable;
- var iterableType = node.awaitKeyword != null
- ? typeProvider.streamType
- : typeProvider.iterableType;
var loopVariable = node.identifier != null
? node.identifier
: node.loopVariable?.identifier;
+
+ // Safely handle malformed statements.
if (loopVariable != null) {
- var iteratorType = loopVariable.staticType;
- var checkedType = iterableType.instantiate([iteratorType]);
- checkAssignment(expr, checkedType);
+ // Find the element type of the sequence.
+ var sequenceInterface = node.awaitKeyword != null
+ ? typeProvider.streamType
+ : typeProvider.iterableType;
+ var iterableType = _getStaticType(node.iterable);
+ var elementType =
+ rules.mostSpecificTypeArgument(iterableType, sequenceInterface);
+
+ // If the sequence is not an Iterable (or Stream for await for) but is a
+ // supertype of it, do an implicit downcast to Iterable<dynamic>. Then
+ // we'll do a separate cast of the dynamic element to the variable's type.
+ if (elementType == null) {
+ var sequenceType = sequenceInterface.instantiate([DynamicTypeImpl.instance]);
+
+ if (rules.isSubtypeOf(sequenceType, iterableType)) {
+ _recordMessage(DownCast.create(
+ rules, node.iterable, iterableType, sequenceType));
+ elementType = DynamicTypeImpl.instance;
+ }
+ }
+
+ // If the sequence doesn't implement the interface at all, [ErrorVerifier]
+ // will report the error, so ignore it here.
+ if (elementType != null) {
+ // Insert a cast from the sequence's element type to the loop variable's
+ // if needed.
+ _checkDowncast(loopVariable, _getStaticType(loopVariable),
+ from: elementType);
+ }
}
+
node.visitChildren(this);
}
@@ -595,8 +619,8 @@ class CodeChecker extends RecursiveAstVisitor {
rules.isSubtypeOf(lhsType, rhsType)) {
// This is also slightly different from spec, but allows us to keep
// compound operators in the int += num and num += dynamic cases.
- staticInfo = DownCast.create(
- rules, expr.rightHandSide, rhsType, lhsType);
+ staticInfo =
+ DownCast.create(rules, expr.rightHandSide, rhsType, lhsType);
rhsType = lhsType;
} else {
staticInfo = new StaticTypeError(rules, expr, lhsType);
@@ -663,18 +687,22 @@ class CodeChecker extends RecursiveAstVisitor {
}
}
- /// Records a [DownCast] of [expr] to [toT], if there is one.
+ /// Records a [DownCast] of [expr] from [from] to [to], if there is one.
///
- /// If [expr] does not require a downcast because it is not related to [toT]
+ /// If [from] is omitted, uses the static type of [expr].
+ ///
+ /// If [expr] does not require a downcast because it is not related to [to]
/// or is already a subtype of it, does nothing.
- void _checkDowncast(Expression expr, DartType toT) {
- DartType fromT = _getStaticType(expr);
+ void _checkDowncast(Expression expr, DartType to, {DartType from}) {
+ if (from == null) {
+ from = _getStaticType(expr);
+ }
// We can use anything as void.
- if (toT.isVoid) return;
+ if (to.isVoid) return;
// fromT <: toT, no coercion needed.
- if (rules.isSubtypeOf(fromT, toT)) return;
+ if (rules.isSubtypeOf(from, to)) return;
// TODO(vsm): We can get rid of the second clause if we disallow
// all sideways casts - see TODO below.
@@ -682,14 +710,14 @@ class CodeChecker extends RecursiveAstVisitor {
// Note: a function type is never assignable to a class per the Dart
// spec - even if it has a compatible call method. We disallow as
// well for consistency.
- if ((fromT is FunctionType && rules.getCallMethodType(toT) != null) ||
- (toT is FunctionType && rules.getCallMethodType(fromT) != null)) {
+ if ((from is FunctionType && rules.getCallMethodType(to) != null) ||
+ (to is FunctionType && rules.getCallMethodType(from) != null)) {
return;
}
// Downcast if toT <: fromT
- if (rules.isSubtypeOf(toT, fromT)) {
- _recordMessage(DownCast.create(rules, expr, fromT, toT));
+ if (rules.isSubtypeOf(to, from)) {
+ _recordMessage(DownCast.create(rules, expr, from, to));
return;
}
@@ -703,8 +731,8 @@ class CodeChecker extends RecursiveAstVisitor {
// are likely to succeed. The canonical example is List<dynamic> and
// Iterable<T> for some concrete T (e.g. Object). These are unrelated
// in the restricted system, but List<dynamic> <: Iterable<T> in dart.
- if (fromT.isAssignableTo(toT)) {
- _recordMessage(DownCast.create(rules, expr, fromT, toT));
+ if (from.isAssignableTo(to)) {
+ _recordMessage(DownCast.create(rules, expr, from, to));
}
}
« no previous file with comments | « pkg/analyzer/lib/src/generated/error_verifier.dart ('k') | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698