Set ConstructorReference TypeName.type to null; re-arrange bounds checks

A follow up from https://dart-review.googlesource.com/c/sdk/+/211304

Change-Id: Ia68a232befc6371739551eb40ad49a9ef15ee5c0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211640
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
index e2a2b97..75ac605 100644
--- a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
@@ -53,7 +53,7 @@
       if (enclosingElement is TypeAliasElement) {
         enclosingElement = enclosingElement.aliasedType.element;
       }
-      // TODO(srawlins): Handle `enclosingElement` being a functio typedef:
+      // TODO(srawlins): Handle `enclosingElement` being a function typedef:
       // typedef F<T> = void Function(); var a = F<int>.extensionOnType;`.
       // This is illegal.
       if (enclosingElement is ClassElement) {
@@ -123,16 +123,7 @@
         constructorName.staticElement = constructorElement.declaration;
         constructorName.name?.staticElement = constructorElement.declaration;
         node.staticType = inferred;
-        // TODO(srawlins): Always set the TypeName's type to `null`, here, and
-        // in the "else" case below, at the very end of [_inferArgumentTypes].
-        // This requires refactoring how type arguments are checked against
-        // bounds, as this is currently always done with the [TypeName], in
-        // type_argument_verifier.dart.
-        if (inferred.typeFormals.isNotEmpty) {
-          constructorName.type.type = null;
-        } else {
-          constructorName.type.type = inferredReturnType;
-        }
+        constructorName.type.type = null;
       }
     } else {
       var constructorElement = constructorName.staticElement;
@@ -141,6 +132,7 @@
       } else {
         node.staticType = constructorElement.type;
       }
+      constructorName.type.type = null;
     }
   }
 }
diff --git a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
index 1bdea4c..71ab7b9 100644
--- a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
+++ b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
@@ -32,8 +32,67 @@
   TypeSystemImpl get _typeSystem =>
       _libraryElement.typeSystem as TypeSystemImpl;
 
