| 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 91134c2f558bf9a5bd17219eb60ee654b390a389..8d3c9a275ee0b59f6333d51a4969ef8b8e1e700d 100644
|
| --- a/pkg/analyzer/lib/src/task/strong/checker.dart
|
| +++ b/pkg/analyzer/lib/src/task/strong/checker.dart
|
| @@ -742,7 +742,7 @@ class CodeChecker extends RecursiveAstVisitor {
|
| {DartType from, bool opAssign: false}) {
|
| from ??= _getDefiniteType(expr);
|
|
|
| - if (_needsImplicitCast(expr, to, from: from)) {
|
| + if (_needsImplicitCast(expr, to, from: from) == true) {
|
| _recordImplicitCast(expr, to, from: from, opAssign: opAssign);
|
| }
|
| }
|
| @@ -841,9 +841,11 @@ class CodeChecker extends RecursiveAstVisitor {
|
| // Stream<T> -> T
|
| expectedType = typeProvider.streamType;
|
| } else {
|
| - // Don't validate return type of async methods.
|
| - // They're handled by the runtime implementation.
|
| - return null;
|
| + // Future<T> -> FutureOr<T>
|
| + var typeArg = (type.element == typeProvider.futureType.element)
|
| + ? type.typeArguments[0]
|
| + : typeProvider.dynamicType;
|
| + return typeProvider.futureOrType.instantiate([typeArg]);
|
| }
|
| } else {
|
| if (body.isGenerator) {
|
| @@ -907,9 +909,10 @@ class CodeChecker extends RecursiveAstVisitor {
|
| }
|
|
|
| /// Returns true if we need an implicit cast of [expr] from [from] type to
|
| - /// [to] type, otherwise returns false.
|
| + /// [to] type, returns false if no cast is needed, and returns null if the
|
| + /// types are statically incompatible.
|
| ///
|
| - /// If [from] is omitted, uses the static type of [expr].
|
| + /// If [from] is omitted, uses the static type of [expr]
|
| bool _needsImplicitCast(Expression expr, DartType to, {DartType from}) {
|
| from ??= _getDefiniteType(expr);
|
|
|
| @@ -921,22 +924,22 @@ class CodeChecker extends RecursiveAstVisitor {
|
| // fromT <: toT, no coercion needed.
|
| if (rules.isSubtypeOf(from, to)) return false;
|
|
|
| - // 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 (from is FunctionType && rules.getCallMethodType(to) != null) {
|
| - return false;
|
| - }
|
| + // Down cast or legal sideways cast, coercion needed.
|
| + if (rules.isAssignableTo(from, to)) return true;
|
|
|
| - // Downcast if toT <: fromT
|
| - if (rules.isSubtypeOf(to, from)) {
|
| - return true;
|
| + // Special case for FutureOr to handle returned values from async functions.
|
| + // In this case, we're more permissive than assignability.
|
| + if (to.element == typeProvider.futureOrType.element) {
|
| + var to1 = to.typeArguments[0];
|
| + var to2 = typeProvider.futureType.instantiate([to1]);
|
| + return _needsImplicitCast(expr, to1, from: from) == true ||
|
| + _needsImplicitCast(expr, to2, from: from) == true;
|
| }
|
|
|
| // Anything else is an illegal sideways cast.
|
| // However, these will have been reported already in error_verifier, so we
|
| // don't need to report them again.
|
| - return false;
|
| + return null;
|
| }
|
|
|
| void _recordDynamicInvoke(AstNode node, Expression target) {
|
| @@ -953,8 +956,6 @@ class CodeChecker extends RecursiveAstVisitor {
|
| /// the AST node.
|
| void _recordImplicitCast(Expression expr, DartType to,
|
| {DartType from, bool opAssign: false}) {
|
| - assert(rules.isSubtypeOf(to, from));
|
| -
|
| // Inference "casts":
|
| if (expr is Literal) {
|
| // fromT should be an exact type - this will almost certainly fail at
|
|
|