Эх сурвалжийг харах

Finish bracketing of parse nodes. (#2430)

- Finishes remaining "todo" parse nodes.
- Improving error recovery for invalid designators and structs, so that the parse tree still looks similar to a valid parse tree.
- Call expressions now have the thing being called as a child (of the start) instead of a sibling.
- Use of Start is replacing use of End in several parse nodes, like structs and call expressions.
- Adjusting documentation of parse node structures in an attempt to make it more consistent and understandable.
- The current state for interfaces and if/else is mostly being documented, not altered.
Jon Ross-Perkins 3 жил өмнө
parent
commit
6bfd202f4e

+ 1 - 2
toolchain/parser/parse_node_kind.cpp

@@ -47,8 +47,7 @@ auto ParseNodeKind::child_count() const -> int32_t {
 #include "toolchain/parser/parse_node_kind.def"
   };
   auto child_count = ChildCount[static_cast<int>(kind_)];
-  // TODO: Allows TodoFixParseNode; see parse_node_kind.def.
-  CARBON_CHECK(child_count != -1) << *this;
+  CARBON_CHECK(child_count >= 0) << *this;
   return child_count;
 }
 

+ 137 - 59
toolchain/parser/parse_node_kind.def

@@ -19,11 +19,6 @@
 //     must be correct even when the node contains errors.
 //
 // Macro definitions  will be removed at the end of this file to clean up.
-//
-// TODO: Using CHILD_COUNT(..., TodoFixParseNode) to indicate nodes which need to be fixed,
-// sometimes due to inconsistent output in error modes. This is also handled in
-// ParseNodeKind::child_count() and ParseTree::Verify(). All of that can be
-// cleaned up once remainining nodes are fixed.
 
 #if !(defined(CARBON_PARSE_NODE_KIND) ||          \
       (defined(CARBON_PARSE_NODE_KIND_BRACKET) && \
@@ -52,8 +47,8 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(DeclaredName, 0)
 
 // `package`:
 //   PackageIntroducer
-//   (name)
-//     (library)
+//   _external_: DeclaredName
+//     _external_: Literal
 //   PackageLibrary
 //   PackageApi or PackageImpl
 // PackageDirective
@@ -65,24 +60,23 @@ CARBON_PARSE_NODE_KIND_BRACKET(PackageDirective, PackageIntroducer)
 
 // A code block:
 //   CodeBlockStart
-//   (statements)
+//   _external_: statements
 // CodeBlock
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(CodeBlockStart, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(CodeBlock, CodeBlockStart)
 
-
 // `fn`:
 //     FunctionIntroducer
 //     DeclaredName
-//     (parameter list)
-//       (expression)
+//     _external_: ParameterList
+//       _external_: type expression
 //     ReturnType
 //   FunctionDefinitionStart
-//   (statements)
+//   _external_: statements
 // FunctionDefinition
 //
-// Or, for a declaration, FunctionDeclaration will end it where
-// FunctionDefinitionStart is for a definition.
+// The above is a definition; for a declaration, FunctionDeclaration will end it
+// where FunctionDefinitionStart is for a definition.
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(FunctionIntroducer, 0)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnType, 1)
 CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinitionStart, FunctionIntroducer)
@@ -91,22 +85,26 @@ CARBON_PARSE_NODE_KIND_BRACKET(FunctionDeclaration, FunctionIntroducer)
 
 // A parameter list:
 //   ParamertListStart
-//   (expressions separated by ParameterListComma)
+//   _external_: expression
+//   ParameterListComma
 // ParameterList
+//
+// Expressions and ParameterListComma may repeat with ParameterListComma as a
+// separator.
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParameterListStart, 0)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParameterListComma, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(ParameterList, ParameterListStart)
 
 // A pattern binding, such as `name: Type`:
 //   DeclaredName
-//   (type expression)
+//   _external_: type expression
 // PatternBinding
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(PatternBinding, 2)
 
 // `var`:
 //   VariableIntroducer
-//   (pattern)
-//     (expression)
+//   _external_: PatternBinding
+//     _external_: expression
 //   optional VariableInitializer
 // VariableDeclaration
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableIntroducer, 0)
@@ -114,98 +112,178 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableInitializer, 1)
 CARBON_PARSE_NODE_KIND_BRACKET(VariableDeclaration, VariableIntroducer)
 
 // An expression statement:
