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:
committed by
commit-bot@chromium.org
parent
d0ee565bf8
commit
e673c19820
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user