Version 1.22.0-dev.10.1

Cherry-pick 39977ac6ec58516f5db5ac4df9fa2426fc19dac5 to dev
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index a86bcde..a2aeae1 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -278,16 +278,19 @@
    */
   @override
   Object visitAwaitExpression(AwaitExpression node) {
-    DartType staticExpressionType = _getStaticType(node.expression);
-    if (staticExpressionType == null) {
-      // TODO(brianwilkerson) Determine whether this can still happen.
-      staticExpressionType = _dynamicType;
+    // Await the Future. This results in whatever type is (ultimately) returned.
+    DartType awaitType(DartType awaitedType) {
+      if (awaitedType == null) {
+        return null;
+      }
+      if (awaitedType.isDartAsyncFutureOr) {
+        return awaitType((awaitedType as InterfaceType).typeArguments[0]);
+      }
+      return awaitedType.flattenFutures(_typeSystem);
     }
-    DartType staticType = staticExpressionType.flattenFutures(_typeSystem);
-    _recordStaticType(node, staticType);
-    DartType propagatedExpressionType = node.expression.propagatedType;
-    DartType propagatedType =
-        propagatedExpressionType?.flattenFutures(_typeSystem);
+
+    _recordStaticType(node, awaitType(_getStaticType(node.expression)));
+    DartType propagatedType = awaitType(node.expression.propagatedType);
     _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
     return null;
   }
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index 2b6012a..b951f4f 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -696,13 +696,6 @@
     };
   }
 
