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>
This commit is contained in:
Konstantin Shcheglov
2021-04-01 02:52:43 +00:00
committed by commit-bot@chromium.org
parent d0ee565bf8
commit e673c19820
5 changed files with 1013 additions and 47 deletions
@@ -14,6 +14,7 @@ import 'package:analyzer/src/dart/constant/utilities.dart';
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 @@ class AnnotationResolver {
}
}
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 @@ class AnnotationResolver {
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 @@ class AnnotationResolver {
return;
}
var typeParameters = classElement.typeParameters;
// If no type parameters, the elements are correct.
if (typeParameters.isEmpty) {
_resolveConstructorInvocationArguments(node);
@@ -123,10 +147,7 @@ class AnnotationResolver {
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 @@ class AnnotationResolver {
_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 AnnotationResolver {
// 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 @@ class AnnotationResolver {
// 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 @@ class AnnotationResolver {
_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 @@ class AnnotationResolver {
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 @@ class AnnotationResolver {
}
}
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;
@@ -40,6 +40,12 @@ class ConstructorElementToInfer {
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,
@@ -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 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
_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
@@ -149,6 +149,7 @@ class FindElement extends _FindElementBase {
return result!;
}
@override
ParameterElement parameter(String name) {
ParameterElement? result;
@@ -527,6 +528,28 @@ abstract class _FindElementBase {
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;
@@ -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 @@ A
);
}
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 @@ A
''');
}
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 @@ class _DartObjectPrinter {
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 @@ class _DartObjectPrinter {
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);