Просмотр исходного кода

Extend deduced and regular parameter handling to types. (#2684)

This makes it possible to specify both deduced and regular parameters on types. It reorganizes the handling of parameter lists in order to allow more reuse of code in this approach. Both functions and types use the new DeclarationNameAndParams handling. Overall the goal here is to take advantage of commonality in structure.

Regarding destructors, the likely approach would be to use ParameterListAsDeduced directly because `destructor` is a keyword with no declaration name and no regular parameters.
Jon Ross-Perkins 3 лет назад
Родитель
Сommit
7d553107dd
31 измененных файлов с 831 добавлено и 415 удалено
  1. 2 1
      .codespell_ignore
  2. 2 1
      toolchain/diagnostics/diagnostic_kind.def
  3. 185 134
      toolchain/parser/parser.cpp
  4. 29 4
      toolchain/parser/parser.h
  5. 68 75
      toolchain/parser/parser_state.def
  6. 3 6
      toolchain/parser/testdata/function/declaration/deduced_empty.carbon
  7. 0 22
      toolchain/parser/testdata/function/declaration/deduced_one_suffix_comma.carbon
  8. 0 0
      toolchain/parser/testdata/function/declaration/deduced_params.carbon
  9. 0 41
      toolchain/parser/testdata/function/declaration/deduced_six.carbon
  10. 0 26
      toolchain/parser/testdata/function/declaration/deduced_two_suffix_comma.carbon
  11. 1 1
      toolchain/parser/testdata/function/declaration/fail_identifier_instead_of_sig.carbon
  12. 1 1
      toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon
  13. 1 1
      toolchain/parser/testdata/function/declaration/fail_no_sig_or_semi.carbon
  14. 0 0
      toolchain/parser/testdata/function/declaration/params.carbon
  15. 0 19
      toolchain/parser/testdata/function/declaration/params_one.carbon
  16. 0 20
      toolchain/parser/testdata/function/declaration/params_one_suffix_comma.carbon
  17. 0 39
      toolchain/parser/testdata/function/declaration/params_six.carbon
  18. 0 24
      toolchain/parser/testdata/function/declaration/params_two_suffix_comma.carbon
  19. 28 0
      toolchain/parser/testdata/generics/deduced_params/empty.carbon
  20. 47 0
      toolchain/parser/testdata/generics/deduced_params/fail_no_parens.carbon
  21. 34 0
      toolchain/parser/testdata/generics/deduced_params/one.carbon
  22. 36 0
      toolchain/parser/testdata/generics/deduced_params/one_suffix_comma.carbon
  23. 74 0
      toolchain/parser/testdata/generics/deduced_params/six.carbon
  24. 42 0
      toolchain/parser/testdata/generics/deduced_params/two.carbon
  25. 44 0
      toolchain/parser/testdata/generics/deduced_params/two_suffix_comma.carbon
  26. 24 0
      toolchain/parser/testdata/generics/params/empty.carbon
  27. 30 0
      toolchain/parser/testdata/generics/params/one.carbon
  28. 32 0
      toolchain/parser/testdata/generics/params/one_suffix_comma.carbon
  29. 70 0
      toolchain/parser/testdata/generics/params/six.carbon
  30. 38 0
      toolchain/parser/testdata/generics/params/two.carbon
  31. 40 0
      toolchain/parser/testdata/generics/params/two_suffix_comma.carbon

+ 2 - 1
.codespell_ignore

@@ -4,11 +4,12 @@
 
 atleast
 circularly
+compiletime
 copyable
 crate
 crossreference
 falsy
 inout
+parameteras
 pullrequest
 statics
-compiletime

+ 2 - 1
toolchain/diagnostics/diagnostic_kind.def

@@ -53,7 +53,6 @@ CARBON_DIAGNOSTIC_KIND(BinaryOperatorRequiresWhitespace)
 CARBON_DIAGNOSTIC_KIND(ExpectedCloseParen)
 CARBON_DIAGNOSTIC_KIND(ExpectedCodeBlock)
 CARBON_DIAGNOSTIC_KIND(ExpectedExpression)
-CARBON_DIAGNOSTIC_KIND(ExpectedFunctionParams)
 CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterDot)
 CARBON_DIAGNOSTIC_KIND(ExpectedParameterName)
 CARBON_DIAGNOSTIC_KIND(ExpectedParenAfter)
@@ -83,6 +82,8 @@ CARBON_DIAGNOSTIC_KIND(ExpectedInNotColon)
 CARBON_DIAGNOSTIC_KIND(ExpectedDeclarationName)
 CARBON_DIAGNOSTIC_KIND(ExpectedDeclarationSemiOrDefinition)
 CARBON_DIAGNOSTIC_KIND(MethodImplNotAllowed)
+CARBON_DIAGNOSTIC_KIND(ParametersRequiredByIntroducer)
+CARBON_DIAGNOSTIC_KIND(ParametersRequiredByDeduced)
 
 // ============================================================================
 // Semantics diagnostics

+ 185 - 134
toolchain/parser/parser.cpp

@@ -22,9 +22,6 @@ CARBON_DIAGNOSTIC(
     OperatorRequiresParentheses, Error,
     "Parentheses are required to disambiguate operator precedence.");
 
-CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.",
-                  TokenKind);
-
 CARBON_DIAGNOSTIC(ExpectedSemiAfterExpression, Error,
                   "Expected `;` after expression.");
 
