Version 2.15.0-272.0.dev

Merge commit '243fb5c383416fb82b6852a83ad3c4449683858a' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index dc0030a..ac9fbb1 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -632,39 +632,31 @@
 
   @override
   DartObjectImpl? visitConstructorReference(ConstructorReference node) {
-    var constructorTearoffResult = DartObjectImpl(
+    var constructorFunctionType = node.typeOrThrow as FunctionType;
+    var classType = constructorFunctionType.returnType as InterfaceType;
+    var typeArguments = classType.typeArguments;
+    // The result is already instantiated during resolution;
+    // [_dartObjectComputer.typeInstantiate] is unnecessary.
+    var typeElement =
+        node.constructorName.type2.name.staticElement as TypeDefiningElement;
+
+    TypeAliasElement? viaTypeAlias;
+    if (typeElement is TypeAliasElementImpl) {
+      if (constructorFunctionType.typeFormals.isNotEmpty &&
+          !typeElement.isProperRename()) {
+        // The type alias is not a proper rename of the aliased class, so
+        // the constructor tear-off is distinct from the associated
+        // constructor function of the aliased class.
+        viaTypeAlias = typeElement;
+      }
+    }
+
+    return DartObjectImpl(
       typeSystem,
       node.typeOrThrow,
-      FunctionState(node.constructorName.staticElement),
+      FunctionState(node.constructorName.staticElement,
+          typeArguments: typeArguments, viaTypeAlias: viaTypeAlias),
     );
-    var typeArgumentList = node.constructorName.type2.typeArguments;
-    if (typeArgumentList == null) {
-      return constructorTearoffResult;
-    } else {
-      var typeArguments = <DartType>[];
-      for (var typeArgument in typeArgumentList.arguments) {
-        var object = typeArgument.accept(this);
-        if (object == null) {
-          return null;
-        }
-        var typeArgumentType = object.toTypeValue();
-        if (typeArgumentType == null) {
-          return null;
-        }
-        // TODO(srawlins): Test type alias types (`typedef i = int`) used as
-        // type arguments. Possibly change implementation based on
-        // canonicalization rules.
-        typeArguments.add(typeArgumentType);
-      }
-      // The result is already instantiated during resolution;
-      // [_dartObjectComputer.typeInstantiate] is unnecessary.
-      return DartObjectImpl(
-        typeSystem,
-        node.typeOrThrow,
-        FunctionState(node.constructorName.staticElement,
-            typeArguments: typeArguments),
-      );
-    }
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/constant/value.dart b/pkg/analyzer/lib/src/dart/constant/value.dart
index 227abea..094b8b6 100644
--- a/pkg/analyzer/lib/src/dart/constant/value.dart
+++ b/pkg/analyzer/lib/src/dart/constant/value.dart
@@ -920,11 +920,14 @@
     List<DartType> typeArguments,
   ) {
     var functionState = _state as FunctionState;
-    var element = functionState._element;
     return DartObjectImpl(
       typeSystem,
       type,
-      FunctionState(element, typeArguments: typeArguments),
+      FunctionState(
+        functionState._element,
+        typeArguments: typeArguments,
+        viaTypeAlias: functionState._viaTypeAlias,
+      ),
     );
   }
 
@@ -1245,10 +1248,22 @@
 
   final List<DartType>? _typeArguments;
 
+  /// The type alias which was referenced when tearing off a constructor,
+  /// if this function is a constructor tear-off, referenced via a type alias,
+  /// and the type alias is not a proper rename for the class, and the
+  /// constructor tear-off is generic, so the tear-off cannot be considered
+  /// equivalent to tearing off the associated constructor function of the
+  /// aliased class.
+  ///
+  /// Otherwise null.
+  final TypeDefiningElement? _viaTypeAlias;
+
   /// Initialize a newly created state to represent the function with the given
   /// [element].
-  FunctionState(this._element, {List<DartType>? typeArguments})
-      : _typeArguments = typeArguments;
+  FunctionState(this._element,
+      {List<DartType>? typeArguments, TypeDefiningElement? viaTypeAlias})
+      : _typeArguments = typeArguments,
+        _viaTypeAlias = viaTypeAlias;
 
   @override
   int get hashCode => _element == null ? 0 : _element.hashCode;
@@ -1272,6 +1287,9 @@
     if (typeArguments.length != otherTypeArguments.length) {
       return false;
     }
+    if (_viaTypeAlias != object._viaTypeAlias) {
+      return false;
+    }
     for (var i = 0; i < typeArguments.length; i++) {
       if (typeArguments[i] != otherTypeArguments[i]) {
         return false;
@@ -1309,6 +1327,9 @@
       if (element?.declaration != otherElement?.declaration) {
         return BoolState.FALSE_STATE;
       }
+      if (_viaTypeAlias != rightOperand._viaTypeAlias) {
+        return BoolState.FALSE_STATE;
+      }
       var typeArguments = _typeArguments;
       var otherTypeArguments = rightOperand._typeArguments;
       if (typeArguments == null || otherTypeArguments == null) {
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 549136f..d5bc77f 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -5582,6 +5582,37 @@
     }
   }
 
+  /// Returns whether this alias is a "proper rename" of [aliasedClass], as
+  /// defined in the constructor-tearoffs specification.
+  bool isProperRename() {
+    var aliasedType_ = aliasedType;
+    if (aliasedType_ is! InterfaceType) {
+      return false;
+    }
+    var aliasedClass = aliasedType_.element;
+    var typeArguments = aliasedType_.typeArguments;
+    var typeParameterCount = typeParameters.length;
+    if (typeParameterCount != aliasedClass.typeParameters.length) {
+      return false;
+    }
+    if (typeParameterCount != typeArguments.length) {
+      return false;
+    }
+    for (var i = 0; i < typeParameterCount; i++) {
+      var bound = typeParameters[i].bound ?? library.typeProvider.dynamicType;
+      var aliasedBound = aliasedClass.typeParameters[i].bound ??
+          library.typeProvider.dynamicType;
+      if (!library.typeSystem.isSubtypeOf(bound, aliasedBound) ||
+          !library.typeSystem.isSubtypeOf(aliasedBound, bound)) {
+        return false;
+      }
+      if (typeParameters[i] != typeArguments[i].element) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   void setLinkedData(Reference reference, ElementLinkedData linkedData) {
     this.reference = reference;
     reference.element = this;
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 ceecf70..4ef8323 100644
--- a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
@@ -123,6 +123,7 @@
         constructorName.staticElement = constructorElement.declaration;
         constructorName.name?.staticElement = constructorElement.declaration;
         node.staticType = inferred;
+        // The NamedType child of `constructorName` doesn't have a static type.
         constructorName.type2.type = null;
       }
     } else {
@@ -132,6 +133,7 @@
       } else {
         node.staticType = constructorElement.type;
       }
+      // The NamedType child of `constructorName` doesn't have a static type.
       constructorName.type2.type = null;
     }
   }
diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
index f544ef9..26f0785 100644
--- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
@@ -28,6 +28,126 @@
 @reflectiveTest
 class ConstantVisitorTest extends ConstantVisitorTestSupport
     with ConstantVisitorTestCases {
+  test_identical_constructorReference_aliasIsNotGeneric() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC = C<int>;
+const a = identical(MyC.new, C<int>.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(true),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsNotProperRename_differentBound() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends num> = C<T>;
+const a = identical(MyC.new, C.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(false),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsNotProperRename_differentCount() async {
+    await resolveTestCode('''
+class C<T, U> {}
+typedef MyC<T> = C<T, int>;
+const a = identical(MyC.new, C.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(false),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsNotProperRename_differentOrder() async {
+    await resolveTestCode('''
+class C<T, U> {}
+typedef MyC<T, U> = C<U, T>;
+const a = identical(MyC.new, C.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(false),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsNotProperRename_instantiated() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends num> = C<T>;
+const a = identical(MyC<int>.new, C<int>.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(true),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsNotProperRename_mixedInstantiations() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends num> = C<T>;
+const a = identical(MyC<int>.new, (MyC.new)<int>);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(false),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsProperRename_instantiated() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC<T> = C<T>;
+const a = identical(MyC<int>.new, MyC<int>.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(true),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsProperRename_mixedInstantiations() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC<T> = C<T>;
+const a = identical(MyC<int>.new, (MyC.new)<int>);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(true),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsProperRename_mutualSubtypes() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends Object?> = C<T>;
+const a = identical(MyC<int>.new, MyC<int>.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(true),
+    );
+  }
+
+  test_identical_constructorReference_aliasIsProperRename_uninstantiated() async {
+    await resolveTestCode('''
+class C<T> {}
+typedef MyC<T> = C<T>;
+const a = identical(MyC.new, MyC.new);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(true),
+    );
+  }
+
   test_identical_constructorReference_explicitTypeArgs_differentClasses() async {
     await resolveTestCode('''
 class C<T> {}
@@ -76,6 +196,19 @@
     );
   }
 
+  test_identical_constructorReference_inferredTypeArgs_sameElement() async {
+    await resolveTestCode('''
+class C<T> {}
+const C<int> Function() c1 = C.new;
+const c2 = C<int>.new;
+const a = identical(c1, c2);
+''');
+    expect(
+      _evaluateConstant('a'),
+      _boolValue(true),
+    );
+  }
+
   test_identical_constructorReference_notInstantiated_differentClasses() async {
     await resolveTestCode('''
 class C<T> {}
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 48f946f..0b2478b 100644
--- a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
@@ -85,6 +85,26 @@
     );
   }
 
+  test_alias_generic_uninstantiated_const() async {
+    await assertNoErrorsInCode('''
+class A<T, U> {
+  const A.foo();
+}
+typedef TA<T, U> = A<U, T>;
+
+const a = TA.foo;
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('TA.foo;'),
+      classElement.getNamedConstructor('foo'),
+      classElement,
+      'A<U, T> Function<T, U>()',
+      expectedTypeNameElement: findElement.typeAlias('TA'),
+    );
+  }
+
   test_alias_generic_unnamed() async {
     await assertNoErrorsInCode('''
 class A<T> {
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index bed3b36..6a27317 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -150,13 +150,12 @@
     }
   }
 
-  void assertConstructorElement(
-      ConstructorElement? expected, ConstructorElement? actual) {
-    if (expected is ConstructorMember && actual is ConstructorMember) {
-      expect(expected.declaration, same(actual.declaration));
+  void assertConstructorElement(ConstructorElement? actual, Object? expected) {
+    if (actual is ConstructorMember && expected is ConstructorMember) {
+      expect(actual.declaration, same(expected.declaration));
       // TODO(brianwilkerson) Compare the type arguments of the two members.
     } else {
-      expect(expected, same(actual));
+      assertElement(actual, expected);
     }
   }
 
@@ -168,12 +167,11 @@
     PrefixElement? expectedPrefix,
     Element? expectedTypeNameElement,
   }) {
-    var actualConstructorElement = getNodeElement(node) as ConstructorElement?;
     var actualConstructorName = node.constructorName.name;
     if (actualConstructorName != null) {
       assertConstructorElement(
         actualConstructorName.staticElement as ConstructorElement?,
-        actualConstructorElement,
+        expectedConstructorElement,
       );
     }
 
@@ -182,8 +180,13 @@
 
     var namedType = node.constructorName.type2;
     expectedTypeNameElement ??= expectedClassElement;
-    assertNamedType(namedType, expectedTypeNameElement, null,
-        expectedPrefix: expectedPrefix);
+    assertNamedType(
+      namedType, expectedTypeNameElement,
+      // The [NamedType] child node of the [ConstructorName] should not have a
+      // static type.
+      null,
+      expectedPrefix: expectedPrefix,
+    );
   }
 
   void assertConstructors(ClassElement class_, List<String> expected) {
diff --git a/tools/VERSION b/tools/VERSION
index 341b927..319e2c2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 271
+PRERELEASE 272
 PRERELEASE_PATCH 0
\ No newline at end of file