-  /// If [t1] or [t2] is a type parameter we are inferring, update its bound.
-  /// Returns `true` if we could possibly find a compatible type,
-  /// otherwise `false`.
-  bool _inferTypeParameterSubtypeOf(DartType t1, DartType t2) {
-    return false;
-  }
-
   /**
    * This currently does not implement a very complete least upper bound
    * algorithm, but handles a couple of the very common cases that are
@@ -840,28 +833,7 @@
     // Trivially false.
     if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
         _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
-      return _inferTypeParameterSubtypeOf(t1, t2);
-    }
-
-    // S <: T where S is a type variable
-    //  T is not dynamic or object (handled above)
-    //  True if T == S
-    //  Or true if bound of S is S' and S' <: T
-    if (t1 is TypeParameterType) {
-      if (t2 is TypeParameterType &&
-          t1.definition == t2.definition &&
-          guardedSubtype(t1.bound, t2.bound, visited)) {
-        return true;
-      }
-      if (_inferTypeParameterSubtypeOf(t1, t2)) {
-        return true;
-      }
-      DartType bound = t1.element.bound;
-      return bound == null ? false : guardedSubtype(bound, t2, visited);
-    }
-
-    if (t2 is TypeParameterType) {
-      return _inferTypeParameterSubtypeOf(t1, t2);
+      return false;
     }
 
     // Handle FutureOr<T> union type.
@@ -887,6 +859,23 @@
       return isSubtypeOf(t1, t2Future) || isSubtypeOf(t1, t2TypeArg);
     }
 
+    // S <: T where S is a type variable
+    //  T is not dynamic or object (handled above)
+    //  True if T == S
+    //  Or true if bound of S is S' and S' <: T
+    if (t1 is TypeParameterType) {
+      if (t2 is TypeParameterType &&
+          t1.definition == t2.definition &&
+          guardedSubtype(t1.bound, t2.bound, visited)) {
+        return true;
+      }
+      DartType bound = t1.element.bound;
+      return bound == null ? false : guardedSubtype(bound, t2, visited);
+    }
+    if (t2 is TypeParameterType) {
+      return false;
+    }
+
     // Void only appears as the return type of a function, and we handle it
     // directly in the function subtype rules. We should not get to a point
     // where we're doing a subtype test on a "bare" void, but just in case we
@@ -1482,6 +1471,7 @@
 
     for (int i = 0; i < fnTypeParams.length; i++) {
       TypeParameterType typeParam = fnTypeParams[i];
+      _TypeParameterBound bound = _bounds[typeParam];
 
       // Apply the `extends` clause for the type parameter, if any.
       //
@@ -1503,7 +1493,9 @@
       DartType declaredUpperBound = typeParam.element.bound;
       if (declaredUpperBound != null) {
         // Assert that the type parameter is a subtype of its bound.
-        _inferTypeParameterSubtypeOf(typeParam,
+        // TODO(jmesserly): the order of calling GLB here matters, because of
+        // https://github.com/dart-lang/sdk/issues/28513
+        bound.upper = _typeSystem.getGreatestLowerBound(bound.upper,
             declaredUpperBound.substitute2(inferredTypes, fnTypeParams));
       }
 
@@ -1519,7 +1511,6 @@
       _TypeParameterVariance variance =
           new _TypeParameterVariance.from(typeParam, declaredReturnType);
 
-      _TypeParameterBound bound = _bounds[typeParam];
       DartType lowerBound = bound.lower;
       DartType upperBound = bound.upper;
 
@@ -1562,8 +1553,19 @@
   }
 
   @override
-  bool _inferTypeParameterSubtypeOf(DartType t1, DartType t2) {
+  bool _isSubtypeOf(DartType t1, DartType t2, Set<Element> visited,
+      {bool dynamicIsBottom: false}) {
+    // TODO(jmesserly): the trivial constraints are not treated as part of
+    // the constraint set here. This seems incorrect once we are able to pin the
+    // inferred type of a type parameter based on the downwards information.
+    if (identical(t1, t2) ||
+        _isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
+        _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
+      return true;
+    }
+
     if (t1 is TypeParameterType) {
+      // TODO(jmesserly): we ignore `dynamicIsBottom` here, is that correct?
       _TypeParameterBound bound = _bounds[t1];
       if (bound != null) {
         // Ensure T1 <: T2, where T1 is a type parameter we are inferring.
@@ -1591,7 +1593,8 @@
         return true;
       }
     }
-    return false;
+    return super
+        ._isSubtypeOf(t1, t2, visited, dynamicIsBottom: dynamicIsBottom);
   }
 }
 
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index d0042cd..a90fe14 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -14,6 +14,7 @@
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/source_io.dart';
+import 'package:front_end/src/base/errors.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -38,6 +39,7 @@
   Asserter<DartType> _isDynamic;
   Asserter<InterfaceType> _isFutureOfDynamic;
   Asserter<InterfaceType> _isFutureOfInt;
+  Asserter<InterfaceType> _isFutureOrOfInt;
   Asserter<DartType> _isInt;
   Asserter<DartType> _isNum;
   Asserter<DartType> _isObject;
@@ -46,6 +48,7 @@
   AsserterBuilder2<Asserter<DartType>, Asserter<DartType>, DartType>
       _isFunction2Of;
   AsserterBuilder<List<Asserter<DartType>>, InterfaceType> _isFutureOf;
+  AsserterBuilder<List<Asserter<DartType>>, InterfaceType> _isFutureOrOf;
   AsserterBuilderBuilder<Asserter<DartType>, List<Asserter<DartType>>, DartType>
       _isInstantiationOf;
   AsserterBuilder<Asserter<DartType>, InterfaceType> _isListOf;
@@ -75,8 +78,11 @@
       _isFunction2Of = _assertions.isFunction2Of;
       _hasElementOf = _assertions.hasElementOf;
       _isFutureOf = _isInstantiationOf(_hasElementOf(typeProvider.futureType));
+      _isFutureOrOf =
+          _isInstantiationOf(_hasElementOf(typeProvider.futureOrType));
       _isFutureOfDynamic = _isFutureOf([_isDynamic]);
       _isFutureOfInt = _isFutureOf([_isInt]);
+      _isFutureOrOfInt = _isFutureOrOf([_isInt]);
       _isStreamOf = _isInstantiationOf(_hasElementOf(typeProvider.streamType));
     }
     return result;
@@ -135,19 +141,10 @@
     // when instantiating type variables.
     // TODO(leafp): I think this should pass once the inference changes
     // that jmesserly is adding are landed.
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     T mk<T extends int>(T x) => null;
     FutureOr<int> test() => mk(new Future.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
     _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
   }
@@ -157,19 +154,10 @@
     // when instantiating type variables.
     // TODO(leafp): I think this should pass once the inference changes
     // that jmesserly is adding are landed.
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     T mk<T extends Future<Object>>(T x) => null;
     FutureOr<int> test() => mk(new Future.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
     _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
   }
@@ -970,57 +958,30 @@
   test_futureOr_downwards1() async {
     // Test that downwards inference interacts correctly with FutureOr
     // parameters.
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     Future<T> mk<T>(FutureOr<T> x) => null;
     Future<int> test() => mk(new Future<int>.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
   }
 
   test_futureOr_downwards2() async {
     // Test that downwards inference interacts correctly with FutureOr
     // parameters when the downwards context is FutureOr
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     Future<T> mk<T>(FutureOr<T> x) => null;
     FutureOr<int> test() => mk(new Future<int>.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
   }
 
   test_futureOr_downwards3() async {
     // Test that downwards inference correctly propogates into
     // arguments.
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     Future<T> mk<T>(FutureOr<T> x) => null;
     Future<int> test() => mk(new Future.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
     _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
   }
@@ -1028,19 +989,10 @@
   test_futureOr_downwards4() async {
     // Test that downwards inference interacts correctly with FutureOr
     // parameters when the downwards context is FutureOr
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     Future<T> mk<T>(FutureOr<T> x) => null;
     FutureOr<int> test() => mk(new Future.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
     _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
   }
@@ -1048,19 +1000,10 @@
   test_futureOr_downwards5() async {
     // Test that downwards inference correctly pins the type when it
     // comes from a FutureOr
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     Future<T> mk<T>(FutureOr<T> x) => null;
     FutureOr<num> test() => mk(new Future.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOf([_isNum])(invoke.staticType);
     _isFutureOf([_isNum])(invoke.argumentList.arguments[0].staticType);
   }
@@ -1068,19 +1011,10 @@
   test_futureOr_downwards6() async {
     // Test that downwards inference doesn't decompose FutureOr
     // when instantiating type variables.
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     T mk<T>(T x) => null;
     FutureOr<int> test() => mk(new Future.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
     _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
   }
@@ -1088,129 +1022,119 @@
   test_futureOr_downwards9() async {
     // Test that downwards inference decomposes correctly with
     // other composite types
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     List<T> mk<T>(T x) => null;
     FutureOr<List<int>> test() => mk(3);
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isListOf(_isInt)(invoke.staticType);
     _isInt(invoke.argumentList.arguments[0].staticType);
   }
 
   test_futureOr_methods1() async {
     // Test that FutureOr has the Object methods
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     dynamic test(FutureOr<int> x) => x.toString();
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isString(invoke.staticType);
   }
 
   test_futureOr_methods2() async {
     // Test that FutureOr does not have the constituent type methods
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(
+        r'''
     dynamic test(FutureOr<int> x) => x.abs();
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_METHOD]);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''',
+        errors: [StaticTypeWarningCode.UNDEFINED_METHOD]);
     _isDynamic(invoke.staticType);
   }
 
   test_futureOr_methods3() async {
     // Test that FutureOr does not have the Future type methods
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(
+        r'''
     dynamic test(FutureOr<int> x) => x.then((x) => x);
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_METHOD]);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''',
+        errors: [StaticTypeWarningCode.UNDEFINED_METHOD]);
     _isDynamic(invoke.staticType);
   }
 
   test_futureOr_methods4() async {
     // Test that FutureOr<dynamic> does not have all methods
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(
+        r'''
     dynamic test(FutureOr<dynamic> x) => x.abs();
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_METHOD]);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''',
+        errors: [StaticTypeWarningCode.UNDEFINED_METHOD]);
     _isDynamic(invoke.staticType);
   }
 
   test_futureOr_upwards1() async {
     // Test that upwards inference correctly prefers to instantiate type
     // variables with the "smaller" solution when both are possible.
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(r'''
     Future<T> mk<T>(FutureOr<T> x) => null;
     dynamic test() => mk(new Future<int>.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertNoErrors(source);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''');
     _isFutureOfInt(invoke.staticType);
   }
 
   test_futureOr_upwards2() async {
     // Test that upwards inference fails when the solution doesn't
     // match the bound.
-    String code = r'''
-    import "dart:async";
+    MethodInvocation invoke = await _testFutureOr(
+        r'''
     Future<T> mk<T extends Future<Object>>(FutureOr<T> x) => null;
     dynamic test() => mk(new Future<int>.value(42));
-   ''';
-    Source source = addSource(code);
-    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
-    assertErrors(source, [StrongModeCode.COULD_NOT_INFER]);
-    verify([source]);
-    CompilationUnit unit = analysisResult.unit;
-    FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
-    ExpressionFunctionBody body = test.functionExpression.body;
-    MethodInvocation invoke = body.expression;
+    ''',
+        errors: [StrongModeCode.COULD_NOT_INFER]);
     _isFutureOf([_isObject])(invoke.staticType);
   }
 
+  test_futureOr_assignFromValue() async {
+    // Test a T can be assigned to FutureOr<T>.
+    MethodInvocation invoke = await _testFutureOr(r'''
+    FutureOr<T> mk<T>(T x) => x;
+    test() => mk(42);
+    ''');
+    _isFutureOrOfInt(invoke.staticType);
+  }
+
+  test_futureOr_assignFromFuture() async {
+    // Test a Future<T> can be assigned to FutureOr<T>.
+    MethodInvocation invoke = await _testFutureOr(r'''
+    FutureOr<T> mk<T>(Future<T> x) => x;
+    test() => mk(new Future<int>.value(42));
+    ''');
+    _isFutureOrOfInt(invoke.staticType);
+  }
+
+  test_futureOr_await() async {
+    // Test a FutureOr<T> can be awaited.
+    MethodInvocation invoke = await _testFutureOr(r'''
+    Future<T> mk<T>(FutureOr<T> x) async => await x;
+    test() => mk(42);
+    ''');
+    _isFutureOfInt(invoke.staticType);
+  }
+
+  test_futureOr_asyncExpressionBody() async {
+    // A FutureOr<T> can be used as the expression body for an async function
+    MethodInvocation invoke = await _testFutureOr(r'''
+    Future<T> mk<T>(FutureOr<T> x) async => x;
+    test() => mk(42);
+    ''');
+    _isFutureOfInt(invoke.staticType);
+  }
+
+  test_futureOr_asyncReturn() async {
+    // A FutureOr<T> can be used as the return value for an async function
+    MethodInvocation invoke = await _testFutureOr(r'''
+    Future<T> mk<T>(FutureOr<T> x) async { return x; }
+    test() => mk(42);
+    ''');
+    _isFutureOfInt(invoke.staticType);
+  }
+
   test_inference_hints() async {
     Source source = addSource(r'''
       void main () {
@@ -2073,6 +1997,30 @@
     check("f2", _isListOf(_isInt));
     check("f3", _isListOf((DartType type) => _isListOf(_isInt)(type)));
   }
+
+  /// Helper method for testing `FutureOr<T>`.
+  ///
+  /// Validates that [code] produces [errors]. It should define a function
+  /// "test", whose body is an expression that invokes a method. Returns that
+  /// invocation.
+  Future<MethodInvocation> _testFutureOr(String code,
+      {List<ErrorCode> errors}) async {
+    Source source = addSource("""
+    import "dart:async";
+    $code""");
+    TestAnalysisResult analysisResult = await computeAnalysisResult(source);
+
+    if (errors == null) {
+      assertNoErrors(source);
+    } else {
+      assertErrors(source, errors);
+    }
+    verify([source]);
+    FunctionDeclaration test =
+        AstFinder.getTopLevelFunction(analysisResult.unit, "test");
+    ExpressionFunctionBody body = test.functionExpression.body;
+    return body.expression;
+  }
 }
 
 /**
diff --git a/tools/VERSION b/tools/VERSION
index 977d95b..4e9ac1f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 22
 PATCH 0
 PRERELEASE 10
-PRERELEASE_PATCH 0
+PRERELEASE_PATCH 1