@@ -127,6 +124,8 @@ auto Parser::ConsumeAndAddOpenParen(TokenizedBuffer::Token default_token,
   if (auto open_paren = ConsumeIf(TokenKind::OpenParen)) {
     AddLeafNode(start_kind, *open_paren, /*has_error=*/false);
   } else {
+    CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.",
+                      TokenKind);
     emitter_->Emit(*position_, ExpectedParenAfter,
                    tokens_->GetKind(default_token));
     AddLeafNode(start_kind, default_token, /*has_error=*/true);
@@ -766,6 +765,53 @@ auto Parser::HandleCodeBlockFinishState() -> void {
   }
 }
 
+auto Parser::HandleDeclarationNameAndParams(bool params_required) -> void {
+  auto state = PopState();
+
+  if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier,
+                               ParseNodeKind::DeclaredName)) {
+    emitter_->Emit(*position_, ExpectedDeclarationName,
+                   tokens_->GetKind(state.token));
+    ReturnErrorOnState();
+    return;
+  }
+
+  if (PositionIs(TokenKind::OpenSquareBracket)) {
+    PushState(ParserState::DeclarationNameAndParamsAfterDeduced);
+    PushState(ParserState::ParameterListAsDeduced);
+  } else if (PositionIs(TokenKind::OpenParen)) {
+    PushState(ParserState::ParameterListAsRegular);
+  } else if (params_required) {
+    CARBON_DIAGNOSTIC(ParametersRequiredByIntroducer, Error,
+                      "`{0}` requires a `(` for parameters.", TokenKind);
+    emitter_->Emit(*position_, ParametersRequiredByIntroducer,
+                   tokens_->GetKind(state.token));
+    ReturnErrorOnState();
+  }
+}
+
+auto Parser::HandleDeclarationNameAndParamsAsOptionalState() -> void {
+  HandleDeclarationNameAndParams(/*params_required=*/false);
+}
+
+auto Parser::HandleDeclarationNameAndParamsAsRequiredState() -> void {
+  HandleDeclarationNameAndParams(/*params_required=*/true);
+}
+
+auto Parser::HandleDeclarationNameAndParamsAfterDeducedState() -> void {
+  PopAndDiscardState();
+
+  if (PositionIs(TokenKind::OpenParen)) {
+    PushState(ParserState::ParameterListAsRegular);
+  } else {
+    CARBON_DIAGNOSTIC(
+        ParametersRequiredByDeduced, Error,
+        "A `(` for parameters is required after deduced parameters.");
+    emitter_->Emit(*position_, ParametersRequiredByDeduced);
+    ReturnErrorOnState();
+  }
+}
+
 auto Parser::HandleDeclarationScopeLoopState() -> void {
   // This maintains the current state unless we're at the end of the scope.
 
@@ -807,35 +853,6 @@ auto Parser::HandleDeclarationScopeLoopState() -> void {
   }
 }
 
-auto Parser::HandleDeducedParameterState() -> void {
-  PopAndDiscardState();
-
-  PushState(ParserState::DeducedParameterFinish);
-  PushState(ParserState::PatternAsDeducedParameter);
-}
-
-auto Parser::HandleDeducedParameterFinishState() -> void {
-  auto state = PopState();
-
-  if (state.has_error) {
-    ReturnErrorOnState();
-  }
-
-  if (ConsumeListToken(ParseNodeKind::ParameterListComma,
-                       TokenKind::CloseSquareBracket,
-                       state.has_error) == ListTokenKind::Comma) {
-    PushState(ParserState::DeducedParameter);
-  }
-}
-
-auto Parser::HandleDeducedParameterListFinishState() -> void {
-  auto state = PopState();
-
-  AddNode(ParseNodeKind::DeducedParameterList,
-          ConsumeChecked(TokenKind::CloseSquareBracket), state.subtree_start,
-          state.has_error);
-}
-
 auto Parser::HandleDesignator(bool as_struct) -> void {
   auto state = PopState();
 
@@ -1084,87 +1101,13 @@ auto Parser::HandleFunctionIntroducerState() -> void {
 
   AddLeafNode(ParseNodeKind::FunctionIntroducer, Consume());
 
-  if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier,
-                               ParseNodeKind::DeclaredName)) {
-    emitter_->Emit(*position_, ExpectedDeclarationName, TokenKind::Fn);
-    // TODO: We could change the lexer to allow us to synthesize certain
-    // kinds of tokens and try to "recover" here, but unclear that this is
-    // really useful.
-    HandleDeclarationError(state, ParseNodeKind::FunctionDeclaration,
-                           /*skip_past_likely_end=*/true);
-    return;
-  }
-
-  // Proceed to the same state regardless of whether there's a deduced
-  // parameter list.
-  state.state = ParserState::FunctionAfterDeducedParameterList;
+  state.state = ParserState::FunctionAfterParameters;
   PushState(state);
