Version 2.13.0-189.0.dev
Merge commit 'e673c1982045905c6bd488f524c0d7c497c733d9' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
index d246ff1..c82e332 100644
--- a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
@@ -14,6 +14,7 @@
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
@@ -46,6 +47,38 @@
}
}
+ void _classConstructorInvocation(
+ AnnotationImpl node,
+ ClassElement classElement,
+ SimpleIdentifierImpl? constructorName,
+ ArgumentList argumentList,
+ List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+ ) {
+ ConstructorElement? constructorElement;
+ if (constructorName != null) {
+ constructorElement = classElement.getNamedConstructor(
+ constructorName.name,
+ );
+ } else {
+ constructorElement = classElement.unnamedConstructor;
+ }
+
+ _constructorInvocation(
+ node,
+ constructorName,
+ classElement.typeParameters,
+ constructorElement,
+ argumentList,
+ (typeArguments) {
+ return classElement.instantiate(
+ typeArguments: typeArguments,
+ nullabilitySuffix: _resolver.noneOrStarSuffix,
+ );
+ },
+ whyNotPromotedList,
+ );
+ }
+
void _classGetter(
AnnotationImpl node,
ClassElement classElement,
@@ -80,20 +113,13 @@
void _constructorInvocation(
AnnotationImpl node,
- ClassElement classElement,
SimpleIdentifierImpl? constructorName,
+ List<TypeParameterElement> typeParameters,
+ ConstructorElement? constructorElement,
ArgumentList argumentList,
+ InterfaceType Function(List<DartType> typeArguments) instantiateElement,
List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
) {
- ConstructorElement? constructorElement;
- if (constructorName != null) {
- constructorElement = classElement.getNamedConstructor(
- constructorName.name,
- );
- } else {
- constructorElement = classElement.unnamedConstructor;
- }
-
constructorElement = _resolver.toLegacyElement(constructorElement);
constructorName?.staticElement = constructorElement;
node.element = constructorElement;
@@ -108,8 +134,6 @@
return;
}
- var typeParameters = classElement.typeParameters;
-
// If no type parameters, the elements are correct.
if (typeParameters.isEmpty) {
_resolveConstructorInvocationArguments(node);
@@ -123,10 +147,7 @@
List<DartType> typeArguments,
ConstructorElement constructorElement,
) {
- var type = classElement.instantiate(
- typeArguments: typeArguments,
- nullabilitySuffix: _resolver.noneOrStarSuffix,
- );
+ var type = instantiateElement(typeArguments);
constructorElement = ConstructorMember.from(constructorElement, type);
constructorName?.staticElement = constructorElement;
node.element = constructorElement;
@@ -166,8 +187,11 @@
_resolver.visitArgumentList(argumentList,
whyNotPromotedList: whyNotPromotedList);
- var constructorRawType = _resolver.typeAnalyzer
- .constructorToGenericFunctionType(constructorElement);
+ var elementToInfer = ConstructorElementToInfer(
+ typeParameters,
+ constructorElement,
+ );
+ var constructorRawType = elementToInfer.asType;
var inferred = _resolver.inferenceHelper.inferGenericInvoke(
node, constructorRawType, typeArgumentList, argumentList, node,
@@ -264,7 +288,7 @@
// Class(args) or Class.CONST
if (element1 is ClassElement) {
if (argumentList != null) {
- _constructorInvocation(
+ _classConstructorInvocation(
node, element1, name2, argumentList, whyNotPromotedList);
} else {
_classGetter(node, element1, name2, whyNotPromotedList);
@@ -286,7 +310,7 @@
// prefix.Class(args) or prefix.Class.CONST
if (element2 is ClassElement) {
if (argumentList != null) {
- _constructorInvocation(
+ _classConstructorInvocation(
node, element2, name3, argumentList, whyNotPromotedList);
} else {
_classGetter(node, element2, name3, whyNotPromotedList);
@@ -303,6 +327,19 @@
_propertyAccessorElement(node, name2, element2, whyNotPromotedList);
return;
}
+
+ // prefix.TypeAlias(args) or prefix.TypeAlias.CONST
+ if (element2 is TypeAliasElement) {
+ var aliasedType = element2.aliasedType;
+ var argumentList = node.arguments;
+ if (aliasedType is InterfaceType && argumentList != null) {
+ _typeAliasConstructorInvocation(node, element2, name3, aliasedType,
+ argumentList, whyNotPromotedList);
+ } else {
+ _typeAliasGetter(node, element2, name3, whyNotPromotedList);
+ }
+ return;
+ }
// undefined
if (element2 == null) {
_errorReporter.reportErrorForNode(
@@ -322,6 +359,19 @@
return;
}
+ // TypeAlias(args) or TypeAlias.CONST
+ if (element1 is TypeAliasElement) {
+ var aliasedType = element1.aliasedType;
+ var argumentList = node.arguments;
+ if (aliasedType is InterfaceType && argumentList != null) {
+ _typeAliasConstructorInvocation(node, element1, name2, aliasedType,
+ argumentList, whyNotPromotedList);
+ } else {
+ _typeAliasGetter(node, element1, name2, whyNotPromotedList);
+ }
+ return;
+ }
+
// TODO(scheglov) Must be const.
if (element1 is VariableElement) {
return;
@@ -393,6 +443,67 @@
}
}
+ void _typeAliasConstructorInvocation(
+ AnnotationImpl node,
+ TypeAliasElement typeAliasElement,
+ SimpleIdentifierImpl? constructorName,
+ InterfaceType aliasedType,
+ ArgumentList argumentList,
+ List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+ ) {
+ var constructorElement = aliasedType.lookUpConstructor(
+ constructorName?.name,
+ _definingLibrary,
+ );
+
+ _constructorInvocation(
+ node,
+ constructorName,
+ typeAliasElement.typeParameters,
+ constructorElement,
+ argumentList,
+ (typeArguments) {
+ return typeAliasElement.instantiate(
+ typeArguments: typeArguments,
+ nullabilitySuffix: _resolver.noneOrStarSuffix,
+ ) as InterfaceType;
+ },
+ whyNotPromotedList,
+ );
+ }
+
+ void _typeAliasGetter(
+ AnnotationImpl node,
+ TypeAliasElement typeAliasElement,
+ SimpleIdentifierImpl? getterName,
+ List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+ ) {
+ ExecutableElement? getter;
+ var aliasedType = typeAliasElement.aliasedType;
+ if (aliasedType is InterfaceType) {
+ var classElement = aliasedType.element;
+ if (getterName != null) {
+ getter = classElement.getGetter(getterName.name);
+ getter = _resolver.toLegacyElement(getter);
+ }
+ }
+
+ getterName?.staticElement = getter;
+ node.element = getter;
+
+ if (getterName != null && getter is PropertyAccessorElement) {
+ _propertyAccessorElement(node, getterName, getter, whyNotPromotedList);
+ _resolveAnnotationElementGetter(node, getter);
+ } else if (getter is! ConstructorElement) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.INVALID_ANNOTATION,
+ node,
+ );
+ }
+
+ _visitArguments(node, whyNotPromotedList);
+ }
+
void _visitArguments(AnnotationImpl node,
List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
var arguments = node.arguments;
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
index 65f17a2..4635774 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -40,6 +40,12 @@
ConstructorElementToInfer(this.typeParameters, this.element);
+ /// Return the equivalent generic function type that we could use to
+ /// forward to the constructor, or for a non-generic type simply returns
+ /// the constructor type.
+ ///
+ /// For example given the type `class C<T> { C(T arg); }`, the generic
+ /// function type is `<T>(T) -> C<T>`.
FunctionType get asType {
return FunctionTypeImpl(
typeFormals: typeParameters,
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 9ef5076..ead332f 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -4,13 +4,10 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/member.dart' show ConstructorMember;
-import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/generated/migration.dart';
@@ -48,28 +45,6 @@
_dynamicType = _typeProvider.dynamicType;
}
- /// Given a constructor for a generic type, returns the equivalent generic
- /// function type that we could use to forward to the constructor, or for a
- /// non-generic type simply returns the constructor type.
- ///
- /// For example given the type `class C<T> { C(T arg); }`, the generic function
- /// type is `<T>(T) -> C<T>`.
- FunctionType constructorToGenericFunctionType(
- ConstructorElement constructor) {
- var classElement = constructor.enclosingElement;
- var typeParameters = classElement.typeParameters;
- if (typeParameters.isEmpty) {
- return constructor.type;
- }
-
- return FunctionTypeImpl(
- typeFormals: typeParameters,
- parameters: constructor.parameters,
- returnType: constructor.returnType,
- nullabilitySuffix: NullabilitySuffix.star,
- );
- }
-
/// Record that the static type of the given node is the given type.
///
/// @param expression the node whose type is to be recorded
diff --git a/pkg/analyzer/lib/src/test_utilities/find_element.dart b/pkg/analyzer/lib/src/test_utilities/find_element.dart
index 53cdcc4..75e989e 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_element.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_element.dart
@@ -149,6 +149,7 @@
return result!;
}
+ @override
ParameterElement parameter(String name) {
ParameterElement? result;
@@ -527,6 +528,28 @@
throw StateError('Not found: $name');
}
+ ParameterElement parameter(String name) {
+ ParameterElement? result;
+
+ for (var class_ in unitElement.types) {
+ for (var constructor in class_.constructors) {
+ for (var parameter in constructor.parameters) {
+ if (parameter.name == name) {
+ if (result != null) {
+ throw StateError('Not unique: $name');
+ }
+ result = parameter;
+ }
+ }
+ }
+ }
+
+ if (result != null) {
+ return result;
+ }
+ throw StateError('Not found: $name');
+ }
+
PropertyAccessorElement setter(String name, {String? of}) {
PropertyAccessorElement? result;
diff --git a/pkg/analyzer/test/src/dart/resolution/metadata_test.dart b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
index d96b3dc..75eaf41 100644
--- a/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
@@ -2,6 +2,8 @@
// 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 'dart:collection';
+
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
@@ -367,6 +369,38 @@
);
}
+ test_value_class_staticConstField() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ static const int foo = 42;
+}
+
+@A.foo
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@A');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ element: self::@class::A::@getter::foo
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: self::@class::A::@getter::foo
+ staticType: null
+ token: foo
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@class::A
+ staticType: null
+ token: A
+ staticElement: self::@class::A::@getter::foo
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, '''
+int 42
+''');
+ }
+
test_value_class_unnamedConstructor() async {
await assertNoErrorsInCode(r'''
class A {
@@ -822,6 +856,819 @@
''');
}
+ test_value_prefix_typeAlias_class_staticConstField() async {
+ newFile('$testPackageLibPath/a.dart', content: r'''
+class A {
+ static const int foo = 42;
+}
+
+typedef B = A;
+''');
+ await assertNoErrorsInCode(r'''
+import 'a.dart' as prefix;
+
+@prefix.B.foo
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@prefix.B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ constructorName: SimpleIdentifier
+ staticElement: package:test/a.dart::@class::A::@getter::foo
+ staticType: null
+ token: foo
+ element: package:test/a.dart::@class::A::@getter::foo
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+ token: B
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@prefix::prefix
+ staticType: null
+ token: prefix
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, '''
+int 42
+''');
+ }
+
+ test_value_prefix_typeAlias_generic_class_generic_all_inference_namedConstructor() async {
+ newFile('$testPackageLibPath/a.dart', content: r'''
+class A<T> {
+ final T f;
+ const A.named(this.f);
+}
+
+typedef B<U> = A<U>;
+''');
+ await assertNoErrorsInCode(r'''
+import 'a.dart' as prefix;
+
+@prefix.B.named(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@prefix.B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ constructorName: SimpleIdentifier
+ staticElement: ConstructorMember
+ base: package:test/a.dart::@class::A::@constructor::named
+ substitution: {T: int}
+ staticType: null
+ token: named
+ element: ConstructorMember
+ base: package:test/a.dart::@class::A::@constructor::named
+ substitution: {T: int}
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+ token: B
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@prefix::prefix
+ staticType: null
+ token: prefix
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.importFind('package:test/a.dart').parameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_prefix_typeAlias_generic_class_generic_all_inference_unnamedConstructor() async {
+ newFile('$testPackageLibPath/a.dart', content: r'''
+class A<T> {
+ final T f;
+ const A(this.f);
+}
+
+typedef B<U> = A<U>;
+''');
+ await assertNoErrorsInCode(r'''
+import 'a.dart' as prefix;
+
+@prefix.B(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@prefix.B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: ConstructorMember
+ base: package:test/a.dart::@class::A::@constructor::•
+ substitution: {T: int}
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+ token: B
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@prefix::prefix
+ staticType: null
+ token: prefix
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.importFind('package:test/a.dart').parameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_prefix_typeAlias_generic_class_generic_all_typeArguments_namedConstructor() async {
+ newFile('$testPackageLibPath/a.dart', content: r'''
+class A<T> {
+ final T f;
+ const A.named(this.f);
+}
+
+typedef B<U> = A<U>;
+''');
+ await assertNoErrorsInCode(r'''
+import 'a.dart' as prefix;
+
+@prefix.B<int>.named(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@prefix.B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ constructorName: SimpleIdentifier
+ staticElement: ConstructorMember
+ base: package:test/a.dart::@class::A::@constructor::named
+ substitution: {T: int}
+ staticType: null
+ token: named
+ element: ConstructorMember
+ base: package:test/a.dart::@class::A::@constructor::named
+ substitution: {T: int}
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+ token: B
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@prefix::prefix
+ staticType: null
+ token: prefix
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+ typeArguments: TypeArgumentList
+ arguments
+ TypeName
+ name: SimpleIdentifier
+ staticElement: dart:core::@class::int
+ staticType: null
+ token: int
+ type: int
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.importFind('package:test/a.dart').parameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_prefix_typeAlias_generic_class_generic_all_typeArguments_unnamedConstructor() async {
+ newFile('$testPackageLibPath/a.dart', content: r'''
+class A<T> {
+ final T f;
+ const A(this.f);
+}
+
+typedef B<U> = A<U>;
+''');
+ await assertNoErrorsInCode(r'''
+import 'a.dart' as prefix;
+
+@prefix.B<int>(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@prefix.B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: ConstructorMember
+ base: package:test/a.dart::@class::A::@constructor::•
+ substitution: {T: int}
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+ token: B
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@prefix::prefix
+ staticType: null
+ token: prefix
+ staticElement: package:test/a.dart::@typeAlias::B
+ staticType: null
+ typeArguments: TypeArgumentList
+ arguments
+ TypeName
+ name: SimpleIdentifier
+ staticElement: dart:core::@class::int
+ staticType: null
+ token: int
+ type: int
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.importFind('package:test/a.dart').parameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_typeAlias_class_staticConstField() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ static const int foo = 42;
+}
+
+typedef B = A;
+
+@B.foo
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ element: self::@class::A::@getter::foo
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: self::@class::A::@getter::foo
+ staticType: null
+ token: foo
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ staticElement: self::@class::A::@getter::foo
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, '''
+int 42
+''');
+ }
+
+ test_value_typeAlias_generic_class_generic_1of2_typeArguments_namedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T, U> {
+ final T t;
+ final U u;
+ const A.named(this.t, this.u);
+}
+
+typedef B<T> = A<T, double>;
+
+@B<int>.named(42, 1.2)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ DoubleLiteral
+ literal: 1.2
+ staticType: double
+ constructorName: SimpleIdentifier
+ staticElement: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int, U: double}
+ staticType: null
+ token: named
+ element: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int, U: double}
+ name: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ typeArguments: TypeArgumentList
+ arguments
+ TypeName
+ name: SimpleIdentifier
+ staticElement: dart:core::@class::int
+ staticType: null
+ token: int
+ type: int
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int, double>
+ t: int 42
+ u: double 1.2
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('t'),
+ substitution: {'T': 'int', 'U': 'double'},
+ );
+
+ assertElement2(
+ findNode.doubleLiteral('1.2').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('u'),
+ substitution: {'T': 'int', 'U': 'double'},
+ );
+ }
+
+ test_value_typeAlias_generic_class_generic_1of2_typeArguments_unnamedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T, U> {
+ final T t;
+ final U u;
+ const A(this.t, this.u);
+}
+
+typedef B<T> = A<T, double>;
+
+@B<int>(42, 1.2)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ DoubleLiteral
+ literal: 1.2
+ staticType: double
+ element: ConstructorMember
+ base: self::@class::A::@constructor::•
+ substitution: {T: int, U: double}
+ name: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ typeArguments: TypeArgumentList
+ arguments
+ TypeName
+ name: SimpleIdentifier
+ staticElement: dart:core::@class::int
+ staticType: null
+ token: int
+ type: int
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int, double>
+ t: int 42
+ u: double 1.2
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('t'),
+ substitution: {'T': 'int', 'U': 'double'},
+ );
+
+ assertElement2(
+ findNode.doubleLiteral('1.2').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('u'),
+ substitution: {'T': 'int', 'U': 'double'},
+ );
+ }
+
+ test_value_typeAlias_generic_class_generic_all_inference_namedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ final T f;
+ const A.named(this.f);
+}
+
+typedef B<U> = A<U>;
+
+@B.named(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ staticType: null
+ token: named
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ staticElement: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_typeAlias_generic_class_generic_all_inference_unnamedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ final T f;
+ const A(this.f);
+}
+
+typedef B<U> = A<U>;
+
+@B(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: ConstructorMember
+ base: self::@class::A::@constructor::•
+ substitution: {T: int}
+ name: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_typeAlias_generic_class_generic_all_typeArguments_namedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ final T f;
+ const A.named(this.f);
+}
+
+typedef B<U> = A<U>;
+
+@B<int>.named(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ constructorName: SimpleIdentifier
+ staticElement: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ staticType: null
+ token: named
+ element: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ name: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ typeArguments: TypeArgumentList
+ arguments
+ TypeName
+ name: SimpleIdentifier
+ staticElement: dart:core::@class::int
+ staticType: null
+ token: int
+ type: int
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_typeAlias_generic_class_generic_all_typeArguments_unnamedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ final T f;
+ const A(this.f);
+}
+
+typedef B<U> = A<U>;
+
+@B<int>(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: ConstructorMember
+ base: self::@class::A::@constructor::•
+ substitution: {T: int}
+ name: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ typeArguments: TypeArgumentList
+ arguments
+ TypeName
+ name: SimpleIdentifier
+ staticElement: dart:core::@class::int
+ staticType: null
+ token: int
+ type: int
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_typeAlias_notGeneric_class_generic_namedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ final T f;
+ const A.named(this.f);
+}
+
+typedef B = A<int>;
+
+@B.named(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ staticType: null
+ token: named
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ staticElement: ConstructorMember
+ base: self::@class::A::@constructor::named
+ substitution: {T: int}
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_typeAlias_notGeneric_class_generic_unnamedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ final T f;
+ const A(this.f);
+}
+
+typedef B = A<int>;
+
+@B(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: ConstructorMember
+ base: self::@class::A::@constructor::•
+ substitution: {T: int}
+ name: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+''');
+ _assertAnnotationValueText(annotation, r'''
+A<int>
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ substitution: {'T': 'int'},
+ );
+ }
+
+ test_value_typeAlias_notGeneric_class_notGeneric_namedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ final int f;
+ const A.named(this.f);
+}
+
+typedef B = A;
+
+@B.named(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: self::@class::A::@constructor::named
+ name: PrefixedIdentifier
+ identifier: SimpleIdentifier
+ staticElement: self::@class::A::@constructor::named
+ staticType: null
+ token: named
+ period: .
+ prefix: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+ staticElement: self::@class::A::@constructor::named
+ staticType: null
+''');
+ _assertAnnotationValueText(annotation, r'''
+A
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ );
+ }
+
+ test_value_typeAlias_notGeneric_class_notGeneric_unnamedConstructor() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ final int f;
+ const A(this.f);
+}
+
+typedef B = A;
+
+@B(42)
+void f() {}
+''');
+
+ var annotation = findNode.annotation('@B');
+ _assertResolvedNodeText(annotation, r'''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 42
+ staticType: int
+ element: self::@class::A::@constructor::•
+ name: SimpleIdentifier
+ staticElement: self::@typeAlias::B
+ staticType: null
+ token: B
+''');
+ _assertAnnotationValueText(annotation, r'''
+A
+ f: int 42
+''');
+
+ assertElement2(
+ findNode.integerLiteral('42').staticParameterElement,
+ declaration: findElement.fieldFormalParameter('f'),
+ );
+ }
+
void _assertAnnotationValueText(Annotation annotation, String expected) {
var elementAnnotation = annotation.elementAnnotation!;
_assertElementAnnotationValueText(elementAnnotation, expected);
@@ -874,7 +1721,10 @@
void write(DartObjectImpl? object, String indent) {
if (object != null) {
var type = object.type;
- if (type.isDartCoreInt) {
+ if (type.isDartCoreDouble) {
+ sink.write('double ');
+ sink.writeln(object.toDoubleValue());
+ } else if (type.isDartCoreInt) {
sink.write('int ');
sink.writeln(object.toIntValue());
} else if (object.isUserDefinedObject) {
@@ -883,7 +1733,8 @@
sink.writeln(typeStr);
var fields = object.fields;
if (fields != null) {
- for (var entry in fields.entries) {
+ var sortedFields = SplayTreeMap.of(fields);
+ for (var entry in sortedFields.entries) {
sink.write(newIndent);
sink.write('${entry.key}: ');
write(entry.value, newIndent);
diff --git a/pkg/test_runner/lib/src/static_error.dart b/pkg/test_runner/lib/src/static_error.dart
index a24aad6..8252dc5 100644
--- a/pkg/test_runner/lib/src/static_error.dart
+++ b/pkg/test_runner/lib/src/static_error.dart
@@ -490,7 +490,12 @@
/// For errors that have a number associated with them, tracks that number.
///
/// These are used after parsing to attach context messages to their errors.
- final Map<StaticError, int> _errorNumbers = {};
+ ///
+ /// Note: if the same context message appears multiple times at the same
+ /// location, there will be distinct (non-identical) StaticError instances
+ /// that compare equal. We use `Map.identity` to ensure that we can associate
+ /// each with its own context number.
+ final Map<StaticError, int> _errorNumbers = Map.identity();
int _currentLine = 0;
diff --git a/pkg/test_runner/lib/src/update_errors.dart b/pkg/test_runner/lib/src/update_errors.dart
index 0023b02..7ef97fe 100644
--- a/pkg/test_runner/lib/src/update_errors.dart
+++ b/pkg/test_runner/lib/src/update_errors.dart
@@ -174,7 +174,11 @@
/// Assigns unique numbers to all [errors] that have context messages, as well
/// as their context messages.
Map<StaticError, int> _numberErrors(List<StaticError> errors) {
- var result = <StaticError, int>{};
+ // Note: if the same context message appears multiple times at the same
+ // location, there will be distinct (non-identical) StaticError instances
+ // that compare equal. We use `Map.identity` to ensure that we can associate
+ // each with its own context number.
+ var result = Map<StaticError, int>.identity();
var number = 1;
for (var error in errors) {
if (error.contextMessages.isEmpty) continue;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e82c80d..bbe41e4 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5976,23 +5976,24 @@
if (num_types <= kNullabilityMaxTypes) {
AbstractType& type = AbstractType::Handle();
for (intptr_t i = 0; i < num_types; i++) {
- result <<= kNullabilityBitsPerType;
type = TypeAt(i);
+ intptr_t type_bits = 0;
if (!type.IsNull() && !type.IsNullTypeRef()) {
switch (type.nullability()) {
case Nullability::kNullable:
- result |= kNullableBits;
+ type_bits = kNullableBits;
break;
case Nullability::kNonNullable:
- result |= kNonNullableBits;
+ type_bits = kNonNullableBits;
break;
case Nullability::kLegacy:
- result |= kLegacyBits;
+ type_bits = kLegacyBits;
break;
default:
UNREACHABLE();
}
}
+ result |= (type_bits << (i * kNullabilityBitsPerType));
}
}
set_nullability(result);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6a4b5bb..259e3df 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -7368,10 +7368,12 @@
// 2 bits per type:
// - the high bit is set if the type is nullable or legacy.
// - the low bit is set if the type is nullable.
- // The nullabilty is 0 if the vector is longer than kNullabilityMaxTypes.
+ // The nullability is 0 if the vector is longer than kNullabilityMaxTypes.
// The condition evaluated at runtime to decide whether UTA can share ITA is
// (UTA.nullability & ITA.nullability) == UTA.nullability
- // Note that this allows for ITA to be longer than UTA.
+ // Note that this allows for ITA to be longer than UTA (the bit vector must be
+ // stored in the same order as the corresponding type vector, i.e. with the
+ // least significant 2 bits representing the nullability of the first type).
static const intptr_t kNullabilityBitsPerType = 2;
static const intptr_t kNullabilityMaxTypes =
kSmiBits / kNullabilityBitsPerType;
diff --git a/tests/language/regress/regress45529_test.dart b/tests/language/regress/regress45529_test.dart
new file mode 100644
index 0000000..6cd4ed2
--- /dev/null
+++ b/tests/language/regress/regress45529_test.dart
@@ -0,0 +1,24 @@
+// 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:expect/expect.dart";
+
+void main() {
+ final baz = Foo<Null>().baz;
+ Expect.equals("Baz<Bar<Null>?>", baz.runtimeType.toString());
+ baz.v = baz.v;
+}
+
+class Bar<T> {}
+
+class Foo<T> extends Quux<Bar<T>> {}
+
+class Baz<T> {
+ Baz(this.v);
+ T v;
+}
+
+class Quux<T> {
+ final baz = Baz<T?>(null);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 079095e..27132aa 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 188
+PRERELEASE 189
PRERELEASE_PATCH 0
\ No newline at end of file