Resolve RecordTypeAnnotation AST.

Change-Id: I5eac632ea3badf5dbce35fb842b17c9898bb02cb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255818
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index ee1c4b7..938d24f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -23,6 +23,7 @@
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/element_walker.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/summary2/types_builder.dart';
 
 class ElementHolder {
   final ElementImpl _element;
@@ -968,6 +969,12 @@
   }
 
   @override
+  void visitRecordTypeAnnotation(covariant RecordTypeAnnotationImpl node) {
+    node.visitChildren(this);
+    TypesBuilder.recordTypeAnnotation(node);
+  }
+
+  @override
   void visitSimpleFormalParameter(covariant SimpleFormalParameterImpl node) {
     ParameterElementImpl element;
     if (node.parent is DefaultFormalParameter) {
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 26e9a4e9..3acde71 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -2216,6 +2216,32 @@
   }
 
   @override
+  void visitRecordTypeAnnotation(RecordTypeAnnotation node) {
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitRecordTypeAnnotationNamedField(
+    RecordTypeAnnotationNamedField node,
+  ) {
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitRecordTypeAnnotationNamedFields(
+    RecordTypeAnnotationNamedFields node,
+  ) {
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitRecordTypeAnnotationPositionalField(
+    RecordTypeAnnotationPositionalField node,
+  ) {
+    node.visitChildren(this);
+  }
+
+  @override
   void visitRedirectingConstructorInvocation(
       RedirectingConstructorInvocation node) {
     //
diff --git a/pkg/analyzer/lib/src/summary2/types_builder.dart b/pkg/analyzer/lib/src/summary2/types_builder.dart
index 06c100c..1234ef7 100644
--- a/pkg/analyzer/lib/src/summary2/types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/types_builder.dart
@@ -205,7 +205,7 @@
     } else if (node is MixinDeclaration) {
       _mixinDeclaration(node);
     } else if (node is RecordTypeAnnotationImpl) {
-      _recordTypeAnnotation(node);
+      recordTypeAnnotation(node);
     } else if (node is SimpleFormalParameter) {
       var element = node.declaredElement as ParameterElementImpl;
       element.type = node.type?.type ?? _dynamicType;
@@ -327,33 +327,6 @@
     }
   }
 
-  void _recordTypeAnnotation(RecordTypeAnnotationImpl node) {
-    final positionalFields = node.positionalFields.map((field) {
-      return RecordPositionalFieldElementImpl(
-        name: field.name?.lexeme,
-        nameOffset: -1,
-        type: field.type.typeOrThrow,
-      );
-    }).toList();
-
-    final namedFields = node.namedFields?.fields.map((field) {
-      return RecordNamedFieldElementImpl(
-        name: field.name.lexeme,
-        nameOffset: -1,
-        type: field.type.typeOrThrow,
-      );
-    }).toList();
-
-    node.type = RecordElementImpl(
-      positionalFields: positionalFields,
-      namedFields: namedFields ?? const [],
-    ).instantiate(
-      nullabilitySuffix: node.question != null
-          ? NullabilitySuffix.question
-          : NullabilitySuffix.none,
-    );
-  }
-
   void _superFormalParameter(SuperFormalParameter node) {
     var element = node.declaredElement as SuperFormalParameterElementImpl;
     var parameterList = node.parameters;
@@ -380,6 +353,33 @@
         .toList();
   }
 
+  static void recordTypeAnnotation(RecordTypeAnnotationImpl node) {
+    final positionalFields = node.positionalFields.map((field) {
+      return RecordPositionalFieldElementImpl(
+        name: field.name?.lexeme,
+        nameOffset: -1,
+        type: field.type.typeOrThrow,
+      );
+    }).toList();
+
+    final namedFields = node.namedFields?.fields.map((field) {
+      return RecordNamedFieldElementImpl(
+        name: field.name.lexeme,
+        nameOffset: -1,
+        type: field.type.typeOrThrow,
+      );
+    }).toList();
+
+    node.type = RecordElementImpl(
+      positionalFields: positionalFields,
+      namedFields: namedFields ?? const [],
+    ).instantiate(
+      nullabilitySuffix: node.question != null
+          ? NullabilitySuffix.question
+          : NullabilitySuffix.none,
+    );
+  }
+
   /// The [FunctionType] to use when a function type is expected for a type
   /// alias, but the actual provided type annotation is not a function type.
   static FunctionTypeImpl _errorFunctionType() {
diff --git a/pkg/analyzer/test/src/dart/resolution/record_type_annotation_test.dart b/pkg/analyzer/test/src/dart/resolution/record_type_annotation_test.dart
new file mode 100644
index 0000000..a228671
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/record_type_annotation_test.dart
@@ -0,0 +1,376 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(RecordTypeAnnotationTest);
+  });
+}
+
+@reflectiveTest
+class RecordTypeAnnotationTest extends PubPackageResolutionTest {
+  test_class_method_formalParameter() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  void foo((int, String) a) {}
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+
+  test_class_method_returnType() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  (int, String) foo() => throw 0;
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+
+  test_localFunction_formalParameter() async {
+    await assertNoErrorsInCode(r'''
+void f() {
+  // ignore:unused_element
+  void g((int, String) a) {}
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+
+  test_localFunction_returnType() async {
+    await assertNoErrorsInCode(r'''
+void f() {
+  // ignore:unused_element
+  (int, String) g() => throw 0;
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+
+  test_localVariable_mixed() async {
+    await assertNoErrorsInCode(r'''
+void f() {
+  // ignore:unused_local_variable
+  (int, String, {bool f3}) x;
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  namedFields: RecordTypeAnnotationNamedFields
+    leftBracket: {
+    fields
+      RecordTypeAnnotationNamedField
+        type: NamedType
+          name: SimpleIdentifier
+            token: bool
+            staticElement: dart:core::@class::bool
+            staticType: null
+          type: bool
+        name: f3
+    rightBracket: }
+  rightParenthesis: )
+  type: (int, String, {bool f3})
+''');
+  }
+
+  test_localVariable_named() async {
+    await assertNoErrorsInCode(r'''
+void f() {
+  // ignore:unused_local_variable
+  ({int f1, String f2}) x;
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('({int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  namedFields: RecordTypeAnnotationNamedFields
+    leftBracket: {
+    fields
+      RecordTypeAnnotationNamedField
+        type: NamedType
+          name: SimpleIdentifier
+            token: int
+            staticElement: dart:core::@class::int
+            staticType: null
+          type: int
+        name: f1
+      RecordTypeAnnotationNamedField
+        type: NamedType
+          name: SimpleIdentifier
+            token: String
+            staticElement: dart:core::@class::String
+            staticType: null
+          type: String
+        name: f2
+    rightBracket: }
+  rightParenthesis: )
+  type: ({int f1, String f2})
+''');
+  }
+
+  test_localVariable_positional() async {
+    await assertNoErrorsInCode(r'''
+void f() {
+  // ignore:unused_local_variable
+  (int, String) x;
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+
+  test_topFunction_formalParameter() async {
+    await assertNoErrorsInCode(r'''
+void f((int, String) a) {}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+
+  test_topFunction_nullable() async {
+    await assertNoErrorsInCode(r'''
+(int, String)? f() => throw 0;
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  question: ?
+  type: (int, String)?
+''');
+  }
+
+  test_topFunction_returnType() async {
+    await assertNoErrorsInCode(r'''
+(int, String) f() => throw 0;
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/49769')
+  test_typeArgument() async {
+    await assertNoErrorsInCode(r'''
+void f() {
+  // ignore:unused_local_variable
+  final x = <(int, String)>[];
+}
+''');
+
+    final node = findNode.recordTypeAnnotation('(int');
+    assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+  leftParenthesis: (
+  positionalFields
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: int
+          staticElement: dart:core::@class::int
+          staticType: null
+        type: int
+    RecordTypeAnnotationPositionalField
+      type: NamedType
+        name: SimpleIdentifier
+          token: String
+          staticElement: dart:core::@class::String
+          staticType: null
+        type: String
+  rightParenthesis: )
+  type: (int, String)
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index c6c8dca..346c51d 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -63,6 +63,7 @@
 import 'prefixed_identifier_test.dart' as prefixed_identifier;
 import 'property_access_test.dart' as property_access;
 import 'record_literal_test.dart' as record_literal;
+import 'record_type_annotation_test.dart' as record_type_annotation;
 import 'simple_identifier_test.dart' as simple_identifier;
 import 'super_formal_parameter_test.dart' as super_formal_parameter;
 import 'top_level_variable_test.dart' as top_level_variable;
@@ -131,6 +132,7 @@
     prefixed_identifier.main();
     property_access.main();
     record_literal.main();
+    record_type_annotation.main();
     simple_identifier.main();
     super_formal_parameter.main();
     top_level_variable.main();
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index 4320431..84a97b1 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -975,7 +975,7 @@
     _writeln('RecordTypeAnnotation');
     _withIndent(() {
       _writeNamedChildEntities(node);
-      _writeType('staticType', node.type);
+      _writeType('type', node.type);
     });
   }
 
diff --git a/tests/language_2/record_type_test.dart b/tests/language_2/record_type_test.dart
index 643c6cb..2ab7375 100644
--- a/tests/language_2/record_type_test.dart
+++ b/tests/language_2/record_type_test.dart
@@ -7,7 +7,6 @@
 main() {
   (int, int) record1 = (1, 2);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                     ^
@@ -18,7 +17,6 @@
   // [cfe] This expression has type 'void' and can't be used.
   (int x, int y) record1Named = (1, 2);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                              ^
@@ -30,7 +28,6 @@
 
   (int, int, ) record2 = (1, 2);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                       ^
@@ -42,7 +39,6 @@
 
   (int x, int y, ) record2Named = (1, 2);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                                ^
@@ -54,7 +50,6 @@
 
   (int, int, {int a, int b}) record3 = (1, 2, a: 3, b: 4);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                                     ^
@@ -66,7 +61,6 @@
 
   (int x, int y, {int a, int b}) record3Named = (1, 2, a: 3, b: 4);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                                              ^
@@ -78,7 +72,6 @@
 
   (int, int, {int a, int b, }) record4 = (1, 2, a: 3, b: 4);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                                       ^
@@ -90,7 +83,6 @@
 
   (int x, int y, {int a, int b, }) record4Named = (1, 2, a: 3, b: 4);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                                                ^
@@ -113,15 +105,12 @@
   // [cfe] This expression has type 'void' and can't be used.
   (int, int) Function ((int, int) a) z1 = ((int, int) a) { return (42, 42); };
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                     ^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                                         ^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                                                                ^
@@ -131,11 +120,9 @@
 
 (int, int) foo((int, {bool b}) inputRecord, int x) {
 // [error column 1, length 1]
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //             ^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
   if (inputRecord.b) return (42, 42);
@@ -155,7 +142,6 @@
 class Bar {
   (int, int) foo(int x) => (42, 42);
 //^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
 // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
 // [cfe] This requires the experimental 'records' language feature to be enabled.
 //                         ^