+  void checkConstructorReference(ConstructorReference node) {
+    var classElement = node.constructorName.type.name.staticElement;
+    List<TypeParameterElement> typeParameters;
+    if (classElement is TypeAliasElement) {
+      typeParameters = classElement.typeParameters;
+    } else if (classElement is ClassElement) {
+      typeParameters = classElement.typeParameters;
+    } else {
+      return;
+    }
+
+    if (typeParameters.isEmpty) {
+      return;
+    }
+    var typeArgumentList = node.constructorName.type.typeArguments;
+    if (typeArgumentList == null) {
+      return;
+    }
+    var constructorType = node.staticType;
+    if (constructorType is DynamicType) {
+      // An erroneous constructor reference.
+      return;
+    }
+    if (constructorType is! FunctionType) {
+      return;
+    }
+    var typeArguments = [
+      for (var type in typeArgumentList.arguments) type.type!,
+    ];
+    if (typeArguments.length != typeParameters.length) {
+      // Wrong number of type arguments to be reported elsewhere.
+      return;
+    }
+    var typeArgumentListLength = typeArgumentList.arguments.length;
+    var substitution = Substitution.fromPairs(typeParameters, typeArguments);
+    for (var i = 0; i < typeArguments.length; i++) {
+      var typeParameter = typeParameters[i];
+      var typeArgument = typeArguments[i];
+
+      var bound = typeParameter.bound;
+      if (bound == null) {
+        continue;
+      }
+
+      bound = _libraryElement.toLegacyTypeIfOptOut(bound);
+      bound = substitution.substituteType(bound);
+
+      if (!_typeSystem.isSubtypeOf(typeArgument, bound)) {
+        var errorNode =
+            i < typeArgumentListLength ? typeArgumentList.arguments[i] : node;
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
+          errorNode,
+          [typeArgument, typeParameter.name, bound],
+        );
+      }
+    }
+  }
+
   void checkFunctionExpressionInvocation(FunctionExpressionInvocation node) {
-    _checkTypeArguments(
+    _checkInvocationTypeArguments(
       node.typeArguments?.arguments,
       node.function.staticType,
       node.staticInvokeType,
@@ -42,7 +101,7 @@
   }
 
   void checkFunctionReference(FunctionReference node) {
-    _checkTypeArguments(
+    _checkInvocationTypeArguments(
       node.typeArguments?.arguments,
       node.function.staticType,
       node.staticType,
@@ -80,7 +139,7 @@
   }
 
   void checkMethodInvocation(MethodInvocation node) {
-    _checkTypeArguments(
+    _checkInvocationTypeArguments(
       node.typeArguments?.arguments,
       node.function.staticType,
       node.staticInvokeType,
@@ -313,40 +372,9 @@
     }
   }
 
-  /// Checks to ensure that the given list of type [arguments] does not have a
-  /// type parameter as a type argument. The [errorCode] is either
-  /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST] or
-  /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP].
-  void _checkTypeArgumentConst(
-      NodeList<TypeAnnotation> arguments, ErrorCode errorCode) {
-    for (TypeAnnotation type in arguments) {
-      if (type is TypeName && type.type is TypeParameterType) {
-        _errorReporter.reportErrorForNode(errorCode, type, [type.name]);
-      }
-    }
-  }
-
-  /// Verify that the given list of [typeArguments] contains exactly the
-  /// [expectedCount] of elements, reporting an error with the [errorCode]
-  /// if not.
-  void _checkTypeArgumentCount(
-    TypeArgumentList typeArguments,
-    int expectedCount,
-    ErrorCode errorCode,
-  ) {
-    int actualCount = typeArguments.arguments.length;
-    if (actualCount != expectedCount) {
-      _errorReporter.reportErrorForNode(
-        errorCode,
-        typeArguments,
-        [actualCount],
-      );
-    }
-  }
-
   /// Verify that each type argument in [typeArgumentList] is within its bounds,
   /// as defined by [genericType].
-  void _checkTypeArguments(
+  void _checkInvocationTypeArguments(
     List<TypeAnnotation>? typeArgumentList,
     DartType? genericType,
     DartType? instantiatedType,
@@ -407,6 +435,37 @@
     }
   }
 
+  /// Checks to ensure that the given list of type [arguments] does not have a
+  /// type parameter as a type argument. The [errorCode] is either
+  /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST] or
+  /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP].
+  void _checkTypeArgumentConst(
+      NodeList<TypeAnnotation> arguments, ErrorCode errorCode) {
+    for (TypeAnnotation type in arguments) {
+      if (type is TypeName && type.type is TypeParameterType) {
+        _errorReporter.reportErrorForNode(errorCode, type, [type.name]);
+      }
+    }
+  }
+
+  /// Verify that the given list of [typeArguments] contains exactly the
+  /// [expectedCount] of elements, reporting an error with the [errorCode]
+  /// if not.
+  void _checkTypeArgumentCount(
+    TypeArgumentList typeArguments,
+    int expectedCount,
+    ErrorCode errorCode,
+  ) {
+    int actualCount = typeArguments.arguments.length;
+    if (actualCount != expectedCount) {
+      _errorReporter.reportErrorForNode(
+        errorCode,
+        typeArguments,
+        [actualCount],
+      );
+    }
+  }
+
   /// Given a [node] without type arguments that refers to [element], issues
   /// an error if [type] is a generic type, and the type arguments were not
   /// supplied from inference or a non-dynamic default instantiation.
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index a702c1c..31e2553 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -558,6 +558,11 @@
   }
 
   @override
