Implement AstBuilder support for record literals

Change-Id: I54f7a04d240f613185d5b114802fb1da4f974269
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255260
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/dart/analysis/features.dart b/pkg/analyzer/lib/dart/analysis/features.dart
index effa404..390c532 100644
--- a/pkg/analyzer/lib/dart/analysis/features.dart
+++ b/pkg/analyzer/lib/dart/analysis/features.dart
@@ -40,6 +40,9 @@
   /// Feature information for macros.
   static final macros = ExperimentalFeatures.macros;
 
+  /// Feature information for records.
+  static final records = ExperimentalFeatures.records;
+
   /// Feature information for spread collections.
   static final spread_collections = ExperimentalFeatures.spread_collections;
 
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 44e0f2b..8baf433 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -155,6 +155,9 @@
   /// `true` if macros are enabled
   final bool enableMacros;
 
+  /// `true` if records are enabled
+  final bool enableRecords;
+
   final FeatureSet _featureSet;
 
   final LineInfo _lineInfo;
@@ -180,6 +183,7 @@
         enableSuperParameters = _featureSet.isEnabled(Feature.super_parameters),
         enableEnhancedEnums = _featureSet.isEnabled(Feature.enhanced_enums),
         enableMacros = _featureSet.isEnabled(Feature.macros),
+        enableRecords = _featureSet.isEnabled(Feature.records),
         uri = uri ?? fileUri;
 
   NodeList<ClassMember> get currentDeclarationMembers {
@@ -2408,24 +2412,25 @@
   @override
   void endRecordLiteral(Token token, int count) {
     debugEvent("RecordLiteral");
-    // TODO: Actual implementation of record literals.
 
-    _reportFeatureNotEnabled(
-      feature: ExperimentalFeatures.records,
-      startToken: token,
-    );
-
-    // Pretend that the record literal is a list literal as the record literal
-    // isn't implemented yet.
+    if (!enableRecords) {
+      _reportFeatureNotEnabled(
+        feature: ExperimentalFeatures.records,
+        startToken: token,
+      );
+    }
 
     var elements = popTypedList<Expression>(count) ?? const [];
-
     List<Expression> expressions = <Expression>[];
     for (var elem in elements) {
       expressions.add(elem);
     }
 
-    push(ast.listLiteral(null, null, token, expressions, token));
+    push(RecordLiteralImpl(
+      leftParenthesis: token,
+      fields: expressions,
+      rightParenthesis: token.endGroup!,
+    ));
   }
 
   @override
@@ -3965,7 +3970,6 @@
   }
 
   @override
-  // TODO: Handle directly.
   void handleNamedRecordField(Token colon) => handleNamedArgument(colon);
 
   @override
diff --git a/pkg/analyzer/test/generated/utilities_test.dart b/pkg/analyzer/test/generated/utilities_test.dart
index 331ed22..0a2372c 100644
--- a/pkg/analyzer/test/generated/utilities_test.dart
+++ b/pkg/analyzer/test/generated/utilities_test.dart
@@ -1525,9 +1525,7 @@
     );
   }
 
-  @failingTest
   void test_recordLiteral() {
-    // Failing because record literals can't be parsed yet.
     var findNode = _parseStringToFindNode(r'''
 void f() {
   (1, 2);
diff --git a/pkg/analyzer/test/src/fasta/ast_builder_test.dart b/pkg/analyzer/test/src/fasta/ast_builder_test.dart
index fda8d7d..7707f8d 100644
--- a/pkg/analyzer/test/src/fasta/ast_builder_test.dart
+++ b/pkg/analyzer/test/src/fasta/ast_builder_test.dart
@@ -521,6 +521,32 @@
 ''');
   }
 
+  void test_recordLiteral() {
+    var parseResult = parseStringWithErrors(r'''
+void f() {
+  var r = (0, a: 1);
+}
+''');
+    parseResult.assertNoErrors();
+
+    var node = parseResult.findNode.recordLiteral('(0');
+    assertParsedNodeText(node, r'''
+RecordLiteral
+  leftParenthesis: (
+  fields
+    IntegerLiteral
+      literal: 0
+    NamedExpression
+      name: Label
+        label: SimpleIdentifier
+          token: a
+        colon: :
+      expression: IntegerLiteral
+        literal: 1
+  rightParenthesis: )
+''');
+  }
+
   void test_superFormalParameter() {
     var parseResult = parseStringWithErrors(r'''
 class A {
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index da47fbb..31859dc 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -962,6 +962,15 @@
   }
 
   @override
+  void visitRecordLiteral(RecordLiteral node) {
+    _writeln('RecordLiteral');
+    _withIndent(() {
+      _writeNamedChildEntities(node);
+      _writeType('staticType', node.staticType);
+    });
+  }
+
+  @override
   void visitRedirectingConstructorInvocation(
     RedirectingConstructorInvocation node,
   ) {
diff --git a/pkg/analyzer/test/util/feature_sets.dart b/pkg/analyzer/test/util/feature_sets.dart
index 53627ba..f925df7 100644
--- a/pkg/analyzer/test/util/feature_sets.dart
+++ b/pkg/analyzer/test/util/feature_sets.dart
@@ -40,6 +40,7 @@
       EnableString.enhanced_enums,
       EnableString.macros,
       EnableString.named_arguments_anywhere,
+      EnableString.records,
       EnableString.super_parameters,
     ],
   );