Version 2.14.0-236.0.dev

Merge commit 'b687e7e7570c7e27fdd83d5a844c1ca8f6447688' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
new file mode 100644
index 0000000..b8c9a8a
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -0,0 +1,247 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// 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/dart/element/type.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/resolver/resolution_result.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+
+/// A resolver for [FunctionReference] nodes.
+///
+/// This resolver is responsible for writing a given [FunctionReference] as a
+/// [ConstructorReference] or as a [TypeLiteral], depending on how a function
+/// reference's `function` resolves.
+class FunctionReferenceResolver {
+  /// The resolver driving this participant.
+  final ResolverVisitor _resolver;
+
+  FunctionReferenceResolver(this._resolver);
+
+  ErrorReporter get _errorReporter => _resolver.errorReporter;
+
+  void resolve(FunctionReferenceImpl node) {
+    var function = node.function;
+    node.typeArguments?.accept(_resolver);
+
+    if (function is SimpleIdentifierImpl) {
+      var element = _resolver.nameScope.lookup(function.name).getter;
+      if (element is ExecutableElement) {
+        function.staticElement = element;
+        _resolve(node: node, rawType: element.type);
+        return;
+      } else if (element is VariableElement) {
+        var functionType = element.type;
+        if (functionType is FunctionType) {
+          function.accept(_resolver);
+          _resolve(node: node, rawType: functionType);
+          return;
+        }
+      }
+    }
+
+    // TODO(srawlins): Handle `function` being a [SuperExpression].
+
+    if (function is PrefixedIdentifierImpl) {
+      var prefixElement =
+          _resolver.nameScope.lookup(function.prefix.name).getter;
+      if (prefixElement is PrefixElement) {
+        var nameNode = function.identifier;
+        var name = nameNode.name;
+        _resolveReceiverPrefix(node, prefixElement, nameNode, name);
+        return;
+      }
+
+      if (prefixElement is ClassElement) {
+        // TODO(srawlins): Rewrite `node` as a [TypeLiteral], then resolve the
+        // [TypeLiteral] instead of `function`.
+        function.accept(_resolver);
+        node.staticType = DynamicTypeImpl.instance;
+        return;
+      } else if (prefixElement is TypeAliasElement) {
+        var aliasedType = prefixElement.aliasedType;
+        if (aliasedType is InterfaceType) {
+          // TODO(srawlins): Rewrite `node` as a [TypeLiteral], then resolve
+          // the [TypeLiteral] instead of `function`.
+          function.accept(_resolver);
+          node.staticType = DynamicTypeImpl.instance;
+          return;
+        }
+      } else if (prefixElement is ExtensionElement) {
+        // TODO(srawlins): Rewrite `node` as a [TypeLiteral], then resolve the
+        // [TypeLiteral] instead of `function`.
+        function.accept(_resolver);
+        node.staticType = DynamicTypeImpl.instance;
+        return;
+      }
+
+      ResolutionResult resolveTypeProperty(DartType prefixType) {
+        return _resolver.typePropertyResolver.resolve(
+          receiver: function.prefix,
+          receiverType: prefixType,
+          name: function.identifier.name,
+          propertyErrorEntity: function.identifier,
+          nameErrorEntity: function,
+        );
+      }
+
+      function.prefix.staticElement = prefixElement;
+      ExecutableElement? methodElement;
+      if (prefixElement is VariableElement) {
+        var prefixType = prefixElement.type;
+        function.prefix.staticType = prefixType;
+        methodElement = resolveTypeProperty(prefixType).getter;
+      } else if (prefixElement is PropertyAccessorElement) {
+        var prefixType = prefixElement.returnType;
+        function.prefix.staticType = prefixType;
+        methodElement = resolveTypeProperty(prefixType).getter;
+      }
+      if (methodElement is MethodElement) {
+        _resolveFunctionReferenceMethod(node: node, function: function);
+        return;
+      }
+
+      // TODO(srawlins): Check for [ConstructorElement] and rewrite to
+      // [ConstructorReference] before resolving.
+      function.accept(_resolver);
+      node.staticType = DynamicTypeImpl.instance;
+      return;
+    }
+
+    if (function is PropertyAccess) {
+      function.accept(_resolver);
+      DartType functionType = function.typeOrThrow;
+
+      if (functionType is FunctionType) {
+        _resolve(node: node, rawType: function.staticType as FunctionType);
+        return;
+      }
+
+      // TODO(srawlins): Handle type variables bound to function type, like
+      // `T extends void Function<U>(U)`.
+    }
+
+    // TODO(srawlins): Enumerate and handle all cases that fall through to
+    // here; ultimately it should just be a case of "unknown identifier."
+    function.accept(_resolver);
+    node.staticType = DynamicTypeImpl.instance;
+  }
+
+  /// Resolves [node]'s static type, as an instantiated function type, and type
+  /// argument types, using [rawType] as the uninstantiated function type.
+  void _resolve({
+    required FunctionReferenceImpl node,
+    required FunctionType rawType,
+  }) {
+    // `node.typeArguments`, coming from the parser, is never null.
+    var typeArgumentList = node.typeArguments!;
+    var typeParameters = rawType.typeFormals;
+
+    List<DartType> typeArguments;
+    if (typeArgumentList.arguments.length != typeParameters.length) {
+      _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
+        typeArgumentList,
+        [
+          rawType,
+          typeParameters.length,
+          typeArgumentList.arguments.length,
+        ],
+      );
+      typeArguments = List.filled(
+        typeParameters.length,
+        DynamicTypeImpl.instance,
+      );
+    } else {
+      typeArguments = typeArgumentList.arguments
+          .map((typeArgument) => typeArgument.typeOrThrow)
+          .toList();
+    }
+
+    var invokeType = rawType.instantiate(typeArguments);
+    node.typeArgumentTypes = typeArguments;
+    node.staticType = invokeType;
+
+    // TODO(srawlins): Verify that type arguments conform to bounds. This will
+    // probably be done later, not in this resolution phase.
+  }
+
+  void _resolveFunctionReferenceMethod({
+    required FunctionReferenceImpl node,
+    required PrefixedIdentifier function,
+  }) {
+    function.accept(_resolver);
+    var receiver = function.prefix;
+    var receiverType = receiver.staticType;
+    if (receiverType == null) {
+      // TODO(srawlins): Handle this situation; see
+      //  `test_staticMethod_explicitReceiver` test case.
+      node.staticType = DynamicTypeImpl.instance;
+      return;
+    }
+    var nameNode = function.identifier;
+    var name = nameNode.name;
+    var result = _resolver.typePropertyResolver.resolve(
+      receiver: receiver,
+      receiverType: receiverType,
+      name: name,
+      propertyErrorEntity: nameNode,
+      nameErrorEntity: nameNode,
+    );
+
+    var target = result.getter;
+    if (target != null) {
+      // TODO(srawlins): Set static type on `nameNode`?
+
+      _resolve(
+        node: node,
+        rawType: function.staticType as FunctionType,
+      );
+      return;
+    }
+
+    // TODO(srawlins): Report unresolved identifier.
+    node.function.accept(_resolver);
+    node.staticType = DynamicTypeImpl.instance;
+  }
+
+  void _resolveReceiverPrefix(
+    FunctionReferenceImpl node,
+    PrefixElement prefix,
+    SimpleIdentifierImpl nameNode,
+    String name,
+  ) {
+    // TODO(srawlins): Handle `loadLibrary`, as in `p.loadLibrary<int>;`.
+
+    var element = prefix.scope.lookup(name).getter;
+    element = _resolver.toLegacyElement(element);
+    nameNode.staticElement = element;
+
+    if (element is MultiplyDefinedElement) {
+      MultiplyDefinedElement multiply = element;
+      element = multiply.conflictingElements[0];
+
+      // TODO(srawlins): Add a resolution test for this case.
+    }
+
+    if (element is ExecutableElement) {
+      var function = node.function;
+      function.accept(_resolver);
+      return _resolve(
+          node: node, rawType: function.typeOrThrow as FunctionType);
+    }
+
+    // TODO(srawlins): Handle prefixed constructor references and type literals.
+
+    // TODO(srawlins): Report undefined prefixed identifier.
+
+    node.function.accept(_resolver);
+    node.staticType = DynamicTypeImpl.instance;
+  }
+}
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 88b7c38..b6480b1 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -37,6 +37,7 @@
 import 'package:analyzer/src/dart/resolver/for_resolver.dart';
 import 'package:analyzer/src/dart/resolver/function_expression_invocation_resolver.dart';
 import 'package:analyzer/src/dart/resolver/function_expression_resolver.dart';