-//   (expression)
-// Statement
+//   _external_: expression
+// ExpressionStatement
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExpressionStatement, 1)
 
 // `break`:
 //   BreakStatementStart
 // BreakStatement
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(BreakStatement, 1)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(BreakStatementStart, 0)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(BreakStatement, 1)
 
 // `continue`:
 //   ContinueStatementStart
 // ContinueStatement
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(ContinueStatement, 1)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ContinueStatementStart, 0)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ContinueStatement, 1)
 
 // `return`:
 //   ReturnStatementStart
-//   (optional expression)
+//   _external_: expression
 // ReturnStatement
-CARBON_PARSE_NODE_KIND_BRACKET(ReturnStatement, ReturnStatementStart)
+//
+// The child expression is optional.
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnStatementStart, 0)
+CARBON_PARSE_NODE_KIND_BRACKET(ReturnStatement, ReturnStatementStart)
 
 // `for`:
 //     ForHeaderStart
-//       (var)
-//     ForIn (versus a normal `var`, replaces VariableDeclaration)
-//     (expression)
+//       VariableIntroducer
+//       _external_: PatternBinding
+//         _external_: type expression
+//     ForIn
+//     _external_: expression
 //   ForHeader
-//     CodeBlockStart
-//     (statements)
-//   CodeBlock
+//   _external_: CodeBlock
 // ForStatement
+//
+// Versus a normal `var`, ForIn replaces VariableDeclaration.
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForHeaderStart, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(ForIn, VariableIntroducer)
 CARBON_PARSE_NODE_KIND_BRACKET(ForHeader, ForHeaderStart)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForStatement, 2)
 
-// `if`:
-// TODO: Still thinking about structure.
-//   IfConditionStart
-//   (expression)
-// IfCondition
+// `if` + `else`:
+//     IfConditionStart
+//     _external_: expression
+//   IfCondition
+//   _external_: CodeBlock
+//   IfStatementElse
+//   _external_: CodeBlock or IfStatement
+// IfStatement
+//
+// IfStatementElse and the following node are optional based on `else` presence.
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfConditionStart, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(IfCondition, IfConditionStart)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfStatement, TodoFixParseNode)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfStatementElse, 0)
+CARBON_PARSE_NODE_KIND_BRACKET(IfStatement, IfCondition)
 
 // `while`:
 //     WhileConditionStart
-//     (expression)
+//     _external_: expression
 //   WhileCondition
-//     CodeBlockStart
-//     (statements)
-//   CodeBlock
+//   _external_: CodeBlock
 // WhileStatement
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(WhileConditionStart, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(WhileCondition, WhileConditionStart)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(WhileStatement, 2)
 
-// Expressions.
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(Literal, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteral, TodoFixParseNode)
+// Parenthesized expressions:
+//   ParenExpressionOrTupleLiteralStart
+//   _external_: expression
+// ParenExpression
+
+// Tuples:
+//   ParenExpressionOrTupleLiteralStart
+//   _external_: expression
+//   TupleLiteralComma
+// TupleLiteral
+//
+// Expressions and TupleLiteralComma may repeat with TupleLiteralComma as a
+// separator.
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParenExpressionOrTupleLiteralStart, 0)
+CARBON_PARSE_NODE_KIND_BRACKET(ParenExpression,
+                               ParenExpressionOrTupleLiteralStart)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteralComma, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteralEnd, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(NameReference, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParenExpression, TodoFixParseNode)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParenExpressionEnd, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(DesignatorExpression, TodoFixParseNode)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(DesignatedName, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExpression, TodoFixParseNode)
+CARBON_PARSE_NODE_KIND_BRACKET(TupleLiteral, ParenExpressionOrTupleLiteralStart)
+
+// Call expressions, such as `a()`:
+//     _external_: expression
+//   CallExpressionStart
+//   _external_: expression
+//   CallExpressionComma
+// CallExpression
+//
+// Expressions and CallExpressionComma may repeat with CallExpressionComma as a
+// separator.
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExpressionStart, 1)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExpressionComma, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExpressionEnd, 0)
+CARBON_PARSE_NODE_KIND_BRACKET(CallExpression, CallExpressionStart)
+
+// A designator expression, such as `a.b`:
+//   _external_: DesignatedName or lhs expression
+//   _external_: DesignatedName
+// DesignatorExpression
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(DesignatedName, 0)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(DesignatorExpression, 2)
+
+// A literal.
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(Literal, 0)
+
+// A reference to an identifier.
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(NameReference, 0)
+
+// A prefix operator:
+//   _external_: expression
+// PrefixOperator
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(PrefixOperator, 1)
+
+// An infix operator:
+//   _external_: lhs expression
+//   _external_: rhs expression
+// InfixOperator
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2)
+
+// A postfix operator:
+//   _external_: expression
+// PostfixOperator
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(PostfixOperator, 1)
 