-
-  if (!PositionIs(TokenKind::OpenSquareBracket)) {
-    return;
-  }
-
-  // Parse the deduced parameter list as its own subtree.
-  PushState(ParserState::DeducedParameterListFinish);
-  AddLeafNode(ParseNodeKind::DeducedParameterListStart, Consume());
-
-  if (!PositionIs(TokenKind::CloseSquareBracket)) {
-    PushState(ParserState::DeducedParameter);
-  }
-}
-
-auto Parser::HandleFunctionAfterDeducedParameterListState() -> void {
-  auto state = PopState();
-
-  if (!PositionIs(TokenKind::OpenParen)) {
-    CARBON_DIAGNOSTIC(ExpectedFunctionParams, Error,
-                      "Expected `(` after function name.");
-    emitter_->Emit(*position_, ExpectedFunctionParams);
-    HandleDeclarationError(state, ParseNodeKind::FunctionDeclaration,
-                           /*skip_past_likely_end=*/true);
-    return;
-  }
-
-  // Parse the parameter list as its own subtree; once that pops, resume
-  // function parsing.
-  state.state = ParserState::FunctionAfterParameterList;
+  state.state = ParserState::DeclarationNameAndParamsAsRequired;
   PushState(state);
-  PushState(ParserState::FunctionParameterListFinish);
-  AddLeafNode(ParseNodeKind::ParameterListStart, Consume());
-
-  if (!PositionIs(TokenKind::CloseParen)) {
-    PushState(ParserState::FunctionParameter);
-  }
-}
-
-auto Parser::HandleFunctionParameterState() -> void {
-  PopAndDiscardState();
-
-  PushState(ParserState::FunctionParameterFinish);
-  PushState(ParserState::PatternAsFunctionParameter);
 }
 