+import 'package:analyzer/src/dart/resolver/function_reference_resolver.dart';
 import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
 import 'package:analyzer/src/dart/resolver/lexical_lookup.dart';
 import 'package:analyzer/src/dart/resolver/method_invocation_resolver.dart';
@@ -263,6 +264,8 @@
   /// TODO(scheglov) Stop cloning altogether.
   bool shouldCloneAnnotations = true;
 
+  late final FunctionReferenceResolver _functionReferenceResolver;
+
   /// Initialize a newly created visitor to resolve the nodes in an AST node.
   ///
   /// The [definingLibrary] is the element for the library containing the node
@@ -381,6 +384,7 @@
         migratableAstInfoProvider: _migratableAstInfoProvider);
     inferenceContext = InferenceContext._(this);
     typeAnalyzer = StaticTypeAnalyzer(this, migrationResolutionHooks);
+    _functionReferenceResolver = FunctionReferenceResolver(this);
   }
 
   /// Return the element representing the function containing the current node,
@@ -1538,6 +1542,11 @@
   }
 
   @override
+  void visitFunctionReference(FunctionReference node) {
+    _functionReferenceResolver.resolve(node as FunctionReferenceImpl);
+  }
+
+  @override
   void visitFunctionTypeAlias(FunctionTypeAlias node) {
     // Resolve the metadata in the library scope.
     node.metadata.accept(this);
diff --git a/pkg/analyzer/lib/src/test_utilities/find_node.dart b/pkg/analyzer/lib/src/test_utilities/find_node.dart
index 11c7031e..0443c30 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_node.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_node.dart
@@ -152,6 +152,10 @@
     return _node(search, (n) => n is FunctionExpressionInvocation);
   }
 