+  void visitConstructorReference(ConstructorReference node) {
+    _typeArgumentsVerifier.checkConstructorReference(node);
+  }
+
+  @override
   void visitContinueStatement(ContinueStatement node) {
     var labelNode = node.label;
     if (labelNode != null) {
diff --git a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
index 499d93f..3262f0b 100644
--- a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
@@ -37,7 +37,27 @@
           substitution: {'T': 'String', 'U': 'int'}),
       classElement,
       'A<String, int> Function()',
-      expectedTypeNameType: 'A<String, int>',
+      expectedTypeNameElement: findElement.typeAlias('TA'),
+    );
+  }
+
+  test_alias_generic_const_differingNumberOfTypeParamters() async {
+    await assertNoErrorsInCode('''
+class A<T, U> {
+  A.foo() {}
+}
+typedef TA<T> = A<T, String>;
+
+const x = TA<int>.foo;
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('TA<int>.foo;'),
+      elementMatcher(classElement.getNamedConstructor('foo')!,
+          substitution: {'T': 'int', 'U': 'String'}),
+      classElement,
+      'A<int, String> Function()',
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -61,7 +81,6 @@
           substitution: {'T': 'String', 'U': 'int'}),
       classElement,
       'A<String, int> Function()',
-      expectedTypeNameType: 'A<String, int>',
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -85,7 +104,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -109,7 +127,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -135,7 +152,6 @@
           substitution: {'T': 'String'}),
       classElement,
       'A<String> Function()',
-      expectedTypeNameType: 'A<String>',
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -156,7 +172,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -178,7 +193,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -241,7 +255,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -265,7 +278,6 @@
       null,
       classElement,
       'dynamic',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -287,7 +299,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -309,7 +320,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -331,7 +341,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -355,7 +364,6 @@
           substitution: {'T': 'String'}),
       classElement,
       'A<String> Function()',
-      expectedTypeNameType: 'A<String>',
     );
   }
 
@@ -381,7 +389,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedPrefix: findElement.import('package:test/a.dart').prefix,
       expectedTypeNameElement:
           findElement.importFind('package:test/a.dart').typeAlias('TA'),
@@ -409,7 +416,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedPrefix: findElement.import('package:test/a.dart').prefix,
     );
   }
@@ -438,7 +444,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedPrefix: findElement.import('package:test/a.dart').prefix,
     );
   }
@@ -464,7 +469,6 @@
           substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedPrefix: findElement.import('package:test/a.dart').prefix,
     );
   }
@@ -491,7 +495,6 @@
       classElement.unnamedConstructor,
       classElement,
       'A Function()',
-      expectedTypeNameType: 'A',
     );
   }
 
@@ -518,7 +521,6 @@
       classElement.unnamedConstructor,
       classElement,
       'A Function()',
-      expectedTypeNameType: 'A',
     );
   }
 
@@ -547,7 +549,6 @@
       classElement.unnamedConstructor,
       classElement,
       'A Function()',
-      expectedTypeNameType: 'A',
     );
   }
 
@@ -571,7 +572,6 @@
       constructorElement,
       classElement,
       'A<Never> Function()',
-      expectedTypeNameType: 'A<Never>',
     );
   }
 
@@ -593,7 +593,6 @@
       constructorElement,
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -615,7 +614,6 @@
       constructorElement,
       classElement,
       'A<T> Function<T>()',
-      expectedTypeNameType: null,
     );
   }
 
@@ -637,7 +635,6 @@
       constructorElement,
       classElement,
       'A<T> Function<T extends num>()',
-      expectedTypeNameType: null,
     );
   }
 
@@ -656,7 +653,6 @@
       classElement.unnamedConstructor,
       classElement,
       'A Function()',
-      expectedTypeNameType: 'A',
     );
   }
 
@@ -677,7 +673,6 @@
       classElement.getNamedConstructor('foo')!,
       classElement,
       'A Function()',
-      expectedTypeNameType: 'A',
     );
   }
 
@@ -698,7 +693,6 @@
       classElement.unnamedConstructor,
       classElement,
       'A Function()',
-      expectedTypeNameType: 'A',
     );
   }
 
@@ -719,7 +713,6 @@
       constructorElement,
       classElement,
       'A<T> Function<T>()',
-      expectedTypeNameType: null,
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -743,7 +736,6 @@
       constructorElement,
       findElement.class_('A'),
       'A<String, U> Function<U>()',
-      expectedTypeNameType: null,
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -765,7 +757,6 @@
       elementMatcher(constructorElement, substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -789,7 +780,6 @@
       elementMatcher(constructorElement, substitution: {'T': 'int'}),
       classElement,
       'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
       expectedTypeNameElement: findElement.typeAlias('TA'),
     );
   }
@@ -817,7 +807,6 @@
       null,
       classElement,
       'dynamic',
-      expectedTypeNameType: 'A<int>',
     );
   }
 
@@ -840,7 +829,6 @@
       classElement.getNamedConstructor('foo')!,
       classElement,
       'A Function()',
-      expectedTypeNameType: 'A',
     );
   }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 9e3ea8b..9a340186 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -166,7 +166,6 @@
     Object? expectedConstructorElement,
     ClassElement expectedClassElement,
     String expectedType, {
-    required String? expectedTypeNameType,
     PrefixElement? expectedPrefix,
     Element? expectedTypeNameElement,
   }) {
@@ -184,7 +183,7 @@
 
     var typeName = node.constructorName.type;
     expectedTypeNameElement ??= expectedClassElement;
-    assertTypeName(typeName, expectedTypeNameElement, expectedTypeNameType,
+    assertTypeName(typeName, expectedTypeNameElement, null,
         expectedPrefix: expectedPrefix);
   }
 
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 9ea82b2..5bd8d77 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -6332,7 +6332,7 @@
                   staticElement: self::@class::A
                   staticType: null
                   token: A @35
-                type: A
+                type: null
             staticType: A Function()
     accessors
       synthetic static get v @-1