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);