-auto Parser::HandleFunctionParameterFinishState() -> void {
-  auto state = PopState();
-
-  if (state.has_error) {
-    ReturnErrorOnState();
-  }
-
-  if (ConsumeListToken(ParseNodeKind::ParameterListComma, TokenKind::CloseParen,
-                       state.has_error) == ListTokenKind::Comma) {
-    PushState(ParserState::FunctionParameter);
-  }
-}
-
-auto Parser::HandleFunctionParameterListFinishState() -> void {
-  auto state = PopState();
-
-  AddNode(ParseNodeKind::ParameterList, ConsumeChecked(TokenKind::CloseParen),
-          state.subtree_start, state.has_error);
-}
-
-auto Parser::HandleFunctionAfterParameterListState() -> void {
+auto Parser::HandleFunctionAfterParametersState() -> void {
   auto state = PopState();
 
   // Regardless of whether there's a return type, we'll finish the signature.
@@ -1219,8 +1162,10 @@ auto Parser::HandleFunctionSignatureFinishState() -> void {
       break;
     }
     default: {
-      emitter_->Emit(*position_, ExpectedDeclarationSemiOrDefinition,
-                     TokenKind::Fn);
+      if (!state.has_error) {
+        emitter_->Emit(*position_, ExpectedDeclarationSemiOrDefinition,
+                       TokenKind::Fn);
+      }
       // Only need to skip if we've not already found a new line.
       bool skip_past_likely_end =
           tokens_->GetLine(*position_) == tokens_->GetLine(state.token);
@@ -1316,6 +1261,93 @@ auto Parser::HandlePackageState() -> void {
           /*has_error=*/false);
 }
 
+auto Parser::HandleParameter(ParserState pattern_state,
+                             ParserState finish_state) -> void {
+  PopAndDiscardState();
+
+  PushState(finish_state);
+  PushState(pattern_state);
+}
+
+auto Parser::HandleParameterAsDeducedState() -> void {
+  HandleParameter(ParserState::PatternAsDeducedParameter,
+                  ParserState::ParameterFinishAsDeduced);
+}
+
+auto Parser::HandleParameterAsRegularState() -> void {
+  HandleParameter(ParserState::PatternAsParameter,
+                  ParserState::ParameterFinishAsRegular);
+}
+
+auto Parser::HandleParameterFinish(TokenKind close_token,
+                                   ParserState param_state) -> void {
+  auto state = PopState();
+
+  if (state.has_error) {
+    ReturnErrorOnState();
+  }
+
+  if (ConsumeListToken(ParseNodeKind::ParameterListComma, close_token,
+                       state.has_error) == ListTokenKind::Comma) {
+    PushState(param_state);
+  }
+}
+
+auto Parser::HandleParameterFinishAsDeducedState() -> void {
+  HandleParameterFinish(TokenKind::CloseSquareBracket,
+                        ParserState::ParameterAsDeduced);
+}
+
+auto Parser::HandleParameterFinishAsRegularState() -> void {
+  HandleParameterFinish(TokenKind::CloseParen, ParserState::ParameterAsRegular);
+}
+
+auto Parser::HandleParameterList(ParseNodeKind parse_node_kind,
+                                 TokenKind open_token_kind,
+                                 TokenKind close_token_kind,
+                                 ParserState param_state,
+                                 ParserState finish_state) -> void {
+  PopAndDiscardState();
+
+  PushState(finish_state);
+  AddLeafNode(parse_node_kind, ConsumeChecked(open_token_kind));
+
+  if (!PositionIs(close_token_kind)) {
+    PushState(param_state);
+  }
+}
+
+auto Parser::HandleParameterListAsDeducedState() -> void {
+  HandleParameterList(
+      ParseNodeKind::DeducedParameterListStart, TokenKind::OpenSquareBracket,
+      TokenKind::CloseSquareBracket, ParserState::ParameterAsDeduced,
+      ParserState::ParameterListFinishAsDeduced);
+}
+
+auto Parser::HandleParameterListAsRegularState() -> void {
+  HandleParameterList(ParseNodeKind::ParameterListStart, TokenKind::OpenParen,
+                      TokenKind::CloseParen, ParserState::ParameterAsRegular,
+                      ParserState::ParameterListFinishAsRegular);
+}
+
+auto Parser::HandleParameterListFinish(ParseNodeKind parse_node_kind,
+                                       TokenKind token_kind) -> void {
+  auto state = PopState();
+
+  AddNode(parse_node_kind, ConsumeChecked(token_kind), state.subtree_start,
+          state.has_error);
+}
+
+auto Parser::HandleParameterListFinishAsDeducedState() -> void {
+  HandleParameterListFinish(ParseNodeKind::DeducedParameterList,
+                            TokenKind::CloseSquareBracket);
+}
+
+auto Parser::HandleParameterListFinishAsRegularState() -> void {
+  HandleParameterListFinish(ParseNodeKind::ParameterList,
+                            TokenKind::CloseParen);
+}
+
 auto Parser::HandleParenCondition(ParseNodeKind start_kind,
                                   ParserState finish_state) -> void {
   auto state = PopState();
@@ -1489,7 +1521,7 @@ auto Parser::HandlePatternAsDeducedParameterState() -> void {
   HandlePattern(PatternKind::DeducedParameter);
 }
 
-auto Parser::HandlePatternAsFunctionParameterState() -> void {
+auto Parser::HandlePatternAsParameterState() -> void {
   HandlePattern(PatternKind::Parameter);
 }
 
@@ -1733,17 +1765,39 @@ auto Parser::HandleStatementWhileBlockFinishState() -> void {
 }
 
 auto Parser::HandleTypeIntroducer(ParseNodeKind introducer_kind,
-                                  ParseNodeKind declaration_kind,
-                                  ParseNodeKind definition_start_kind,
-                                  ParserState definition_finish_state) -> void {
+                                  ParserState after_params_state) -> void {
   auto state = PopState();
 
   AddLeafNode(introducer_kind, Consume());
 
-  if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier,
-                               ParseNodeKind::DeclaredName)) {
-    emitter_->Emit(*position_, ExpectedDeclarationName,
-                   tokens_->GetKind(state.token));
+  state.state = after_params_state;
+  PushState(state);
+  state.state = ParserState::DeclarationNameAndParamsAsOptional;
+  PushState(state);
+}
+
+auto Parser::HandleTypeIntroducerAsClassState() -> void {
+  HandleTypeIntroducer(ParseNodeKind::ClassIntroducer,
+                       ParserState::TypeAfterParamsAsClass);
+}
+
+auto Parser::HandleTypeIntroducerAsInterfaceState() -> void {
+  HandleTypeIntroducer(ParseNodeKind::InterfaceIntroducer,
+                       ParserState::TypeAfterParamsAsInterface);
+}
+
+auto Parser::HandleTypeIntroducerAsNamedConstraintState() -> void {
+  HandleTypeIntroducer(ParseNodeKind::NamedConstraintIntroducer,
+                       ParserState::TypeAfterParamsAsNamedConstraint);
+}
+
+auto Parser::HandleTypeAfterParams(ParseNodeKind declaration_kind,
+                                   ParseNodeKind definition_start_kind,
+                                   ParserState definition_finish_state)
+    -> void {
+  auto state = PopState();
+
+  if (state.has_error) {
     HandleDeclarationError(state, declaration_kind,
                            /*skip_past_likely_end=*/true);
     return;
@@ -1769,25 +1823,22 @@ auto Parser::HandleTypeIntroducer(ParseNodeKind introducer_kind,
           state.has_error);
 }
 
-auto Parser::HandleTypeIntroducerAsClassState() -> void {
-  HandleTypeIntroducer(ParseNodeKind::ClassIntroducer,
-                       ParseNodeKind::ClassDeclaration,
-                       ParseNodeKind::ClassDefinitionStart,
-                       ParserState::TypeDefinitionFinishAsClass);
+auto Parser::HandleTypeAfterParamsAsClassState() -> void {
+  HandleTypeAfterParams(ParseNodeKind::ClassDeclaration,
+                        ParseNodeKind::ClassDefinitionStart,
+                        ParserState::TypeDefinitionFinishAsClass);
 }
 
-auto Parser::HandleTypeIntroducerAsInterfaceState() -> void {
-  HandleTypeIntroducer(ParseNodeKind::InterfaceIntroducer,
-                       ParseNodeKind::InterfaceDeclaration,
-                       ParseNodeKind::InterfaceDefinitionStart,
-                       ParserState::TypeDefinitionFinishAsInterface);
+auto Parser::HandleTypeAfterParamsAsInterfaceState() -> void {
+  HandleTypeAfterParams(ParseNodeKind::InterfaceDeclaration,
+                        ParseNodeKind::InterfaceDefinitionStart,
+                        ParserState::TypeDefinitionFinishAsInterface);
 }
 
-auto Parser::HandleTypeIntroducerAsNamedConstraintState() -> void {
-  HandleTypeIntroducer(ParseNodeKind::NamedConstraintIntroducer,
-                       ParseNodeKind::NamedConstraintDeclaration,
-                       ParseNodeKind::NamedConstraintDefinitionStart,
-                       ParserState::TypeDefinitionFinishAsNamedConstraint);
+auto Parser::HandleTypeAfterParamsAsNamedConstraintState() -> void {
+  HandleTypeAfterParams(ParseNodeKind::NamedConstraintDeclaration,
+                        ParseNodeKind::NamedConstraintDefinitionStart,
+                        ParserState::TypeDefinitionFinishAsNamedConstraint);
 }
 
 auto Parser::HandleTypeDefinitionFinish(ParseNodeKind definition_kind) -> void {

+ 29 - 4
toolchain/parser/parser.h

@@ -305,9 +305,30 @@ class Parser {
   // Handles BraceExpressionFinishAs(Type|Value|Unknown).
   auto HandleBraceExpressionFinish(BraceExpressionKind kind) -> void;
 
+  // Handles DeclarationNameAndParamsAs(Optional|Required).
+  auto HandleDeclarationNameAndParams(bool params_required) -> void;
+
   // Handles DesignatorAs.
   auto HandleDesignator(bool as_struct) -> void;
 
+  // Handles ParameterAs(Deduced|Regular).
+  auto HandleParameter(ParserState pattern_state, ParserState finish_state)
+      -> void;
+
+  // Handles ParameterFinishAs(Deduced|Regular).
+  auto HandleParameterFinish(TokenKind close_token, ParserState param_state)
+      -> void;
+
+  // Handles ParameterListAs(Deduced|Regular).
+  auto HandleParameterList(ParseNodeKind parse_node_kind,
+                           TokenKind open_token_kind,
+                           TokenKind close_token_kind, ParserState param_state,
+                           ParserState finish_state) -> void;
+
+  // Handles ParameterListFinishAs(Deduced|Regular).
+  auto HandleParameterListFinish(ParseNodeKind parse_node_kind,
+                                 TokenKind token_kind) -> void;
+
   // Handles ParenConditionAs(If|While)
   auto HandleParenCondition(ParseNodeKind start_kind, ParserState finish_state)
       -> void;
@@ -321,11 +342,15 @@ class Parser {
   // Handles the `;` after a keyword statement.
   auto HandleStatementKeywordFinish(ParseNodeKind node_kind) -> void;
 
-  // Handles processing of a type's `<introducer> <name> {`.
+  // Handles processing of a type's introducer.
   auto HandleTypeIntroducer(ParseNodeKind introducer_kind,
-                            ParseNodeKind declaration_kind,
-                            ParseNodeKind definition_start_kind,
-                            ParserState definition_finish_state) -> void;
+                            ParserState after_params_state) -> void;
+
+  // Handles processing after params, deciding whether it's a declaration or
+  // definition.
+  auto HandleTypeAfterParams(ParseNodeKind declaration_kind,
+                             ParseNodeKind definition_start_kind,
+                             ParserState definition_finish_state) -> void;
 
   // Handles parsing after the declaration scope of a type.
   auto HandleTypeDefinitionFinish(ParseNodeKind definition_kind) -> void;

+ 68 - 75
toolchain/parser/parser_state.def

@@ -127,6 +127,26 @@ CARBON_PARSER_STATE(CodeBlock)
 //   (state done)
 CARBON_PARSER_STATE(CodeBlockFinish)
 
+// Handles a general declaration name and parameters, such as `Foo[...](...)`.
+//
+// If `OpenSquareBracket`:
+//   1. ParameterListAsDeduced
+//   2. DeclarationNameAndParamsAfterDeduced
+// If `OpenParen`:
+//   1. ParameterListAsRegular
+// Else:
+//   (state done)
+CARBON_PARSER_STATE_VARIANTS2(DeclarationNameAndParams, Optional, Required)
+
+// Handles regular parameters such as `(...)` for the general declaration case.
+// Only used after deduced parameters.
+//
+// If `OpenParen`:
+//   1. ParameterListAsRegular
+// Else:
+//   (state done)
+CARBON_PARSER_STATE(DeclarationNameAndParamsAfterDeduced)
+
 // Handles processing of a declaration scope. Things like fn, class, interface,
 // and so on.
 //
@@ -153,28 +173,6 @@ CARBON_PARSER_STATE(CodeBlockFinish)
 //   1. DeclarationScopeLoop
 CARBON_PARSER_STATE(DeclarationScopeLoop)
 
-// Starts deduced parameter processing.
-//
-// Always:
-//   1. PatternAsDeducedParameter
-//   2. DeducedParameterFinish
-CARBON_PARSER_STATE(DeducedParameter)
-
-// Finishes deduced parameter processing, including `,`. If there are more
-// parameters, enqueues another parameter processing state.
-//
-// If `Comma` without `CloseSquareBracket`:
-//   1. DeducedParameter
-// Else:
-//   (state done)
-CARBON_PARSER_STATE(DeducedParameterFinish)
-
-// Handles processing of a deduced parameter list `]`.
-//
-// Always:
-//   (state done)
-CARBON_PARSER_STATE(DeducedParameterListFinish)
-
 // Handles a designator expression, such as `.z` in `x.(y.z)`.
 //
 // Always:
@@ -251,55 +249,15 @@ CARBON_PARSER_STATE(ExpressionLoopForPrefix)
 //   (state done)
 CARBON_PARSER_STATE(ExpressionStatementFinish)
 
-// Handles processing of a function's `fn <name>(`, and enqueues parameter list
-// handling.
+// Handles a function's introducer.
 //
 // If invalid:
 //   (state done)
-// If deduced parameters:
-//   1. DeducedParameter
-//   2. DeducedParameterListFinish
-//   3. FunctionAfterDeducedParameterList
 // Else:
-//   1. FunctionAfterDeducedParameterList
+//   1. DeclarationNameAndParamsAsRequired
+//   2. FunctionAfterParameters
 CARBON_PARSER_STATE(FunctionIntroducer)
 
-// Handles processing of a functions' syntax after the deduced parameter lists's
-// `]`. This applies only to interfaces and classes.
-//
-// If invalid:
-//   (state done)
-// If parenthesized parameters:
-//   1. FunctionParameter
-//   2. FunctionParameterListFinish
-//   3. FunctionAfterParameterList
-// Else:
-//   1. FunctionParameterListFinish
-//   2. FunctionAfterParameterList
-CARBON_PARSER_STATE(FunctionAfterDeducedParameterList)
-
-// Starts function parameter processing.
-//
-// Always:
-//   1. PatternAsFunctionParameter
-//   2. FunctionParameterFinish
-CARBON_PARSER_STATE(FunctionParameter)
-
-// Finishes function parameter processing, including `,`. If there are more
-// parameters, enqueues another parameter processing state.
-//
-// If `Comma` without `CloseParen`:
-//   1. FunctionParameter
-// Else:
-//   (state done)
-CARBON_PARSER_STATE(FunctionParameterFinish)
-
-// Handles processing of a function's parameter list `)`.
-//
-// Always:
-//   (state done)
-CARBON_PARSER_STATE(FunctionParameterListFinish)
-
 // Handles processing of a function's syntax after `)`, primarily the
 // possibility a `->` return type is there. Always enqueues signature finish
 // handling.
@@ -310,7 +268,7 @@ CARBON_PARSER_STATE(FunctionParameterListFinish)
 //   3. FunctionSignatureFinish
 // Else:
 //   1. FunctionSignatureFinish
-CARBON_PARSER_STATE(FunctionAfterParameterList)
+CARBON_PARSER_STATE(FunctionAfterParameters)
 
 // Finishes a function return type.
 //
@@ -342,6 +300,37 @@ CARBON_PARSER_STATE(FunctionDefinitionFinish)
 //   (state done)
 CARBON_PARSER_STATE(Package)
 
+// Starts deduced parameter processing.
+//
+// Always:
+//   1. PatternAs(DeducedParameter|Parameter)
+//   2. ParameterFinishAs(Deduced|Regular)
+CARBON_PARSER_STATE_VARIANTS2(Parameter, Deduced, Regular)
+
+// Finishes deduced parameter processing, including `,`. If there are more
+// parameters, enqueues another parameter processing state.
+//
+// If `Comma` without the list close token:
+//   1. ParameterAs(Deduced|Regular)
+// Else:
+//   (state done)
+CARBON_PARSER_STATE_VARIANTS2(ParameterFinish, Deduced, Regular)
+
+// Handles processing of a parameter list `[` or `(`.
+//
+// If the list close token:
+//   1. ParameterListFinishAs(Deduced|Regular)
+// Else:
+//   1. ParameterAs(Deduced|Regular)
+//   2. ParameterListFinishAs(Deduced|Regular)
+CARBON_PARSER_STATE_VARIANTS2(ParameterList, Deduced, Regular)
+
+// Handles processing of a parameter list `]` or `)`.
+//
+// Always:
+//   (state done)
+CARBON_PARSER_STATE_VARIANTS2(ParameterListFinish, Deduced, Regular)
+
 // Handles the processing of a `(condition)` up through the expression.
 //
 // Always:
@@ -394,8 +383,7 @@ CARBON_PARSER_STATE_VARIANTS2(ParenExpressionFinish, Normal, Tuple)
 //   2. PatternFinish
 // Else:
 //   1. PatternFinish
-CARBON_PARSER_STATE_VARIANTS3(Pattern, DeducedParameter, FunctionParameter,
-                              Variable)
+CARBON_PARSER_STATE_VARIANTS3(Pattern, DeducedParameter, Parameter, Variable)
 
 // Handles `addr` in a pattern.
 //
@@ -563,11 +551,17 @@ CARBON_PARSER_STATE(StatementWhileBlockFinish)
 //
 // Always:
 //   (state done)
-CARBON_PARSER_STATE(TypeDefinitionFinishAsClass)
-CARBON_PARSER_STATE(TypeDefinitionFinishAsInterface)
-CARBON_PARSER_STATE(TypeDefinitionFinishAsNamedConstraint)
+CARBON_PARSER_STATE_VARIANTS3(TypeDefinitionFinish, Class, Interface,
+                              NamedConstraint)
+
+// Handles processing of a type's introducer.
+//
+// Always:
+//   1. DeclarationNameAndParamsAsOptional
+//   2. TypeAfterParamsAs(Class|Interface|NamedConstraint)
+CARBON_PARSER_STATE_VARIANTS3(TypeIntroducer, Class, Interface, NamedConstraint)
 
-// Handles processing of a type's `<introducer> <name> [;{]`.
+// Handles processing of a type after its optional parameters.
 //
 // If `Semi`:
 //   (state done)
@@ -576,9 +570,8 @@ CARBON_PARSER_STATE(TypeDefinitionFinishAsNamedConstraint)
 //   2. TypeDefinitionFinishAs(Class|Interface|NamedConstraint)
 // Else:
 //   (state done)
-CARBON_PARSER_STATE(TypeIntroducerAsClass)
-CARBON_PARSER_STATE(TypeIntroducerAsInterface)
-CARBON_PARSER_STATE(TypeIntroducerAsNamedConstraint)
+CARBON_PARSER_STATE_VARIANTS3(TypeAfterParams, Class, Interface,
+                              NamedConstraint)
 
 // Handles the start of a `var`.
 //

+ 3 - 6
toolchain/parser/testdata/function/declaration/deduced_one.carbon → toolchain/parser/testdata/function/declaration/deduced_empty.carbon

@@ -8,14 +8,11 @@
 // CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
 // CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 5},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
 // CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 10},
+// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 7},
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT: ]
 
-fn foo[a: i32]();
+fn foo[]();

+ 0 - 22
toolchain/parser/testdata/function/declaration/deduced_one_suffix_comma.carbon

@@ -1,22 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-// AUTOUPDATE
-// RUN: %{carbon-run-parser}
-// CHECK:STDOUT: [
-// CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
-// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 6},
-// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
-// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 11},
-// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT: ]
-
-fn foo[a: i32,]();

+ 0 - 0
toolchain/parser/testdata/function/declaration/deduced_two.carbon → toolchain/parser/testdata/function/declaration/deduced_params.carbon


+ 0 - 41
toolchain/parser/testdata/function/declaration/deduced_six.carbon

@@ -1,41 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-// AUTOUPDATE
-// RUN: %{carbon-run-parser}
-// CHECK:STDOUT: [
-// CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
-// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'c'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'd'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'e'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'f'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 25},
-// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
-// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 30},
-// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT: ]
-
-fn foo[a: i32, b: i32, c: i32, d: i32, e: i32, f: i32]();

+ 0 - 26
toolchain/parser/testdata/function/declaration/deduced_two_suffix_comma.carbon

@@ -1,26 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-// AUTOUPDATE
-// RUN: %{carbon-run-parser}
-// CHECK:STDOUT: [
-// CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
-// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 10},
-// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
-// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 15},
-// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT: ]
-
-fn foo[a: i32, b: i32,]();

+ 1 - 1
toolchain/parser/testdata/function/declaration/fail_identifier_instead_of_sig.carbon

@@ -11,5 +11,5 @@
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT: ]
 
-// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_identifier_instead_of_sig.carbon:[[@LINE+1]]:8: Expected `(` after function name.
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_identifier_instead_of_sig.carbon:[[@LINE+1]]:8: `fn` requires a `(` for parameters.
 fn foo bar;

+ 1 - 1
toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon

@@ -17,4 +17,4 @@
 // CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon:[[@LINE+2]]:7: Closing symbol does not match most recent opening symbol.
 // CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon:[[@LINE+1]]:8: Expected parameter declaration.
 fn Div[();
-// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon:[[@LINE+0]]:156: Expected `(` after function name.
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon:[[@LINE+0]]:181: A `(` for parameters is required after deduced parameters.

+ 1 - 1
toolchain/parser/testdata/function/declaration/fail_no_sig_or_semi.carbon

@@ -11,5 +11,5 @@
 // CHECK:STDOUT: {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT: ]
 
-// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_no_sig_or_semi.carbon:[[@LINE+1]]:7: Expected `(` after function name.
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_no_sig_or_semi.carbon:[[@LINE+1]]:7: `fn` requires a `(` for parameters.
 fn foo

+ 0 - 0
toolchain/parser/testdata/function/declaration/params_two.carbon → toolchain/parser/testdata/function/declaration/params.carbon


+ 0 - 19
toolchain/parser/testdata/function/declaration/params_one.carbon

@@ -1,19 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-// AUTOUPDATE
-// RUN: %{carbon-run-parser}
-// CHECK:STDOUT: [
-// CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
-// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 5},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 8},
-// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT: ]
-
-fn foo(a: i32);

+ 0 - 20
toolchain/parser/testdata/function/declaration/params_one_suffix_comma.carbon

@@ -1,20 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-// AUTOUPDATE
-// RUN: %{carbon-run-parser}
-// CHECK:STDOUT: [
-// CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
-// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 6},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 9},
-// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT: ]
-
-fn foo(a: i32,);

+ 0 - 39
toolchain/parser/testdata/function/declaration/params_six.carbon

@@ -1,39 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-// AUTOUPDATE
-// RUN: %{carbon-run-parser}
-// CHECK:STDOUT: [
-// CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
-// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'c'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'd'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'e'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'f'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 25},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 28},
-// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT: ]
-
-fn foo(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32);

+ 0 - 24
toolchain/parser/testdata/function/declaration/params_two_suffix_comma.carbon

@@ -1,24 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-// AUTOUPDATE
-// RUN: %{carbon-run-parser}
-// CHECK:STDOUT: [
-// CHECK:STDOUT:   {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
-// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
-// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
-// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
-// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 10},
-// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 13},
-// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT: ]
-
-fn foo(a: i32, b: i32,);

+ 28 - 0
toolchain/parser/testdata/generics/deduced_params/empty.carbon

@@ -0,0 +1,28 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 7},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:     {kind: 'DeducedParameterList', text: ']', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 7},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 8},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo[]();
+
+interface Bar[]() {}

+ 47 - 0
toolchain/parser/testdata/generics/deduced_params/fail_no_parens.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{not} %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 5},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', has_error: yes, subtree_size: 8},
+// CHECK:STDOUT:   {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'InterfaceDeclaration', text: 'interface', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:   {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 5},
+// CHECK:STDOUT: {kind: 'InterfaceDeclaration', text: 'interface', has_error: yes, subtree_size: 8},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/deduced_params/fail_no_parens.carbon:[[@LINE+1]]:12: A `(` for parameters is required after deduced parameters.
+class Foo[];
+
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/deduced_params/fail_no_parens.carbon:[[@LINE+1]]:18: A `(` for parameters is required after deduced parameters.
+class Foo[a: i32];
+
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/deduced_params/fail_no_parens.carbon:[[@LINE+1]]:17: A `(` for parameters is required after deduced parameters.
+interface Bar[] {}
+
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/deduced_params/fail_no_parens.carbon:[[@LINE+1]]:23: A `(` for parameters is required after deduced parameters.
+interface Bar[a: i32] {}

+ 34 - 0
toolchain/parser/testdata/generics/deduced_params/one.carbon

@@ -0,0 +1,34 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 10},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'DeducedParameterList', text: ']', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 10},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 11},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo[a: i32]();
+
+interface Bar[a: i32]() {}

+ 36 - 0
toolchain/parser/testdata/generics/deduced_params/one_suffix_comma.carbon

@@ -0,0 +1,36 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 6},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 11},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:     {kind: 'DeducedParameterList', text: ']', subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 11},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 12},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo[a: i32,]();
+
+interface Bar[a: i32,]() {}

