diff --git a/toolchain/check/testdata/class/self/fail_self_type_member.carbon b/toolchain/check/testdata/class/self/fail_self_type_member.carbon index b2242bfac09e..da7f2686bc24 100644 --- a/toolchain/check/testdata/class/self/fail_self_type_member.carbon +++ b/toolchain/check/testdata/class/self/fail_self_type_member.carbon @@ -16,13 +16,13 @@ class Class { fn F() -> bool { var c1: Class = {.b = true}; - // CHECK:STDERR: fail_self_type_member.carbon:[[@LINE+8]]:17: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_self_type_member.carbon:[[@LINE+8]]:17: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: var c2: Class.Self = c1; // CHECK:STDERR: ^~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_self_type_member.carbon:[[@LINE+4]]:17: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] + // CHECK:STDERR: fail_self_type_member.carbon:[[@LINE+4]]:11: error: member name `r#Self` not found in `Class` [MemberNameNotFoundInInstScope] // CHECK:STDERR: var c2: Class.Self = c1; - // CHECK:STDERR: ^~~~ + // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: var c2: Class.Self = c1; return c2.b; diff --git a/toolchain/check/testdata/struct/fail_keyword_name.carbon b/toolchain/check/testdata/struct/fail_keyword_name.carbon index 741601e9a6f3..185b0bc1f261 100644 --- a/toolchain/check/testdata/struct/fail_keyword_name.carbon +++ b/toolchain/check/testdata/struct/fail_keyword_name.carbon @@ -3,8 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon -// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". -// EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: @@ -12,21 +10,20 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_keyword_name.carbon -// CHECK:STDERR: fail_keyword_name.carbon:[[@LINE+8]]:13: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] -// CHECK:STDERR: fn F() -> {.class: i32}; -// CHECK:STDERR: ^~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_keyword_name.carbon:[[@LINE+4]]:13: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: fail_keyword_name.carbon:[[@LINE+4]]:13: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: fn F() -> {.class: i32}; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn F() -> {.class: i32}; -// CHECK:STDERR: fail_keyword_name.carbon:[[@LINE+4]]:19: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] -// CHECK:STDERR: fn G() { return {.return = 5}; }; -// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: fail_keyword_name.carbon:[[@LINE+4]]:13: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] +// CHECK:STDERR: fn G() -> {.return: i32} { +// CHECK:STDERR: ^~~~~~ // CHECK:STDERR: -fn G() { return {.return = 5}; }; - -// CHECK:STDOUT: --- fail_keyword_name.carbon -// CHECK:STDOUT: +fn G() -> {.return: i32} { + // CHECK:STDERR: fail_keyword_name.carbon:[[@LINE+4]]:12: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: return {.return = 5}; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + return {.return = 5}; +}; diff --git a/toolchain/lex/token_info.h b/toolchain/lex/token_info.h index fcbf22715be4..2841718ca36e 100644 --- a/toolchain/lex/token_info.h +++ b/toolchain/lex/token_info.h @@ -133,6 +133,15 @@ class TokenInfo { *this = error; } + // Resets the token to be an identifier with the given identifier id at its + // original position and with the same whitespace adjacency. + auto ResetAsErrorRecoveryIdentifier(IdentifierId id) -> void { + CARBON_CHECK(kind().is_word()); + TokenInfo error(TokenKind::Identifier, has_leading_space(), id.index, + byte_offset()); + *this = error; + } + private: friend class Lexer; diff --git a/toolchain/lex/token_kind.h b/toolchain/lex/token_kind.h index 352a5ae3bf0e..38befb2eb308 100644 --- a/toolchain/lex/token_kind.h +++ b/toolchain/lex/token_kind.h @@ -86,6 +86,18 @@ class TokenKind : public CARBON_ENUM_BASE(TokenKind) { *this == TokenKind::FloatTypeLiteral; } + // Test whether this kind of token is a word. + auto is_word() const -> bool { + return *this == TokenKind::Identifier || *this == TokenKind::Underscore || + is_keyword() || is_sized_type_literal(); + } + + // Test whether this kind of token is a binding pattern operator. + auto is_binding_pattern_operator() const -> bool { + return *this == TokenKind::Colon || *this == TokenKind::ColonExclaim || + *this == TokenKind::ColonQuestion; + } + // If this token kind has a fixed spelling when in source code, returns it. // Otherwise returns an empty string. auto fixed_spelling() const -> llvm::StringLiteral { diff --git a/toolchain/lex/tokenized_buffer.cpp b/toolchain/lex/tokenized_buffer.cpp index f23edde0e047..5738ffffe10b 100644 --- a/toolchain/lex/tokenized_buffer.cpp +++ b/toolchain/lex/tokenized_buffer.cpp @@ -204,6 +204,32 @@ auto TokenizedBuffer::IsRecoveryToken(TokenIndex token) const -> bool { return recovery_tokens_[token.index]; } +auto TokenizedBuffer::AddPostLexingRecoveryTokenAsIdentifier(TokenIndex token) + -> TokenIndex { + auto kind = GetKind(token); + CARBON_CHECK(kind.is_word(), + "Invalid token kind {0} for recovery as identifier", kind); + CARBON_CHECK(kind != TokenKind::Identifier, "Recovery not required"); + + auto identifier_id = value_stores_->identifiers().Add(GetTokenText(token)); + auto info = token_infos_.Get(token); + info.ResetAsErrorRecoveryIdentifier(identifier_id); + return AddPostLexingRecoveryToken(info); +} + +auto TokenizedBuffer::AddPostLexingRecoveryToken(TokenInfo info) -> TokenIndex { + // Only resize once to avoid quadratic behavior if lots of recovery tokens are + // added. + if (recovery_tokens_.empty()) { + recovery_tokens_.resize(token_infos_.size()); + } + + auto token = token_infos_.Add(info); + recovery_tokens_.push_back(true); + ++post_lexing_recovery_tokens_; + return token; +} + auto TokenizedBuffer::GetIndentColumnNumber(LineIndex line) const -> int { return line_infos_.Get(line).indent + 1; } diff --git a/toolchain/lex/tokenized_buffer.h b/toolchain/lex/tokenized_buffer.h index 1d964b2088d4..780ce9f3b586 100644 --- a/toolchain/lex/tokenized_buffer.h +++ b/toolchain/lex/tokenized_buffer.h @@ -176,6 +176,12 @@ class TokenizedBuffer : public Printable { // For example, a closing paren inserted to match an unmatched paren. auto IsRecoveryToken(TokenIndex token) const -> bool; + // Adds and returns the index of an identifier token formed from the spelling + // of the given word token. This should only be used for error recovery + // purposes, when a phase after the lexer determines that a word token was + // intended to represent an identifier rather than its special meaning. + auto AddPostLexingRecoveryTokenAsIdentifier(TokenIndex token) -> TokenIndex; + // Returns the 1-based indentation column number. auto GetIndentColumnNumber(LineIndex line) const -> int; @@ -221,9 +227,14 @@ class TokenizedBuffer : public Printable { // Returns true if the buffer has errors that were detected at lexing time. auto has_errors() const -> bool { return has_errors_; } + // Returns the tokens produced by lexing the source file. This includes any + // recovery tokens inserted inline by lexing, such as matching brackets, but + // excludes recovery tokens added after lexing finished. auto tokens() const -> llvm::iterator_range { - return llvm::make_range(TokenIterator(TokenIndex(0)), - TokenIterator(TokenIndex(token_infos_.size()))); + return llvm::make_range( + TokenIterator(TokenIndex(0)), + TokenIterator( + TokenIndex(token_infos_.size() - post_lexing_recovery_tokens_))); } auto size() const -> int { return token_infos_.size(); } @@ -317,6 +328,9 @@ class TokenizedBuffer : public Printable { // Adds the token and adjusts the expected tree size. auto AddToken(TokenInfo info) -> TokenIndex; + // Adds a post-lexing recovery token and adjusts the buffer state. + auto AddPostLexingRecoveryToken(TokenInfo info) -> TokenIndex; + auto GetTokenPrintWidths(TokenIndex token) const -> PrintWidths; auto PrintToken(llvm::raw_ostream& output_stream, TokenIndex token, PrintWidths widths) const -> void; @@ -357,6 +371,11 @@ class TokenizedBuffer : public Printable { bool has_errors_ = false; + // The number of recovery tokens created after lexing finished. These tokens + // are included in `token_infos_`, but are excluded from the token sequence + // produced by `tokens()`. + int32_t post_lexing_recovery_tokens_ = 0; + // A vector of flags for recovery tokens. If empty, there are none. When doing // token recovery, this will be extended to be indexable by token indices and // contain true for the tokens that were synthesized for recovery. diff --git a/toolchain/parse/context.h b/toolchain/parse/context.h index be6a238b2cec..8b980025fc19 100644 --- a/toolchain/parse/context.h +++ b/toolchain/parse/context.h @@ -421,7 +421,7 @@ class Context { auto tree() const -> const Tree& { return *tree_; } - auto tokens() const -> const Lex::TokenizedBuffer& { return *tokens_; } + auto tokens() const -> Lex::TokenizedBuffer& { return *tokens_; } auto has_errors() const -> bool { return err_tracker_.seen_error(); } diff --git a/toolchain/parse/handle_binding_pattern.cpp b/toolchain/parse/handle_binding_pattern.cpp index 4777c34c985f..a0800716a794 100644 --- a/toolchain/parse/handle_binding_pattern.cpp +++ b/toolchain/parse/handle_binding_pattern.cpp @@ -12,15 +12,16 @@ auto HandleBindingPattern(Context& context) -> void { auto state = context.PopState(); // Handle an invalid pattern introducer for parameters and variables. - auto on_error = [&](bool expected_name) { + auto on_error = [&](bool expected_name, bool recover_as_raw = false) { if (!state.has_error) { CARBON_DIAGNOSTIC( ExpectedBindingPattern, Error, - "expected {0:name|`:`, `:!`, or `:?`} in binding pattern", - Diagnostics::BoolAsSelect); + "expected {0:name|`:`, `:!`, or `:?`} in binding pattern" + "{1:; prefix reserved word with `r#` to form a valid identifier|}", + Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect); context.emitter().Emit(*context.position(), ExpectedBindingPattern, - expected_name); - state.has_error = true; + expected_name, recover_as_raw); + state.has_error = !recover_as_raw; } }; @@ -44,6 +45,19 @@ auto HandleBindingPattern(Context& context) -> void { context.AddLeafNode(NodeKind::SelfValueName, *self); } else if (auto underscore = context.ConsumeIf(Lex::TokenKind::Underscore)) { context.AddLeafNode(NodeKind::UnderscoreName, *underscore); + } else if (context.PositionKind().is_word() && + context.PositionKind(Lookahead::NextToken) + .is_binding_pattern_operator()) { + // A word token that is not a valid binding name appeared before the `:`, + // such as a numeric type literal or a keyword. For error recovery, convert + // the token to an identifier, as we can be confident that a word in this + // position was intended to be a declared name. + auto word_as_identifier = + context.tokens().AddPostLexingRecoveryTokenAsIdentifier( + context.Consume()); + context.AddLeafNode(NodeKind::IdentifierNameNotBeforeSignature, + word_as_identifier); + on_error(/*expected_name=*/true, /*recover_as_raw*/ true); } else { // Add a placeholder for the name. context.AddLeafNode(NodeKind::IdentifierNameNotBeforeSignature, @@ -52,9 +66,7 @@ auto HandleBindingPattern(Context& context) -> void { } if (auto token_kind = context.PositionKind(); - token_kind == Lex::TokenKind::Colon || - token_kind == Lex::TokenKind::ColonExclaim || - token_kind == Lex::TokenKind::ColonQuestion) { + token_kind.is_binding_pattern_operator()) { // Add the wrapper node for the `template` keyword if present. if (template_token) { if (token_kind != Lex::TokenKind::ColonExclaim && !state.has_error) { diff --git a/toolchain/parse/handle_pattern.cpp b/toolchain/parse/handle_pattern.cpp index 8740e06d01ad..d3f54cdd27be 100644 --- a/toolchain/parse/handle_pattern.cpp +++ b/toolchain/parse/handle_pattern.cpp @@ -7,12 +7,6 @@ namespace Carbon::Parse { -static auto IsBindingPatternOperator(Lex::TokenKind kind) -> bool { - return kind == Lex::TokenKind::Colon || - kind == Lex::TokenKind::ColonExclaim || - kind == Lex::TokenKind::ColonQuestion; -} - auto HandlePattern(Context& context) -> void { auto state = context.PopState(); switch (context.PositionKind()) { @@ -37,19 +31,15 @@ auto HandlePattern(Context& context) -> void { state.in_var_pattern, state.in_unused_pattern, state.ambient_precedence); break; - case Lex::TokenKind::Identifier: - case Lex::TokenKind::SelfValueIdentifier: - case Lex::TokenKind::Underscore: { - if (IsBindingPatternOperator( - context.PositionKind(Lookahead::NextToken))) { + default: + if (context.PositionKind().is_word() && + context.PositionKind(Lookahead::NextToken) + .is_binding_pattern_operator()) { context.PushStateForPattern( StateKind::BindingPattern, state.in_var_pattern, state.in_unused_pattern, state.ambient_precedence); break; } - [[fallthrough]]; - } - default: context.PushState(StateKind::ExprPattern); context.PushStateForExpr(state.ambient_precedence); break; @@ -63,7 +53,7 @@ auto HandleExprPattern(Context& context) -> void { // have a malformed attempt to introduce a binding pattern that we interpreted // as an expression pattern, so diagnose that here rather than diagnosing a // missing `;` at an outer level. - if (IsBindingPatternOperator(context.PositionKind())) { + if (context.PositionKind().is_binding_pattern_operator()) { if (!state.has_error) { CARBON_DIAGNOSTIC(ExpectedBindingName, Error, "unexpected expression before {0} in binding pattern", diff --git a/toolchain/parse/handle_period.cpp b/toolchain/parse/handle_period.cpp index 881d5e3f9946..939573329ca5 100644 --- a/toolchain/parse/handle_period.cpp +++ b/toolchain/parse/handle_period.cpp @@ -38,16 +38,23 @@ static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind, context.PushState(StateKind::OnlyParenExpr); return; } else { - CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPeriodOrArrow, Error, - "expected identifier after `{0:->|.}`", - Diagnostics::BoolAsSelect); + bool recover_as_raw = context.PositionKind().is_word(); + CARBON_DIAGNOSTIC( + ExpectedIdentifierAfterPeriodOrArrow, Error, + "expected identifier after `{0:->|.}`" + "{1:; prefix reserved word with `r#` to form a valid identifier|}", + Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect); context.emitter().Emit(*context.position(), - ExpectedIdentifierAfterPeriodOrArrow, is_arrow); - // If we see a keyword, assume it was intended to be a name. - // TODO: Should keywords be valid here? - if (context.PositionKind().is_keyword()) { + ExpectedIdentifierAfterPeriodOrArrow, is_arrow, + recover_as_raw); + // If we see a word, assume it was intended to be a name. + // TODO: Should word tokens be valid here? + if (recover_as_raw) { + auto word_as_identifier = + context.tokens().AddPostLexingRecoveryTokenAsIdentifier( + context.Consume()); context.AddLeafNode(NodeKind::IdentifierNameNotBeforeSignature, - context.Consume(), /*has_error=*/true); + word_as_identifier); } else { context.AddLeafNode(NodeKind::IdentifierNameNotBeforeSignature, *context.position(), /*has_error=*/true); diff --git a/toolchain/parse/testdata/basics/fail_invalid_designators.carbon b/toolchain/parse/testdata/basics/fail_invalid_designators.carbon index 7f6a3039cf17..cdd460b7b39d 100644 --- a/toolchain/parse/testdata/basics/fail_invalid_designators.carbon +++ b/toolchain/parse/testdata/basics/fail_invalid_designators.carbon @@ -15,7 +15,7 @@ fn F() { // CHECK:STDERR: ^ // CHECK:STDERR: a.; - // CHECK:STDERR: fail_invalid_designators.carbon:[[@LINE+4]]:5: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_invalid_designators.carbon:[[@LINE+4]]:5: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: a.fn; // CHECK:STDERR: ^~ // CHECK:STDERR: @@ -35,7 +35,7 @@ fn F() { // CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 4}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'fn', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'fn'}, // CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, diff --git a/toolchain/parse/testdata/member_access/fail_keyword.carbon b/toolchain/parse/testdata/member_access/fail_keyword.carbon index 6b0760f84453..f4de7870c932 100644 --- a/toolchain/parse/testdata/member_access/fail_keyword.carbon +++ b/toolchain/parse/testdata/member_access/fail_keyword.carbon @@ -9,32 +9,32 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/member_access/fail_keyword.carbon fn F() { - // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:5: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:5: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: a.self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: a.self; - // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:5: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:5: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: a.Self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: a.Self; - // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:5: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:5: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: a.for; // CHECK:STDERR: ^~~ // CHECK:STDERR: a.for; - // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:6: error: expected identifier after `->` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:6: error: expected identifier after `->`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: p->self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: p->self; - // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:6: error: expected identifier after `->` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:6: error: expected identifier after `->`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: p->Self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: p->Self; - // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:6: error: expected identifier after `->` [ExpectedIdentifierAfterPeriodOrArrow] + // CHECK:STDERR: fail_keyword.carbon:[[@LINE+4]]:6: error: expected identifier after `->`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: p->while; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: @@ -50,27 +50,27 @@ fn F() { // CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'self', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'self'}, // CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'Self', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'Self'}, // CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'for', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'for'}, // CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'p'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'self', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'self'}, // CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'p'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'Self', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'Self'}, // CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'p'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'while', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'while'}, // CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 30}, diff --git a/toolchain/parse/testdata/package_expr/fail_in_name.carbon b/toolchain/parse/testdata/package_expr/fail_in_name.carbon index a70a87fac06b..e8efdcaac3e9 100644 --- a/toolchain/parse/testdata/package_expr/fail_in_name.carbon +++ b/toolchain/parse/testdata/package_expr/fail_in_name.carbon @@ -15,7 +15,7 @@ var package.n: i32; // `val` is a keyword, but during error recovery we treat it as an identifier. -// CHECK:STDERR: fail_in_name.carbon:[[@LINE+8]]:13: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] +// CHECK:STDERR: fail_in_name.carbon:[[@LINE+8]]:13: error: expected identifier after `.`; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: var package.val: i32; // CHECK:STDERR: ^~~ // CHECK:STDERR: @@ -49,7 +49,7 @@ class package.C { // CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 6}, // CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, // CHECK:STDOUT: {kind: 'PackageExpr', text: 'package'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'val', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'val'}, // CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', has_error: yes, subtree_size: 4}, // CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 6}, diff --git a/toolchain/parse/testdata/var/fail_bad_name.carbon b/toolchain/parse/testdata/var/fail_bad_name.carbon index 9acc2a081a6e..1833824cc131 100644 --- a/toolchain/parse/testdata/var/fail_bad_name.carbon +++ b/toolchain/parse/testdata/var/fail_bad_name.carbon @@ -14,6 +14,18 @@ // CHECK:STDERR: var *; +// CHECK:STDERR: fail_bad_name.carbon:[[@LINE+4]]:11: error: expected name in binding pattern; prefix reserved word with `r#` to form a valid identifier [ExpectedBindingPattern] +// CHECK:STDERR: var choice: i32; +// CHECK:STDERR: ^ +// CHECK:STDERR: +var choice: i32; + +// CHECK:STDERR: fail_bad_name.carbon:[[@LINE+4]]:7: error: expected name in binding pattern; prefix reserved word with `r#` to form a valid identifier [ExpectedBindingPattern] +// CHECK:STDERR: var f2: f32; +// CHECK:STDERR: ^ +// CHECK:STDERR: +var f2: f32; + // CHECK:STDOUT: - filename: fail_bad_name.carbon // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, @@ -22,5 +34,17 @@ var *; // CHECK:STDOUT: {kind: 'PrefixOperatorStar', text: '*', has_error: yes, subtree_size: 2}, // CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'choice'}, +// 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: 'VariableDecl', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'f2'}, +// CHECK:STDOUT: {kind: 'FloatTypeLiteral', text: 'f32'}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 6}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ]