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