+ 74 - 0
toolchain/parser/testdata/generics/deduced_params/six.carbon

@@ -0,0 +1,74 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'c'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'd'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'e'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'f'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 25},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 30},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'c'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'd'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'e'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'f'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'DeducedParameterList', text: ']', subtree_size: 25},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 30},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 31},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo[a: i32, b: i32, c: i32, d: i32, e: i32, f: i32]();
+
+interface Bar[a: i32, b: i32, c: i32, d: i32, e: i32, f: i32]() {}

+ 42 - 0
toolchain/parser/testdata/generics/deduced_params/two.carbon

@@ -0,0 +1,42 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 14},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'DeducedParameterList', text: ']', subtree_size: 9},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 14},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 15},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo[a: i32, b: i32]();
+
+interface Bar[a: i32, b: i32]() {}

+ 44 - 0
toolchain/parser/testdata/generics/deduced_params/two_suffix_comma.carbon

@@ -0,0 +1,44 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:   {kind: 'DeducedParameterList', text: ']', subtree_size: 10},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 15},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'DeducedParameterListStart', text: '['},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:     {kind: 'DeducedParameterList', text: ']', subtree_size: 10},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 15},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 16},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo[a: i32, b: i32,]();
+
+interface Bar[a: i32, b: i32,]() {}

