diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index 9402f862d829..9dc16d8bdff0 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/toolchain/parser/parse_node_kind.def @@ -43,8 +43,8 @@ // The end of the file. CARBON_PARSE_NODE_KIND_CHILD_COUNT(FileEnd, 0) -// An invalid expression. Used to balance the parse tree. Always has an error. -CARBON_PARSE_NODE_KIND_CHILD_COUNT(InvalidExpression, 0) +// An invalid parse. Used to balance the parse tree. Always has an error. +CARBON_PARSE_NODE_KIND_CHILD_COUNT(InvalidParse, 0) // An empty declaration, such as `;`. CARBON_PARSE_NODE_KIND_CHILD_COUNT(EmptyDeclaration, 0) diff --git a/toolchain/parser/parser.cpp b/toolchain/parser/parser.cpp index 7d52caa99bdb..4e5d6be83963 100644 --- a/toolchain/parser/parser.cpp +++ b/toolchain/parser/parser.cpp @@ -959,8 +959,7 @@ auto Parser::HandleExpressionInPostfixState() -> void { } default: { // Add a node to keep the parse tree balanced. - AddLeafNode(ParseNodeKind::InvalidExpression, *position_, - /*has_error=*/true); + AddLeafNode(ParseNodeKind::InvalidParse, *position_, /*has_error=*/true); CARBON_DIAGNOSTIC(ExpectedExpression, Error, "Expected expression."); emitter_->Emit(*position_, ExpectedExpression); ReturnErrorOnState(); @@ -1490,23 +1489,27 @@ auto Parser::HandlePattern(PatternKind pattern_kind) -> void { break; } } - // Still use the finish state for errors. + // Add a placeholder for the type. + AddLeafNode(ParseNodeKind::InvalidParse, *position_, /*has_error=*/true); state.state = ParserState::PatternFinishAsRegular; state.has_error = true; PushState(state); }; // The first item should be an identifier or, for deduced parameters, `self`. + bool has_name = false; if (auto identifier = ConsumeIf(TokenKind::Identifier)) { AddLeafNode(ParseNodeKind::DeclaredName, *identifier); + has_name = true; } else if (pattern_kind == PatternKind::DeducedParameter) { if (auto self = ConsumeIf(TokenKind::SelfValueIdentifier)) { AddLeafNode(ParseNodeKind::SelfValueIdentifier, *self); - } else { - on_error(); - return; + has_name = true; } - } else { + } + if (!has_name) { + // Add a placeholder for the name. + AddLeafNode(ParseNodeKind::DeclaredName, *position_, /*has_error=*/true); on_error(); return; } @@ -1541,14 +1544,13 @@ auto Parser::HandlePatternAsVariableState() -> void { auto Parser::HandlePatternFinish(ParseNodeKind node_kind) -> void { auto state = PopState(); - // If an error was encountered, propagate it without adding a node. + AddNode(node_kind, state.token, state.subtree_start, state.has_error); + + // Propagate errors to the parent state so that they can take different + // actions on invalid patterns. if (state.has_error) { ReturnErrorOnState(); - return; } - - // TODO: may need to mark has_error if !type. - AddNode(node_kind, state.token, state.subtree_start, /*has_error=*/false); } auto Parser::HandlePatternFinishAsGenericState() -> void { diff --git a/toolchain/parser/testdata/basics/fail_paren_match_regression.carbon b/toolchain/parser/testdata/basics/fail_paren_match_regression.carbon index 0a36274d2e07..8bd683f87e00 100644 --- a/toolchain/parser/testdata/basics/fail_paren_match_regression.carbon +++ b/toolchain/parser/testdata/basics/fail_paren_match_regression.carbon @@ -6,11 +6,14 @@ // RUN: %{not} %{carbon-run-parser} // CHECK:STDOUT: [ // CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: '=', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '=', has_error: yes}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: '=', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'ParenExpressionOrTupleLiteralStart', text: '('}, // CHECK:STDOUT: {kind: 'NameReference', text: 'foo'}, // CHECK:STDOUT: {kind: 'ParenExpression', text: ')', has_error: yes, subtree_size: 3}, -// CHECK:STDOUT: {kind: 'VariableDeclaration', text: 'var', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: 'var', has_error: yes, subtree_size: 9}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon b/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon index 5eaf8e1c93e7..9b8ad08822d0 100644 --- a/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_missing_deduced_close.carbon @@ -8,8 +8,11 @@ // CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'Div'}, // CHECK:STDOUT: {kind: 'DeducedParameterListStart', text: '['}, -// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', has_error: yes, subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: 'fn', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: '(', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '(', has_error: yes}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: '(', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: 'fn', has_error: yes, subtree_size: 8}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/function/declaration/fail_with_identifier_as_param.carbon b/toolchain/parser/testdata/function/declaration/fail_with_identifier_as_param.carbon index 7ea5f6a8d279..3fcd7cba89ee 100644 --- a/toolchain/parser/testdata/function/declaration/fail_with_identifier_as_param.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_with_identifier_as_param.carbon @@ -8,9 +8,11 @@ // CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'foo'}, // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, -// CHECK:STDOUT: {kind: 'DeclaredName', text: 'bar'}, -// CHECK:STDOUT: {kind: 'ParameterList', text: ')', has_error: yes, subtree_size: 3}, -// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'bar'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ')', has_error: yes}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: 'bar', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 8}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/generics/interface/fail_self_param_syntax.carbon b/toolchain/parser/testdata/generics/interface/fail_self_param_syntax.carbon index 13c186c3ec28..0acbcf47dadc 100644 --- a/toolchain/parser/testdata/generics/interface/fail_self_param_syntax.carbon +++ b/toolchain/parser/testdata/generics/interface/fail_self_param_syntax.carbon @@ -11,8 +11,10 @@ // CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'Sub'}, // CHECK:STDOUT: {kind: 'DeducedParameterListStart', text: '['}, -// CHECK:STDOUT: {kind: 'DeclaredName', text: 'me'}, -// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'me'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: 'Self', has_error: yes}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: 'me', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'}, // CHECK:STDOUT: {kind: 'SelfTypeIdentifier', text: 'Self'}, @@ -20,11 +22,14 @@ // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5}, // CHECK:STDOUT: {kind: 'SelfTypeIdentifier', text: 'Self'}, // CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 15}, // CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'Mul'}, // CHECK:STDOUT: {kind: 'DeducedParameterListStart', text: '['}, -// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'Self', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: 'Self', has_error: yes}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: 'Self', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'}, // CHECK:STDOUT: {kind: 'SelfTypeIdentifier', text: 'Self'}, @@ -32,8 +37,8 @@ // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5}, // CHECK:STDOUT: {kind: 'SelfTypeIdentifier', text: 'Self'}, // CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 12}, -// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 29}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 34}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/if/fail_errors.carbon b/toolchain/parser/testdata/if/fail_errors.carbon index 30f611e69894..1c797ad2c328 100644 --- a/toolchain/parser/testdata/if/fail_errors.carbon +++ b/toolchain/parser/testdata/if/fail_errors.carbon @@ -17,7 +17,7 @@ // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 2}, // CHECK:STDOUT: {kind: 'IfStatement', text: 'if', subtree_size: 6}, // CHECK:STDOUT: {kind: 'IfConditionStart', text: '('}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: ')', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ')', has_error: yes}, // CHECK:STDOUT: {kind: 'IfCondition', text: ')', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 2}, @@ -32,7 +32,7 @@ // CHECK:STDOUT: {kind: 'NameReference', text: 'd'}, // CHECK:STDOUT: {kind: 'IfCondition', text: ')', subtree_size: 3}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: '}', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: '}', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'CodeBlock', text: '}', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'IfStatement', text: 'if', subtree_size: 7}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 31}, diff --git a/toolchain/parser/testdata/operators/fail_invalid_infix.carbon b/toolchain/parser/testdata/operators/fail_invalid_infix.carbon index b40093181b90..918b0af27ada 100644 --- a/toolchain/parser/testdata/operators/fail_invalid_infix.carbon +++ b/toolchain/parser/testdata/operators/fail_invalid_infix.carbon @@ -11,7 +11,7 @@ // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'NameReference', text: 'n'}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '==', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 9}, // CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, @@ -19,7 +19,7 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: '==', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '==', has_error: yes}, // CHECK:STDOUT: {kind: 'NameReference', text: 'n'}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '==', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 9}, @@ -28,8 +28,8 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: '==', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '==', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '==', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 9}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parser/testdata/return/fail_no_semi.carbon b/toolchain/parser/testdata/return/fail_no_semi.carbon index 10409d40d92c..e79b6b56b51f 100644 --- a/toolchain/parser/testdata/return/fail_no_semi.carbon +++ b/toolchain/parser/testdata/return/fail_no_semi.carbon @@ -11,7 +11,7 @@ // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: '}', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'ReturnStatement', text: 'return', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 9}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parser/testdata/struct/fail_missing_type.carbon b/toolchain/parser/testdata/struct/fail_missing_type.carbon index 40ed69614762..f13c22fc0bc3 100644 --- a/toolchain/parser/testdata/struct/fail_missing_type.carbon +++ b/toolchain/parser/testdata/struct/fail_missing_type.carbon @@ -10,7 +10,7 @@ // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'DesignatedName', text: 'a'}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: '}', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'StructFieldUnknown', text: ':', has_error: yes}, // CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', subtree_size: 6}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 8}, diff --git a/toolchain/parser/testdata/struct/fail_missing_value.carbon b/toolchain/parser/testdata/struct/fail_missing_value.carbon index f5f0fa7f0948..e7874cb78f1b 100644 --- a/toolchain/parser/testdata/struct/fail_missing_value.carbon +++ b/toolchain/parser/testdata/struct/fail_missing_value.carbon @@ -10,7 +10,7 @@ // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'DesignatedName', text: 'a'}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'InvalidExpression', text: '}', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'StructFieldUnknown', text: '=', has_error: yes}, // CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 6}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 8}, diff --git a/toolchain/parser/testdata/var/fail_bad_name.carbon b/toolchain/parser/testdata/var/fail_bad_name.carbon new file mode 100644 index 000000000000..f1b59db3f57e --- /dev/null +++ b/toolchain/parser/testdata/var/fail_bad_name.carbon @@ -0,0 +1,17 @@ +// 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: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: '*', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '*', has_error: yes}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: '*', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/var/fail_bad_name.carbon:[[@LINE+1]]:5: Expected pattern in `var` declaration. +var *; diff --git a/toolchain/parser/testdata/var/fail_empty.carbon b/toolchain/parser/testdata/var/fail_empty.carbon new file mode 100644 index 000000000000..6183c4407d8a --- /dev/null +++ b/toolchain/parser/testdata/var/fail_empty.carbon @@ -0,0 +1,17 @@ +// 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: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/var/fail_empty.carbon:[[@LINE+1]]:4: Expected pattern in `var` declaration. +var; diff --git a/toolchain/parser/testdata/basics/var.carbon b/toolchain/parser/testdata/var/var.carbon similarity index 97% rename from toolchain/parser/testdata/basics/var.carbon rename to toolchain/parser/testdata/var/var.carbon index cb858ec8c910..0460570cd10b 100644 --- a/toolchain/parser/testdata/basics/var.carbon +++ b/toolchain/parser/testdata/var/var.carbon @@ -33,7 +33,6 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// NOTE: Move to its own directory when more tests are added. var v: i32 = 0; var w: i32; fn F() { diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index 5f3241d9386e..9b6977150365 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/toolchain/semantics/semantics_parse_tree_handler.cpp @@ -732,9 +732,9 @@ auto SemanticsParseTreeHandler::HandleInterfaceIntroducer( return false; } -auto SemanticsParseTreeHandler::HandleInvalidExpression( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleInvalidExpression"); +auto SemanticsParseTreeHandler::HandleInvalidParse(ParseTree::Node parse_node) + -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleInvalidParse"); return false; }