diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 22c6e3f6a5ab..baa5c65d2aae 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -91,7 +91,6 @@ CARBON_DIAGNOSTIC_KIND(ExpectedExprSemi) CARBON_DIAGNOSTIC_KIND(ExpectedStatementSemi) CARBON_DIAGNOSTIC_KIND(ExpectedStructLiteralField) CARBON_DIAGNOSTIC_KIND(ExpectedVarAfterReturned) -CARBON_DIAGNOSTIC_KIND(ExpectedVariableDecl) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceDefinition) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceAlternativeName) CARBON_DIAGNOSTIC_KIND(NestedVar) diff --git a/toolchain/parse/handle_statement.cpp b/toolchain/parse/handle_statement.cpp index d8389ab8f724..a593643d6bfd 100644 --- a/toolchain/parse/handle_statement.cpp +++ b/toolchain/parse/handle_statement.cpp @@ -112,29 +112,44 @@ auto HandleStatementForHeader(Context& context) -> void { if (open_paren) { state.token = *open_paren; } + state.kind = StateKind::StatementForHeaderIn; - - if (context.PositionIs(Lex::TokenKind::Var)) { - context.PushState(state); - context.PushState(StateKind::VarAsFor); - context.AddLeafNode(NodeKind::VariableIntroducer, context.Consume()); - } else { - CARBON_DIAGNOSTIC(ExpectedVariableDecl, Error, - "expected `var` declaration"); - context.emitter().Emit(*context.position(), ExpectedVariableDecl); - - if (auto next_in = context.FindNextOf({Lex::TokenKind::In})) { - context.SkipTo(*next_in); - context.ConsumeAndDiscard(); - } - state.has_error = true; - context.PushState(state); - } + context.PushState(state); + context.PushState(StateKind::Pattern); } auto HandleStatementForHeaderIn(Context& context) -> void { auto state = context.PopState(); + + auto end_token = state.token; + if (context.PositionIs(Lex::TokenKind::In)) { + end_token = context.Consume(); + } else if (context.PositionIs(Lex::TokenKind::Colon)) { + CARBON_DIAGNOSTIC(ExpectedInNotColon, Error, + "`:` should be replaced by `in`"); + context.emitter().Emit(*context.position(), ExpectedInNotColon); + state.has_error = true; + end_token = context.Consume(); + } else if (!state.has_error) { + CARBON_DIAGNOSTIC(ExpectedIn, Error, "expected `in` after loop pattern"); + context.emitter().Emit(*context.position(), ExpectedIn); + state.has_error = true; + } + + context.AddNode(NodeKind::ForIn, end_token, state.has_error); + context.PushState(state, StateKind::StatementForHeaderFinish); + + // If we had a parse error, try to skip to the closing paren rather than + // parsing an expression. + if (state.has_error) { + auto open_token = context.state_stack().back().token; + if (context.tokens().GetKind(open_token).is_opening_symbol()) { + context.SkipTo(context.tokens().GetMatchedClosingToken(open_token)); + return; + } + } + context.PushState(StateKind::Expr); } diff --git a/toolchain/parse/handle_var.cpp b/toolchain/parse/handle_var.cpp index e6003f62762e..471af2bde814 100644 --- a/toolchain/parse/handle_var.cpp +++ b/toolchain/parse/handle_var.cpp @@ -50,15 +50,6 @@ auto HandleVarAsReturned(Context& context) -> void { HandleVar(context, StateKind::VarFinishAsRegular, returned_token); } -auto HandleVarAsFor(Context& context) -> void { - auto state = context.PopState(); - - // The finished variable declaration will start at the `var`. - context.PushState(state, StateKind::VarFinishAsFor); - - context.PushState(StateKind::Pattern); -} - auto HandleFieldDecl(Context& context) -> void { auto state = context.PopState(); @@ -141,30 +132,6 @@ auto HandleVarFinishAsField(Context& context) -> void { HandleVarFinish(context, NodeKind::FieldDecl); } -auto HandleVarFinishAsFor(Context& context) -> void { - auto state = context.PopState(); - - context.AddNode(NodeKind::VariablePattern, state.token, state.has_error); - - auto end_token = state.token; - if (context.PositionIs(Lex::TokenKind::In)) { - end_token = context.Consume(); - } else if (context.PositionIs(Lex::TokenKind::Colon)) { - CARBON_DIAGNOSTIC(ExpectedInNotColon, Error, - "`:` should be replaced by `in`"); - context.emitter().Emit(*context.position(), ExpectedInNotColon); - state.has_error = true; - end_token = context.Consume(); - } else { - CARBON_DIAGNOSTIC(ExpectedIn, Error, - "expected `in` after loop `var` declaration"); - context.emitter().Emit(*context.position(), ExpectedIn); - state.has_error = true; - } - - context.AddNode(NodeKind::ForIn, end_token, state.has_error); -} - auto HandleVariablePattern(Context& context) -> void { auto state = context.PopState(); if (state.in_var_pattern) { diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 44e4217b8d32..12ede7a0416f 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -1116,30 +1116,25 @@ CARBON_PARSE_STATE(StatementBreakFinish) // (state done) CARBON_PARSE_STATE(StatementContinueFinish) -// Handles `for` processing of `(var`, proceeding to a binding pattern before -// continuing. +// Handles `for` processing of `(`, proceeding to a pattern before continuing. // -// for ( var ... ) +// for ( ... ) // ^ -// 1. VarAsFor -// 2. StatementForHeaderIn -// -// for ( ??? -// ^ -// for ( ??? in ... -// ^~~~~~~~ -// for ??? in ... -// ^~~~~~ // for ??? // ^ -// 1. StatementForHeaderIn +// 1. Pattern +// 2. StatementForHeaderIn CARBON_PARSE_STATE(StatementForHeader) // Handles `for` processing of `in`, proceeding to an expression before // continuing. // // for ( ... in ... ) -// ^ +// ^~ +// for ( ... : ... ) +// ^ +// for ( ... ??? ) +// ^ // 1. Expr // 2. StatementForHeaderFinish CARBON_PARSE_STATE(StatementForHeaderIn) @@ -1372,11 +1367,6 @@ CARBON_PARSE_STATE(ImplBeforeAs) // 2. VarAfterPatternAsVar // 3. VarFinishAsRegular // -// var ... (variant is For) -// ^ -// 1. Pattern -// 2. VarFinishAsFor -// // returned var ... (variant is Returned) // ^~~~~~~~~~~~ // 1. Pattern @@ -1386,7 +1376,7 @@ CARBON_PARSE_STATE(ImplBeforeAs) // returned ??? ; (variant is Returned) // ^~~~~~~~~~~~~~ // (state done) -CARBON_PARSE_STATE_VARIANTS3(Var, Regular, Returned, For) +CARBON_PARSE_STATE_VARIANTS2(Var, Regular, Returned) // Handles `var` after the pattern, either followed by an initializer or the // semicolon. @@ -1404,18 +1394,12 @@ CARBON_PARSE_STATE_VARIANTS2(VarAfterPattern, Var, Field) // Handles `var` parsing at the end. // -// var ... ; (variant is Regular or Field) +// var ... ; // ^ -// var ... ??? ; (variant is Regular or Field) +// var ... ??? ; // ^~~~~ // (state done) -// -// var ... in (variant is For) -// ^~ -// var ... : (variant is For, invalid) -// ^ -// (state done) -CARBON_PARSE_STATE_VARIANTS3(VarFinish, Regular, For, Field) +CARBON_PARSE_STATE_VARIANTS2(VarFinish, Regular, Field) // Handles the beginning of a field declaration (`var` in a class context). // diff --git a/toolchain/parse/testdata/for/fail_colon_instead_of_in.carbon b/toolchain/parse/testdata/for/fail_colon_instead_of_in.carbon index e9ff251a69d1..2854f27a8f7d 100644 --- a/toolchain/parse/testdata/for/fail_colon_instead_of_in.carbon +++ b/toolchain/parse/testdata/for/fail_colon_instead_of_in.carbon @@ -27,14 +27,12 @@ fn foo() { // CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, -// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, // CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, -// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ForIn', text: ':', has_error: yes, subtree_size: 6}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ForIn', text: ':', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', has_error: yes, subtree_size: 7}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, // CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, @@ -42,7 +40,7 @@ fn foo() { // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 17}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 23}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 21}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/fail_missing_cond.carbon b/toolchain/parse/testdata/for/fail_missing_cond.carbon index 66fe279e0978..4da3176cceb3 100644 --- a/toolchain/parse/testdata/for/fail_missing_cond.carbon +++ b/toolchain/parse/testdata/for/fail_missing_cond.carbon @@ -13,7 +13,7 @@ fn F() { // CHECK:STDERR: for { // CHECK:STDERR: ^ // CHECK:STDERR: - // CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+4]]:7: error: expected `var` declaration [ExpectedVariableDecl] + // CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+4]]:7: error: expected name in binding pattern [ExpectedBindingPattern] // CHECK:STDERR: for { // CHECK:STDERR: ^ // CHECK:STDERR: @@ -38,14 +38,18 @@ fn F() { // CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ForHeaderStart', text: 'for', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: '{', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '{', has_error: yes}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: '{', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ForIn', text: 'for', has_error: yes, subtree_size: 4}, // CHECK:STDOUT: {kind: 'StructLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'ForHeader', text: 'for', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ForHeader', text: 'for', has_error: yes, subtree_size: 8}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'ExprStatement', text: '}', has_error: yes, subtree_size: 2}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', has_error: yes, subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 9}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 19}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/fail_missing_in.carbon b/toolchain/parse/testdata/for/fail_missing_in.carbon index b7910a81c617..af03ecb3d6da 100644 --- a/toolchain/parse/testdata/for/fail_missing_in.carbon +++ b/toolchain/parse/testdata/for/fail_missing_in.carbon @@ -9,7 +9,7 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/for/fail_missing_in.carbon fn foo() { - // CHECK:STDERR: fail_missing_in.carbon:[[@LINE+4]]:19: error: expected `in` after loop `var` declaration [ExpectedIn] + // CHECK:STDERR: fail_missing_in.carbon:[[@LINE+4]]:19: error: expected `in` after loop pattern [ExpectedIn] // CHECK:STDERR: for (var x: i32 y) { // CHECK:STDERR: ^ // CHECK:STDERR: @@ -27,14 +27,12 @@ fn foo() { // CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, -// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, // CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, -// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ForIn', text: 'var', has_error: yes, subtree_size: 6}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ForIn', text: '(', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', has_error: yes, subtree_size: 7}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, // CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, @@ -42,7 +40,7 @@ fn foo() { // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 17}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 23}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 21}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/fail_missing_var.carbon b/toolchain/parse/testdata/for/fail_missing_var.carbon deleted file mode 100644 index 2a9edf63a057..000000000000 --- a/toolchain/parse/testdata/for/fail_missing_var.carbon +++ /dev/null @@ -1,42 +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 -// TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/for/fail_missing_var.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/for/fail_missing_var.carbon - -fn foo() { - // CHECK:STDERR: fail_missing_var.carbon:[[@LINE+4]]:8: error: expected `var` declaration [ExpectedVariableDecl] - // CHECK:STDERR: for (x: i32 in y) { - // CHECK:STDERR: ^ - // CHECK:STDERR: - for (x: i32 in y) { - Print(x); - } -} - -// CHECK:STDOUT: - filename: fail_missing_var.carbon -// CHECK:STDOUT: parse_tree: [ -// CHECK:STDOUT: {kind: 'FileStart', text: ''}, -// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, -// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'foo'}, -// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, -// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'ForHeader', text: ')', has_error: yes, subtree_size: 3}, -// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, -// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 11}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, -// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, -// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/fail_returned_var.carbon b/toolchain/parse/testdata/for/fail_returned_var.carbon index 6bf697d3d2a3..b951fc4f2376 100644 --- a/toolchain/parse/testdata/for/fail_returned_var.carbon +++ b/toolchain/parse/testdata/for/fail_returned_var.carbon @@ -9,8 +9,7 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/for/fail_returned_var.carbon fn foo() -> i32 { - // TODO: Should we allow this? - // CHECK:STDERR: fail_returned_var.carbon:[[@LINE+4]]:8: error: expected `var` declaration [ExpectedVariableDecl] + // CHECK:STDERR: fail_returned_var.carbon:[[@LINE+4]]:8: error: expected name in binding pattern [ExpectedBindingPattern] // CHECK:STDERR: for (returned var x: i32 in y) { // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: @@ -30,14 +29,17 @@ fn foo() -> i32 { // CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, // CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'ForHeader', text: ')', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'returned', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: 'returned', has_error: yes}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: 'returned', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ForIn', text: '(', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', has_error: yes, subtree_size: 6}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, // CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, // CHECK:STDOUT: {kind: 'ReturnVarModifier', text: 'var'}, // CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 9}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 20}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/fail_square_brackets.carbon b/toolchain/parse/testdata/for/fail_square_brackets.carbon index 656c6f7cba89..2a9cad158166 100644 --- a/toolchain/parse/testdata/for/fail_square_brackets.carbon +++ b/toolchain/parse/testdata/for/fail_square_brackets.carbon @@ -13,7 +13,7 @@ fn F() { // CHECK:STDERR: for [] { // CHECK:STDERR: ^ // CHECK:STDERR: - // CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+16]]:7: error: expected `var` declaration [ExpectedVariableDecl] + // CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+16]]:7: error: expected name in binding pattern [ExpectedBindingPattern] // CHECK:STDERR: for [] { // CHECK:STDERR: ^ // CHECK:STDERR: @@ -42,13 +42,17 @@ fn F() { // CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ForHeaderStart', text: 'for', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: '[', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '[', has_error: yes}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: '[', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ForIn', text: 'for', has_error: yes, subtree_size: 4}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '[', has_error: yes}, -// CHECK:STDOUT: {kind: 'ForHeader', text: 'for', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ForHeader', text: 'for', has_error: yes, subtree_size: 7}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '[', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '[', has_error: yes}, // CHECK:STDOUT: {kind: 'ExprStatement', text: '}', has_error: yes, subtree_size: 2}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '[', has_error: yes, subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 8}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/nested.carbon b/toolchain/parse/testdata/for/nested.carbon index 411ea8011949..70d90e6c1d0b 100644 --- a/toolchain/parse/testdata/for/nested.carbon +++ b/toolchain/parse/testdata/for/nested.carbon @@ -25,24 +25,22 @@ fn foo() { // CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, -// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'y'}, // CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, -// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 5}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 8}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, // CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, -// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'z'}, // CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, -// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 5}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 8}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, // CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, @@ -50,9 +48,9 @@ fn foo() { // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 17}, -// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 19}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 29}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 35}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 18}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 27}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 33}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/pattern.carbon b/toolchain/parse/testdata/for/pattern.carbon new file mode 100644 index 000000000000..3124b32b29d2 --- /dev/null +++ b/toolchain/parse/testdata/for/pattern.carbon @@ -0,0 +1,119 @@ +// 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 +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/for/pattern.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/for/pattern.carbon + +// --- value.carbon + +fn foo() { + for (x: i32 in y) { + Print(x); + } +} + +// --- var.carbon + +fn foo() { + for (var x: i32 in y) { + Print(x); + } +} + +// --- tuple.carbon + +fn foo() { + for ((x: i32, y: i32) in z) { + Print(x); + } +} + +// CHECK:STDOUT: - filename: value.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'foo'}, +// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, +// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, +// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 21}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: var.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'foo'}, +// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, +// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, +// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 22}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: tuple.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'foo'}, +// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'y'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'z'}, +// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, +// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, +// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 21}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 27}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/for/simple.carbon b/toolchain/parse/testdata/for/simple.carbon deleted file mode 100644 index b0cdceb51083..000000000000 --- a/toolchain/parse/testdata/for/simple.carbon +++ /dev/null @@ -1,44 +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 -// TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/for/simple.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/for/simple.carbon - -fn foo() { - for (var x: i32 in y) { - Print(x); - } -} - -// CHECK:STDOUT: - filename: simple.carbon -// CHECK:STDOUT: parse_tree: [ -// CHECK:STDOUT: {kind: 'FileStart', text: ''}, -// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, -// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'foo'}, -// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, -// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ForHeaderStart', text: '('}, -// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, -// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, -// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ForIn', text: 'in', subtree_size: 6}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'ForHeader', text: ')', subtree_size: 9}, -// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Print'}, -// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 7}, -// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 17}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 23}, -// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, -// CHECK:STDOUT: ] diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 5b11a2b5bf3f..8b8c03e9c4e1 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -675,17 +675,15 @@ struct ReturnStatement { using ForHeaderStart = LeafNode; -// The `var ... in` portion of a `for` statement. +// The `... in` portion of a `for` statement. struct ForIn { - static constexpr auto Kind = NodeKind::ForIn.Define( - {.bracketed_by = VariableIntroducer::Kind, .child_count = 2}); + static constexpr auto Kind = NodeKind::ForIn.Define({.child_count = 1}); - VariableIntroducerId introducer; AnyPatternId pattern; Lex::InTokenIndex token; }; -// The `for (var ... in ...)` portion of a `for` statement. +// The `for (... in ...)` portion of a `for` statement. struct ForHeader { static constexpr auto Kind = NodeKind::ForHeader.Define({.bracketed_by = ForHeaderStart::Kind}); diff --git a/toolchain/parse/typed_nodes_test.cpp b/toolchain/parse/typed_nodes_test.cpp index 53c9d5edb2c4..96dc4634b903 100644 --- a/toolchain/parse/typed_nodes_test.cpp +++ b/toolchain/parse/typed_nodes_test.cpp @@ -132,7 +132,7 @@ TEST_F(TypedNodeTest, For) { auto for_var_pattern = tree.ExtractAs(for_var->pattern); ASSERT_TRUE(for_var_pattern.has_value()); auto for_var_binding = - tree.ExtractAs(for_var_pattern->inner); + tree.ExtractAs(for_var_pattern->inner); ASSERT_TRUE(for_var_binding.has_value()); auto for_var_name = tree.ExtractAs(for_var_binding->name);