-// Struct literals.
+// Struct literals, such as `{.a = 0}`:
+//   StructLiteralOrStructTypeLiteralStart
+//       _external_: DesignatedName
+//     StructFieldDesignator
+//     _external_: expression
+//   StructFieldValue
+//   StructComma
+// StructLiteral
+//
+//
+// Struct type literals, such as `{.a: i32}`:
+//       _external_: DesignatedName
+//     StructFieldDesignator
+//     _external_: type expression
+//   StructFieldType
+//   StructComma
+// StructTypeLiteral
+//
+// Elements (StructFieldValue and StructFieldType, respectively) and StructComma
+// may repeat with StructComma as a separator.
+//
+// When a valid StructFieldType or StructFieldValue cannot be formed, elements
+// may be replaced by StructFieldUnknown, which may have a preceding sibling
+// StructFieldDesignator if one was successfully parsed.
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructLiteralOrStructTypeLiteralStart, 0)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldDesignator, 1)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldValue, 2)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldType, 2)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldUnknown, 0)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructComma, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(StructLiteral,
                                StructLiteralOrStructTypeLiteralStart)
 CARBON_PARSE_NODE_KIND_BRACKET(StructTypeLiteral,
                                StructLiteralOrStructTypeLiteralStart)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructLiteralOrStructTypeLiteralStart, 0)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldDesignator, TodoFixParseNode)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldValue, TodoFixParseNode)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldType, TodoFixParseNode)
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructComma, 0)
 
-// Interfaces
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(InterfaceDefinition, TodoFixParseNode)
+// `interface`:
+//   _external_: DeclaredName
+//     InterfaceBodyStart
+//     _external_: statements
+//   InterfaceBodyEnd
+// InterfaceDefinition
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(InterfaceBodyStart, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(InterfaceBody, InterfaceBodyStart)
+CARBON_PARSE_NODE_KIND_BRACKET(InterfaceDefinition, DeclaredName)
 
 #undef CARBON_PARSE_NODE_KIND
 #undef CARBON_PARSE_NODE_KIND_BRACKET

+ 0 - 3
toolchain/parser/parse_node_kind.h

@@ -33,9 +33,6 @@ class ParseNodeKind {
   };
 
  public:
-  // TODO: See parse_node_kind.def.
-  static constexpr int32_t TodoFixParseNode = -2;
-
   // `clang-format` has a bug with spacing around `->` returns in macros. See
   // https://bugs.llvm.org/show_bug.cgi?id=48320 for details.
 #define CARBON_PARSE_NODE_KIND(Name)            \

+ 1 - 6
toolchain/parser/parse_tree.cpp

@@ -26,7 +26,7 @@ auto ParseTree::Parse(TokenizedBuffer& tokens, DiagnosticConsumer& consumer)
   // Delegate to the parser.
   auto tree = Parser::Parse(tokens, emitter);
   auto verify_error = tree.Verify();
-  CARBON_CHECK(!verify_error) << *verify_error;
+  CARBON_CHECK(!verify_error) << tree << *verify_error;
   return tree;
 }
 
@@ -215,11 +215,6 @@ auto ParseTree::Verify() const -> llvm::Optional<Error> {
           break;
         }
       }