+  FunctionReference functionReference(String search) {
+    return _node(search, (n) => n is FunctionReference);
+  }
+
   FunctionTypeAlias functionTypeAlias(String search) {
     return _node(search, (n) => n is FunctionTypeAlias);
   }
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index c6d8019..b545267 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -254,6 +254,7 @@
         EnableString.generic_metadata,
         EnableString.nonfunction_type_aliases,
         EnableString.triple_shift,
+        EnableString.constructor_tearoffs,
       ];
 
   /// The path that is not in [workspaceRootPath], contains external packages.
@@ -417,7 +418,7 @@
   // TODO(https://github.com/dart-lang/sdk/issues/44666): This mixin is a no-op
   // on PubPackageResolutionTest; remove its usage and remove it.
   @override
-  String? get testPackageLanguageVersion => '2.12';
+  String? get testPackageLanguageVersion => '2.14';
 
   @override
   bool get typeToStringWithNullability => true;
diff --git a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
new file mode 100644
index 0000000..4dba4a4
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
@@ -0,0 +1,306 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// 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/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(FunctionReferenceResolutionTest);
+  });
+}
+
+@reflectiveTest
+class FunctionReferenceResolutionTest extends PubPackageResolutionTest {
+  test_instanceMethod() async {
+    await assertNoErrorsInCode('''
+class A {
+  void foo<T>(T a) {}
+
+  bar() {
+    foo<int>;
+  }
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_instanceMethod_explicitReceiver_field() async {
+    await assertNoErrorsInCode('''
+class A {
+  void foo<T>(T a) {}
+}
+
+class B {
+  A a;
+  B(this.a);
+  bar() {
+    a.foo<int>;
+  }
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_instanceMethod_explicitReceiver_super() async {
+    await assertNoErrorsInCode('''
+class A {
+  void foo<T>(T a) {}
+}
+class B extends A {
+  bar() {
+    super.foo<int>;
+  }
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_instanceMethod_explicitReceiver_this() async {
+    await assertNoErrorsInCode('''
+class A {
+  void foo<T>(T a) {}
+
+  bar() {
+    this.foo<int>;
+  }
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_instanceMethod_explicitReceiver_variable() async {
+    await assertNoErrorsInCode('''
+class A {
+  void foo<T>(T a) {}
+}
+
+class B {
+  bar(A a) {
+    a.foo<int>;
+  }
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_localFunction() async {
+    await assertNoErrorsInCode('''
+void bar() {
+  void foo<T>(T a) {}
+
+  foo<int>;
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.localFunction('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_localVariable() async {
+    await assertNoErrorsInCode('''
+void bar(void Function<T>(T a) foo) {
+  foo<int>;
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.parameter('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_nonGenericFunction() async {
+    await assertErrorsInCode('''
+class A {
+  void foo() {}
+
+  bar() {
+    foo<int>;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 44, 5),
+    ]);
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function()');
+  }
+
+  test_staticMethod() async {
+    await assertNoErrorsInCode('''
+class A {
+  static void foo<T>(T a) {}
+
+  bar() {
+    foo<int>;
+  }
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  @FailingTest(reason: 'Unresolved TODO in FunctionReferenceResolver')
+  test_staticMethod_explicitReceiver() async {
+    await assertNoErrorsInCode('''
+class A {
+  static void foo<T>(T a) {}
+
+  bar() {
+    A.foo<int>;
+  }
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  @FailingTest(reason: 'Unresolved TODO in FunctionReferenceResolver')
+  test_staticMethod_explicitReceiver_importPrefix() async {
+    newFile('$testPackageLibPath/a.dart', content: '''
+class A {
+  static void foo<T>(T a) {}
+}
+''');
+    await assertNoErrorsInCode('''
+import 'a.dart' as a;
+
+bar() {
+  a.A.foo<int>;
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_tooFewTypeArguments() async {
+    await assertErrorsInCode('''
+class A {
+  void foo<T, U>(T a, U b) {}
+
+  bar() {
+    foo<int>;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 58, 5),
+    ]);
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(dynamic, dynamic)');
+  }
+
+  test_tooManyTypeArguments() async {
+    await assertErrorsInCode('''
+class A {
+  void foo<T>(T a) {}
+
+  bar() {
+    foo<int, int>;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 50, 10),
+    ]);
+
+    var identifier = findNode.functionReference('foo<int, int>;');
+    assertElement(identifier, findElement.method('foo'));
+    assertType(identifier, 'void Function(dynamic)');
+  }
+
+  test_topLevelFunction() async {
+    await assertNoErrorsInCode('''
+void foo<T>(T a) {}
+
+void bar() {
+  foo<int>;
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier, findElement.topFunction('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_topLevelFunction_importPrefix() async {
+    newFile('$testPackageLibPath/a.dart', content: '''
+void foo<T>(T arg) {}
+''');
+    await assertNoErrorsInCode('''
+import 'a.dart' as a;
+
+void bar() {
+  a.foo<int>;
+}
+''');
+
+    var identifier = findNode.functionReference('foo<int>;');
+    assertElement(identifier,
+        findElement.importFind('package:test/a.dart').topFunction('foo'));
+    assertType(identifier, 'void Function(int)');
+  }
+
+  test_unknownIdentifier() async {
+    await assertErrorsInCode('''
+void bar() {
+  foo<int>;
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 15, 3),
+    ]);
+  }
+
+  test_unknownIdentifier_explicitReceiver() async {
+    await assertErrorsInCode('''
+class A {}
+
+class B {
+  bar(A a) {
+    a.foo<int>;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_GETTER, 41, 3),
+    ]);
+  }
+
+  test_unknownIdentifier_importPrefix() async {
+    newFile('$testPackageLibPath/a.dart', content: '');
+    await assertErrorsInCode('''
+import 'a.dart' as a;
+
+void bar() {
+  a.foo<int>;
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME, 40, 3),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index ca2b79e..2d2415b 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -875,6 +875,15 @@
       return node.declaredElement;
     } else if (node is FunctionExpressionInvocation) {
       return node.staticElement;
+    } else if (node is FunctionReference) {
+      var function = node.function;
+      if (function is Identifier) {
+        return function.staticElement;
+      } else if (function is PropertyAccess) {
+        return function.propertyName.staticElement;
+      } else {
+        fail('Unsupported node: (${node.runtimeType}) $node');
+      }
     } else if (node is Identifier) {
       return node.staticElement;
     } else if (node is IndexExpression) {
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index 7c87fe5..5b1355f 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -23,6 +23,7 @@
 import 'function_declaration_test.dart' as function_declaration;
 import 'function_expression_invocation_test.dart'
     as function_expression_invocation;
+import 'function_reference_test.dart' as function_reference;
 import 'function_type_alias_test.dart' as function_type_alias;
 import 'generic_function_type_test.dart' as generic_function_type;
 import 'generic_type_alias_test.dart' as generic_type_alias;
@@ -82,6 +83,7 @@
     for_in.main();
     function_declaration.main();
     function_expression_invocation.main();
+    function_reference.main();
     function_type_alias.main();
     generic_function_type.main();
     generic_type_alias.main();
diff --git a/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart b/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart
index c0ecd09..a305fc4 100644
--- a/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart
@@ -17,6 +17,7 @@
 class ExperimentNotEnabledTest extends PubPackageResolutionTest {
   test_constructor_tearoffs_disabled_grammar() async {
     await assertErrorsInCode('''
+// @dart = 2.12
 class Foo<X> {
   const Foo.bar();
   int get baz => 0;
@@ -25,7 +26,7 @@
   Foo<int>.bar.baz();
 }
 ''', [
-      error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 70, 5),
+      error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 86, 5),
     ]);
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart b/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart
index 6754881..b610200 100644
--- a/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart
@@ -93,6 +93,26 @@
     ]);
   }
 
+  test_functionReference_tooFew() async {
+    await assertErrorsInCode('''
+f(void Function<T, U>() foo) {
+  foo<int>;
+}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 36, 5),
+    ]);
+  }
+
+  test_functionReference_tooMany() async {
+    await assertErrorsInCode('''
+f(void Function<T>() foo) {
+  foo<int, int>;
+}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 33, 10),
+    ]);
+  }
+
   test_metadata_1of0() async {
     await assertErrorsInCode(r'''
 class A {
diff --git a/tools/VERSION b/tools/VERSION
index 7671cdf..d15e618 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 235
+PRERELEASE 236
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/pub_integration_test.py b/tools/bots/pub_integration_test.py
index cef43cc..371ed1a 100755
--- a/tools/bots/pub_integration_test.py
+++ b/tools/bots/pub_integration_test.py
@@ -23,15 +23,21 @@
     parser = optparse.OptionParser()
     parser.add_option(
         '--mode', action='store', dest='mode', type='string', default='release')
+    parser.add_option('--arch',
+                      action='store',
+                      dest='arch',
+                      type='string',
+                      default='x64')
 
     (options, args) = parser.parse_args()
 
-    out_dir_subfolder = 'DebugX64' if options.mode == 'debug' else 'ReleaseX64'
+    arch = 'XARM64' if options.arch == 'arm64' else 'X64'
+    mode = ('Debug' if options.mode == 'debug' else 'Release')
 
     out_dir = 'xcodebuild' if sys.platform == 'darwin' else 'out'
     extension = '' if not sys.platform == 'win32' else '.bat'
-    pub = os.path.abspath(
-        '%s/%s/dart-sdk/bin/pub%s' % (out_dir, out_dir_subfolder, extension))
+    pub = os.path.abspath('%s/%s%s/dart-sdk/bin/pub%s' %
+                          (out_dir, mode, arch, extension))
     print(pub)
 
     working_dir = tempfile.mkdtemp()
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index b3e5e99..29fe236 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -3503,7 +3503,8 @@
           "name": "pub integration tests",
           "script": "tools/bots/pub_integration_test.py",
           "arguments": [
-            "--mode=release"
+            "--mode=release",
+            "--arch=arm64"
           ]
         }
       ]