Resolve annotations via type aliases.
Change-Id: I6d22e06775d31856fc85a350892efe15208832f5
Bug: https://github.com/dart-lang/sdk/issues/44838
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193743
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
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);