-    } else if (n_impl.kind.child_count() == ParseNodeKind::TodoFixParseNode) {
-      while (subtree_size < n_impl.subtree_size && !nodes.empty()) {
-        auto child_impl = node_impls_[nodes.pop_back_val().index()];
-        subtree_size += child_impl.subtree_size;
-      }
     } else {
       for (int i = 0; i < n_impl.kind.child_count(); ++i) {
         if (nodes.empty()) {

+ 39 - 24
toolchain/parser/parser.cpp

@@ -157,6 +157,12 @@ auto Parser::ConsumeAndAddLeafNodeIf(TokenKind token_kind,
   return true;
 }
 
+auto Parser::ConsumeChecked(TokenKind kind) -> TokenizedBuffer::Token {
+  CARBON_CHECK(PositionIs(kind))
+      << "Required " << kind.Name() << ", found " << PositionKind().Name();
+  return Consume();
+}
+
 auto Parser::ConsumeIf(TokenKind kind)
     -> llvm::Optional<TokenizedBuffer::Token> {
   if (!PositionIs(kind)) {
@@ -598,9 +604,15 @@ auto Parser::HandleBraceExpressionParameterFinish(BraceExpressionKind kind)
     -> void {
   auto state = PopState();
 
-  AddNode(kind == BraceExpressionKind::Type ? ParseNodeKind::StructFieldType()
-                                            : ParseNodeKind::StructFieldValue(),
-          state.token, state.subtree_start, state.has_error);
+  if (state.has_error) {
+    AddLeafNode(ParseNodeKind::StructFieldUnknown(), state.token,
+                /*has_error=*/true);
+  } else {
+    AddNode(kind == BraceExpressionKind::Type
+                ? ParseNodeKind::StructFieldType()
+                : ParseNodeKind::StructFieldValue(),
+            state.token, state.subtree_start, /*has_error=*/false);
+  }
 
   if (ConsumeListToken(ParseNodeKind::StructComma(),
                        TokenKind::CloseCurlyBrace(),
@@ -647,12 +659,11 @@ auto Parser::HandleBraceExpressionFinishAsUnknownState() -> void {
 auto Parser::HandleCallExpressionState() -> void {
   auto state = PopState();
 
-  // TODO: When swapping () start/end, this should AddLeafNode the open before
-  // continuing.
   state.state = ParserState::CallExpressionFinish();
   PushState(state);
-  // Advance past the open paren.
-  ++position_;
+
+  AddNode(ParseNodeKind::CallExpressionStart(), Consume(), state.subtree_start,
+          state.has_error);
   if (!PositionIs(TokenKind::CloseParen())) {
     PushState(ParserState::CallExpressionParameterFinish());
     PushState(ParserState::Expression());
@@ -677,8 +688,7 @@ auto Parser::HandleCallExpressionParameterFinishState() -> void {
 auto Parser::HandleCallExpressionFinishState() -> void {
   auto state = PopState();
 
-  AddLeafNode(ParseNodeKind::CallExpressionEnd(), Consume());
-  AddNode(ParseNodeKind::CallExpression(), state.token, state.subtree_start,
+  AddNode(ParseNodeKind::CallExpression(), Consume(), state.subtree_start,
           state.has_error);
 }
 
@@ -744,8 +754,8 @@ auto Parser::HandleDesignator(bool as_struct) -> void {
   auto state = PopState();
 
   // `.` identifier
-  auto dot = ConsumeIf(TokenKind::Period());
-  CARBON_CHECK(dot);
+  auto dot = ConsumeChecked(TokenKind::Period());
+
   if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier(),
                                ParseNodeKind::DesignatedName())) {
     CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDot, Error,
@@ -757,14 +767,17 @@ auto Parser::HandleDesignator(bool as_struct) -> void {
       AddLeafNode(ParseNodeKind::DesignatedName(), Consume(),
                   /*has_error=*/true);
     } else {
-      state.has_error = true;
+      AddLeafNode(ParseNodeKind::DesignatedName(), *position_,
+                  /*has_error=*/true);
+      // Indicate the error to the parent state so that it can avoid producing
+      // more errors.
       ReturnErrorOnState();
     }
   }
 
   AddNode(as_struct ? ParseNodeKind::StructFieldDesignator()
                     : ParseNodeKind::DesignatorExpression(),
-          *dot, state.subtree_start, state.has_error);
+          dot, state.subtree_start, state.has_error);
 }
 
 auto Parser::HandleDesignatorAsExpressionState() -> void {
@@ -1044,8 +1057,8 @@ auto Parser::HandleFunctionParameterFinishState() -> void {
 auto Parser::HandleFunctionParameterListFinishState() -> void {
   auto state = PopState();
 
-  CARBON_CHECK(PositionIs(TokenKind::CloseParen())) << PositionKind().Name();
-  AddNode(ParseNodeKind::ParameterList(), Consume(), state.subtree_start,
+  AddNode(ParseNodeKind::ParameterList(),
+          ConsumeChecked(TokenKind::CloseParen()), state.subtree_start,
           state.has_error);
 }
 
@@ -1226,12 +1239,10 @@ auto Parser::HandleParenConditionFinishAsWhileState() -> void {
 auto Parser::HandleParenExpressionState() -> void {
   auto state = PopState();
 
-  // TODO: When swapping () start/end, this should AddLeafNode the open before
-  // continuing.
-
   // Advance past the open paren.
-  CARBON_CHECK(PositionIs(TokenKind::OpenParen()));
-  ++position_;
+  AddLeafNode(ParseNodeKind::ParenExpressionOrTupleLiteralStart(),
+              ConsumeChecked(TokenKind::OpenParen()));
+
   if (PositionIs(TokenKind::CloseParen())) {
     state.state = ParserState::ParenExpressionFinishAsTuple();
     PushState(state);
@@ -1284,16 +1295,14 @@ auto Parser::HandleParenExpressionParameterFinishAsTupleState() -> void {
 auto Parser::HandleParenExpressionFinishState() -> void {
   auto state = PopState();
 
-  AddLeafNode(ParseNodeKind::ParenExpressionEnd(), Consume());
-  AddNode(ParseNodeKind::ParenExpression(), state.token, state.subtree_start,
+  AddNode(ParseNodeKind::ParenExpression(), Consume(), state.subtree_start,
           state.has_error);
 }
 
 auto Parser::HandleParenExpressionFinishAsTupleState() -> void {
   auto state = PopState();
 
-  AddLeafNode(ParseNodeKind::TupleLiteralEnd(), Consume());
-  AddNode(ParseNodeKind::TupleLiteral(), state.token, state.subtree_start,
+  AddNode(ParseNodeKind::TupleLiteral(), Consume(), state.subtree_start,
           state.has_error);
 }
 
@@ -1659,6 +1668,12 @@ auto Parser::HandleInterfaceIntroducerState() -> void {
                       "Expected interface name after `interface` keyword.");
     emitter_->Emit(*position_, ExpectedInterfaceName);
     state.has_error = true;
+
+    // Add a name node even when it's not present because it's used for subtree
+    // bracketing on interfaces.
+    // TODO: Either fix this or normalize it, still deciding on the right
+    // approach.
+    AddLeafNode(ParseNodeKind::DeclaredName(), state.token, /*has_error=*/true);
   }
 
   bool parse_body = true;

+ 4 - 0
toolchain/parser/parser.h

@@ -124,6 +124,10 @@ class Parser {
   auto ConsumeAndAddLeafNodeIf(TokenKind token_kind, ParseNodeKind node_kind)
       -> bool;
 
+  // Returns the current position and moves past it. Requires the token is the
+  // expected kind.
+  auto ConsumeChecked(TokenKind kind) -> TokenizedBuffer::Token;
+
   // If the current position's token matches this `Kind`, returns it and
   // advances to the next position. Otherwise returns an empty optional.
   auto ConsumeIf(TokenKind kind) -> llvm::Optional<TokenizedBuffer::Token>;

+ 7 - 5
toolchain/parser/testdata/basics/fail_invalid_designators.carbon

@@ -11,16 +11,18 @@
 // CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:   {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'a'},
-// CHECK:STDOUT:     {kind: 'DesignatorExpression', text: '.', has_error: yes, subtree_size: 2},
-// CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'DesignatedName', text: ';', has_error: yes},
+// CHECK:STDOUT:     {kind: 'DesignatorExpression', text: '.', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', has_error: yes, subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'a'},
 // CHECK:STDOUT:       {kind: 'DesignatedName', text: 'fn', has_error: yes},
 // CHECK:STDOUT:     {kind: 'DesignatorExpression', text: '.', subtree_size: 3},
 // CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'a'},
-// CHECK:STDOUT:     {kind: 'DesignatorExpression', text: '.', has_error: yes, subtree_size: 2},
-// CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16},
+// CHECK:STDOUT:       {kind: 'DesignatedName', text: '42', has_error: yes},
+// CHECK:STDOUT:     {kind: 'DesignatorExpression', text: '.', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT: ]
 

+ 2 - 2
toolchain/parser/testdata/basics/fail_paren_match_regression.carbon

@@ -6,9 +6,9 @@
 // RUN: %{not} %{carbon-run-parser}
 // CHECK:STDOUT: [
 // CHECK:STDOUT:   {kind: 'VariableIntroducer', text: 'var'},
+// CHECK:STDOUT:       {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'foo'},
-// CHECK:STDOUT:       {kind: 'ParenExpressionEnd', text: ')'},
-// CHECK:STDOUT:     {kind: 'ParenExpression', text: '(', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParenExpression', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:   {kind: 'VariableInitializer', text: '=', subtree_size: 4},
 // CHECK:STDOUT: {kind: 'VariableDeclaration', text: 'var', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},

+ 17 - 17
toolchain/parser/testdata/basics/function_call.carbon

@@ -10,24 +10,24 @@
 // CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
 // CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:   {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:               {kind: 'NameReference', text: 'a'},
-// CHECK:STDOUT:               {kind: 'DesignatedName', text: 'b'},
+// CHECK:STDOUT:                   {kind: 'NameReference', text: 'a'},
+// CHECK:STDOUT:                   {kind: 'DesignatedName', text: 'b'},
+// CHECK:STDOUT:                 {kind: 'DesignatorExpression', text: '.', subtree_size: 3},
+// CHECK:STDOUT:                 {kind: 'DesignatedName', text: 'f'},
+// CHECK:STDOUT:               {kind: 'DesignatorExpression', text: '.', subtree_size: 5},
+// CHECK:STDOUT:             {kind: 'CallExpressionStart', text: '(', subtree_size: 6},
+// CHECK:STDOUT:               {kind: 'NameReference', text: 'c'},
+// CHECK:STDOUT:               {kind: 'DesignatedName', text: 'd'},
 // CHECK:STDOUT:             {kind: 'DesignatorExpression', text: '.', subtree_size: 3},
-// CHECK:STDOUT:             {kind: 'DesignatedName', text: 'f'},
-// CHECK:STDOUT:           {kind: 'DesignatorExpression', text: '.', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'NameReference', text: 'c'},
-// CHECK:STDOUT:             {kind: 'DesignatedName', text: 'd'},
-// CHECK:STDOUT:           {kind: 'DesignatorExpression', text: '.', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'CallExpressionComma', text: ','},
-// CHECK:STDOUT:             {kind: 'NameReference', text: 'e'},
-// CHECK:STDOUT:             {kind: 'ParenExpressionEnd', text: ')'},
-// CHECK:STDOUT:           {kind: 'ParenExpression', text: '(', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:         {kind: 'CallExpression', text: '(', subtree_size: 14},
-// CHECK:STDOUT:         {kind: 'DesignatedName', text: 'g'},
-// CHECK:STDOUT:       {kind: 'DesignatorExpression', text: '.', subtree_size: 16},
-// CHECK:STDOUT:       {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:     {kind: 'CallExpression', text: '(', subtree_size: 18},
+// CHECK:STDOUT:             {kind: 'CallExpressionComma', text: ','},
+// CHECK:STDOUT:               {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
+// CHECK:STDOUT:               {kind: 'NameReference', text: 'e'},
+// CHECK:STDOUT:             {kind: 'ParenExpression', text: ')', subtree_size: 3},
+// CHECK:STDOUT:           {kind: 'CallExpression', text: ')', subtree_size: 14},
+// CHECK:STDOUT:           {kind: 'DesignatedName', text: 'g'},
+// CHECK:STDOUT:         {kind: 'DesignatorExpression', text: '.', subtree_size: 16},
+// CHECK:STDOUT:       {kind: 'CallExpressionStart', text: '(', subtree_size: 17},
+// CHECK:STDOUT:     {kind: 'CallExpression', text: ')', subtree_size: 18},
 // CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', subtree_size: 19},
 // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 25},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},

+ 12 - 12
toolchain/parser/testdata/basics/tuples.carbon

@@ -7,31 +7,31 @@
 // CHECK:STDOUT: [
 // CHECK:STDOUT:   {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:     {kind: 'DeclaredName', text: 'x'},
+// CHECK:STDOUT:       {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
 // CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
 // CHECK:STDOUT:       {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:       {kind: 'TupleLiteralEnd', text: ')'},
-// CHECK:STDOUT:     {kind: 'TupleLiteral', text: '(', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'TupleLiteral', text: ')', subtree_size: 5},
 // CHECK:STDOUT:   {kind: 'PatternBinding', text: ':', subtree_size: 7},
+// CHECK:STDOUT:       {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
 // CHECK:STDOUT:       {kind: 'Literal', text: '1'},
 // CHECK:STDOUT:       {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:       {kind: 'Literal', text: '2'},
-// CHECK:STDOUT:       {kind: 'TupleLiteralEnd', text: ')'},
-// CHECK:STDOUT:     {kind: 'TupleLiteral', text: '(', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'TupleLiteral', text: ')', subtree_size: 5},
 // CHECK:STDOUT:   {kind: 'VariableInitializer', text: '=', subtree_size: 6},
 // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 15},
 // CHECK:STDOUT:   {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:     {kind: 'DeclaredName', text: 'y'},
-// CHECK:STDOUT:         {kind: 'TupleLiteralEnd', text: ')'},
-// CHECK:STDOUT:       {kind: 'TupleLiteral', text: '(', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
+// CHECK:STDOUT:       {kind: 'TupleLiteral', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'TupleLiteralComma', text: ','},
-// CHECK:STDOUT:         {kind: 'TupleLiteralEnd', text: ')'},
-// CHECK:STDOUT:       {kind: 'TupleLiteral', text: '(', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
+// CHECK:STDOUT:       {kind: 'TupleLiteral', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'TupleLiteralComma', text: ','},
-// CHECK:STDOUT:         {kind: 'TupleLiteralEnd', text: ')'},
-// CHECK:STDOUT:       {kind: 'TupleLiteral', text: '(', subtree_size: 2},
-// CHECK:STDOUT:       {kind: 'TupleLiteralEnd', text: ')'},
-// CHECK:STDOUT:     {kind: 'TupleLiteral', text: '(', subtree_size: 10},
+// CHECK:STDOUT:         {kind: 'ParenExpressionOrTupleLiteralStart', text: '('},
+// CHECK:STDOUT:       {kind: 'TupleLiteral', text: ')', subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'TupleLiteral', text: ')', subtree_size: 10},
 // CHECK:STDOUT:   {kind: 'PatternBinding', text: ':', subtree_size: 12},
 // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 14},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},

+ 3 - 3
toolchain/parser/testdata/for/fail_colon_instead_of_in.carbon

@@ -19,10 +19,10 @@
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'y'},
 // CHECK:STDOUT:     {kind: 'ForHeader', text: ')', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'CodeBlockStart', text: '{'},
-// CHECK:STDOUT:           {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:             {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:           {kind: 'CallExpressionStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'NameReference', text: 'x'},
-// CHECK:STDOUT:           {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:         {kind: 'CallExpression', text: '(', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'CallExpression', text: ')', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'ExpressionStatement', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'CodeBlock', text: '}', subtree_size: 7},
 // CHECK:STDOUT:   {kind: 'ForStatement', text: 'for', subtree_size: 16},

+ 3 - 3
toolchain/parser/testdata/for/fail_missing_in.carbon

@@ -19,10 +19,10 @@
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'y'},
 // CHECK:STDOUT:     {kind: 'ForHeader', text: ')', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'CodeBlockStart', text: '{'},
-// CHECK:STDOUT:           {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:             {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:           {kind: 'CallExpressionStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'NameReference', text: 'x'},
-// CHECK:STDOUT:           {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:         {kind: 'CallExpression', text: '(', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'CallExpression', text: ')', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'ExpressionStatement', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'CodeBlock', text: '}', subtree_size: 7},
 // CHECK:STDOUT:   {kind: 'ForStatement', text: 'for', subtree_size: 16},

+ 3 - 3
toolchain/parser/testdata/for/fail_missing_var.carbon

@@ -14,10 +14,10 @@
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'y'},
 // CHECK:STDOUT:     {kind: 'ForHeader', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'CodeBlockStart', text: '{'},
-// CHECK:STDOUT:           {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:             {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:           {kind: 'CallExpressionStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'NameReference', text: 'x'},
-// CHECK:STDOUT:           {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:         {kind: 'CallExpression', text: '(', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'CallExpression', text: ')', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'ExpressionStatement', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'CodeBlock', text: '}', subtree_size: 7},
 // CHECK:STDOUT:   {kind: 'ForStatement', text: 'for', subtree_size: 11},

+ 3 - 3
toolchain/parser/testdata/for/nested.carbon

@@ -28,10 +28,10 @@
 // CHECK:STDOUT:           {kind: 'NameReference', text: 'y'},
 // CHECK:STDOUT:         {kind: 'ForHeader', text: ')', subtree_size: 8},
 // CHECK:STDOUT:           {kind: 'CodeBlockStart', text: '{'},
-// CHECK:STDOUT:               {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:                 {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:               {kind: 'CallExpressionStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'NameReference', text: 'z'},
-// CHECK:STDOUT:               {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:             {kind: 'CallExpression', text: '(', subtree_size: 4},
+// CHECK:STDOUT:             {kind: 'CallExpression', text: ')', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'ExpressionStatement', text: ';', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'CodeBlock', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'ForStatement', text: 'for', subtree_size: 16},

+ 3 - 3
toolchain/parser/testdata/for/simple.carbon

@@ -19,10 +19,10 @@
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'y'},
 // CHECK:STDOUT:     {kind: 'ForHeader', text: ')', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'CodeBlockStart', text: '{'},
-// CHECK:STDOUT:           {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:             {kind: 'NameReference', text: 'Print'},
+// CHECK:STDOUT:           {kind: 'CallExpressionStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'NameReference', text: 'x'},
-// CHECK:STDOUT:           {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:         {kind: 'CallExpression', text: '(', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'CallExpression', text: ')', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'ExpressionStatement', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'CodeBlock', text: '}', subtree_size: 7},
 // CHECK:STDOUT:   {kind: 'ForStatement', text: 'for', subtree_size: 16},

+ 3 - 3
toolchain/parser/testdata/function/definition/with_params.carbon

@@ -17,14 +17,14 @@
 // CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
 // CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 9},
 // CHECK:STDOUT:   {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12},
-// CHECK:STDOUT:       {kind: 'NameReference', text: 'foo'},
+// CHECK:STDOUT:         {kind: 'NameReference', text: 'foo'},
+// CHECK:STDOUT:       {kind: 'CallExpressionStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'NameReference', text: 'baz'},
 // CHECK:STDOUT:       {kind: 'CallExpressionComma', text: ','},
 // CHECK:STDOUT:         {kind: 'NameReference', text: 'bar'},
 // CHECK:STDOUT:         {kind: 'NameReference', text: 'baz'},
 // CHECK:STDOUT:       {kind: 'InfixOperator', text: '+', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:     {kind: 'CallExpression', text: '(', subtree_size: 8},
+// CHECK:STDOUT:     {kind: 'CallExpression', text: ')', subtree_size: 8},
 // CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', subtree_size: 9},
 // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 22},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},

+ 2 - 1
toolchain/parser/testdata/generics/interface/fail_missing_name.carbon

@@ -5,9 +5,10 @@
 // AUTOUPDATE
 // RUN: %{not} %{carbon-run-parser}
 // CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'interface', has_error: yes},
 // CHECK:STDOUT:     {kind: 'InterfaceBodyStart', text: '{'},
 // CHECK:STDOUT:   {kind: 'InterfaceBody', text: '}', subtree_size: 2},
-// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: 'interface', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: 'interface', has_error: yes, subtree_size: 4},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT: ]
 

+ 3 - 3
toolchain/parser/testdata/operators/fixity_in_call.carbon

@@ -10,15 +10,15 @@
 // CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
 // CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:   {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:       {kind: 'NameReference', text: 'G'},
+// CHECK:STDOUT:         {kind: 'NameReference', text: 'G'},
+// CHECK:STDOUT:       {kind: 'CallExpressionStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
 // CHECK:STDOUT:       {kind: 'PostfixOperator', text: '*', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'CallExpressionComma', text: ','},
 // CHECK:STDOUT:         {kind: 'NameReference', text: 'n'},
 // CHECK:STDOUT:         {kind: 'NameReference', text: 'n'},
 // CHECK:STDOUT:       {kind: 'InfixOperator', text: '*', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'CallExpressionEnd', text: ')'},
-// CHECK:STDOUT:     {kind: 'CallExpression', text: '(', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'CallExpression', text: ')', subtree_size: 9},
 // CHECK:STDOUT:   {kind: 'ExpressionStatement', text: ';', subtree_size: 10},
 // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},