+ 24 - 0
toolchain/parser/testdata/generics/params/empty.carbon

@@ -0,0 +1,24 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 6},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo();
+
+interface Bar() {}

+ 30 - 0
toolchain/parser/testdata/generics/params/one.carbon

@@ -0,0 +1,30 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 5},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 8},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 5},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 8},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo(a: i32);
+
+interface Bar(a: i32) {}

+ 32 - 0
toolchain/parser/testdata/generics/params/one_suffix_comma.carbon

@@ -0,0 +1,32 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 6},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 6},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 9},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 10},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo(a: i32,);
+
+interface Bar(a: i32,) {}

+ 70 - 0
toolchain/parser/testdata/generics/params/six.carbon

@@ -0,0 +1,70 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'c'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'd'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'e'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'f'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 25},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 28},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'c'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'd'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'e'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'f'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 25},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 28},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 29},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32);
+
+interface Bar(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) {}

+ 38 - 0
toolchain/parser/testdata/generics/params/two.carbon

@@ -0,0 +1,38 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 9},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 12},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 9},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 12},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 13},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo(a: i32, b: i32);
+
+interface Bar(a: i32, b: i32) {}

+ 40 - 0
toolchain/parser/testdata/generics/params/two_suffix_comma.carbon

@@ -0,0 +1,40 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'Foo'},
+// CHECK:STDOUT:     {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:       {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:     {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:   {kind: 'ParameterList', text: ')', subtree_size: 10},
+// CHECK:STDOUT: {kind: 'ClassDeclaration', text: ';', subtree_size: 13},
+// CHECK:STDOUT:     {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:     {kind: 'DeclaredName', text: 'Bar'},
+// CHECK:STDOUT:       {kind: 'ParameterListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'a'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'DeclaredName', text: 'b'},
+// CHECK:STDOUT:         {kind: 'Literal', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'PatternBinding', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ParameterListComma', text: ','},
+// CHECK:STDOUT:     {kind: 'ParameterList', text: ')', subtree_size: 10},
+// CHECK:STDOUT:   {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 13},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 14},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+class Foo(a: i32, b: i32,);
+
+interface Bar(a: i32, b: i32,) {}