Skip to content

Commit

Permalink
On invalid parse nodes, if the token may vary, allow any token.
Browse files Browse the repository at this point in the history
  • Loading branch information
jonmeow committed Dec 11, 2023
1 parent 4ccc684 commit 29528fe
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 66 deletions.
13 changes: 8 additions & 5 deletions toolchain/parse/node_kind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ auto NodeKind::child_count() const -> int32_t {
// NOLINTNEXTLINE(readability-function-size): It's hard to extract macros.
auto CheckNodeMatchesLexerToken(NodeKind node_kind, Lex::TokenKind token_kind,
bool has_error) -> void {
// As a special-case, a placeholder node may correspond to any lexer token.
if (node_kind == NodeKind::Placeholder) {
return;
}

switch (node_kind) {
// Use `CARBON_LOG CARBON_ANY_TOKEN` to discover which combinations happen
// in practice.
Expand All @@ -58,16 +63,14 @@ auto CheckNodeMatchesLexerToken(NodeKind node_kind, Lex::TokenKind token_kind,
<< " and has_error " << has_error << " for lexical token " \
<< token_kind << "\n";

#define CARBON_ANY_TOKEN return;

#define CARBON_TOKEN(Expected) \
if (token_kind == Lex::TokenKind::Expected) { \
return; \
}

#define CARBON_IF_ERROR(MatchActions) \
if (has_error) { \
MatchActions \
#define CARBON_ANY_TOKEN_ON_ERROR \
if (has_error) { \
return; \
}

#define CARBON_CASE(Name, MatchActions) \
Expand Down
99 changes: 38 additions & 61 deletions toolchain/parse/node_kind.def
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@
// In both cases, LexTokenKinds says which Lex::TokenKind values that this
// parse node can correspond to, and is a sequence of:
// - CARBON_TOKEN(kind): This node can correspond to this kind of token.
// - CARBON_ANY_TOKEN: This node can correspond to any token.
// - CARBON_IF_ERROR(LexTokenKinds): This node can additionally correspond
// to the given kinds of tokens if its `has_error` flag is set.
// - CARBON_ANY_TOKEN_ON_ERROR: When the `has_error` flag is set, this node
// can correspond to any token.
//
// This tree represents the subset relationship between these macros, where if a
// specific x-macro isn't defined, it'll fall back to the parent macro.
Expand Down Expand Up @@ -64,8 +63,7 @@
// #define CARBON_PARSE_NODE_KIND_INFIX_OPERATOR(Name, ...) <code>
#ifndef CARBON_PARSE_NODE_KIND_INFIX_OPERATOR
#define CARBON_PARSE_NODE_KIND_INFIX_OPERATOR(Name) \
CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator##Name, 2, \
CARBON_TOKEN(Name))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator##Name, 2, CARBON_TOKEN(Name))
#endif

// This is expected to be used with something like:
Expand All @@ -74,7 +72,7 @@
// #define CARBON_PARSE_NODE_KIND(...)
// #define CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Name, ...) <code>
#ifndef CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR
#define CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Name) \
#define CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Name) \
CARBON_PARSE_NODE_KIND_CHILD_COUNT(PrefixOperator##Name, 1, \
CARBON_TOKEN(Name))
#endif
Expand Down Expand Up @@ -106,27 +104,26 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(FileStart, 0, CARBON_TOKEN(FileStart))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(FileEnd, 0, CARBON_TOKEN(FileEnd))

// An invalid parse. Used to balance the parse tree. Always has an error.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(InvalidParse, 0,
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(InvalidParse, 0, CARBON_ANY_TOKEN_ON_ERROR)

// An invalid subtree. Always has an error.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(InvalidParseStart, 0,
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_BRACKET(InvalidParseSubtree, InvalidParseStart,
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_ANY_TOKEN_ON_ERROR)

// A placeholder node to be replaced.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(Placeholder, 0, CARBON_ANY_TOKEN)
// A placeholder node to be replaced. Its token kind is not enforced even when
// valid, as a special-case in node_kind.cpp.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(Placeholder, 0, CARBON_ANY_TOKEN_ON_ERROR)

// An empty declaration, such as `;`.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(EmptyDecl, 0,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// An identifier name in a non-expression context, such as a declaration.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IdentifierName, 0,
CARBON_TOKEN(Identifier)
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_ANY_TOKEN_ON_ERROR)

// An identifier name in an expression context.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IdentifierNameExpr, 0,
Expand Down Expand Up @@ -190,8 +187,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageIntroducer, 0, CARBON_TOKEN(Package))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageApi, 0, CARBON_TOKEN(Api))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageImpl, 0, CARBON_TOKEN(Impl))
CARBON_PARSE_NODE_KIND_BRACKET(PackageDirective, PackageIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Package)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `import`:
// ImportIntroducer
Expand All @@ -200,17 +196,15 @@ CARBON_PARSE_NODE_KIND_BRACKET(PackageDirective, PackageIntroducer,
// ImportDirective
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ImportIntroducer, 0, CARBON_TOKEN(Import))
CARBON_PARSE_NODE_KIND_BRACKET(ImportDirective, ImportIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Import)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)
// `library` as directive:
// LibraryIntroducer
// DefaultLibrary or _external_: LibraryName
// LibraryDirective
CARBON_PARSE_NODE_KIND_CHILD_COUNT(DefaultLibrary, 0, CARBON_TOKEN(Default))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibraryIntroducer, 0, CARBON_TOKEN(Library))
CARBON_PARSE_NODE_KIND_BRACKET(LibraryDirective, LibraryIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Library)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `library` in `package` or `import`:
// _external_: LibraryName
Expand All @@ -224,19 +218,18 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibrarySpecifier, 1, CARBON_TOKEN(Library))
// Namespace
CARBON_PARSE_NODE_KIND_CHILD_COUNT(NamespaceStart, 0, CARBON_TOKEN(Namespace))
CARBON_PARSE_NODE_KIND_BRACKET(Namespace, NamespaceStart,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Namespace)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// A code block:
// CodeBlockStart
// _repeated_ _external_: statement
// CodeBlock
CARBON_PARSE_NODE_KIND_CHILD_COUNT(CodeBlockStart, 0,
CARBON_TOKEN(OpenCurlyBrace)
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_BRACKET(CodeBlock, CodeBlockStart,
CARBON_TOKEN(CloseCurlyBrace)
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_ANY_TOKEN_ON_ERROR)

// `fn`:
// FunctionIntroducer
Expand All @@ -259,8 +252,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinitionStart, FunctionIntroducer,
CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinition, FunctionDefinitionStart,
CARBON_TOKEN(CloseCurlyBrace))
CARBON_PARSE_NODE_KIND_BRACKET(FunctionDecl, FunctionIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Fn)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// A tuple pattern:
// TuplePatternStart
Expand Down Expand Up @@ -301,8 +293,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ImplicitParamList,
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ArrayExprStart, 0,
CARBON_TOKEN(OpenSquareBracket))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ArrayExprSemi, 2,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_BRACKET(ArrayExpr, ArrayExprSemi,
CARBON_TOKEN(CloseSquareBracket))

Expand All @@ -314,7 +305,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ArrayExpr, ArrayExprSemi,
// _optional_ Template
CARBON_PARSE_NODE_KIND_CHILD_COUNT(BindingPattern, 2,
CARBON_TOKEN(Colon)
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_CHILD_COUNT(GenericBindingPattern, 2,
CARBON_TOKEN(ColonExclaim))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(Address, 1, CARBON_TOKEN(Addr))
Expand All @@ -332,8 +323,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(Template, 1, CARBON_TOKEN(Template))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(LetIntroducer, 0, CARBON_TOKEN(Let))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(LetInitializer, 0, CARBON_TOKEN(Equal))
CARBON_PARSE_NODE_KIND_BRACKET(LetDecl, LetIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Let)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `var` and `returned var`:
// VariableIntroducer
Expand All @@ -350,14 +340,11 @@ CARBON_PARSE_NODE_KIND_BRACKET(LetDecl, LetIntroducer,
// The VariableInitializer and following expression are paired: either both will
// be present, or neither will.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableIntroducer, 0,
CARBON_TOKEN(Var)
CARBON_IF_ERROR(CARBON_TOKEN(Returned)))
CARBON_TOKEN(Var) CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnedModifier, 0, CARBON_TOKEN(Returned))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableInitializer, 0, CARBON_TOKEN(Equal))
CARBON_PARSE_NODE_KIND_BRACKET(VariableDecl, VariableIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Var)
CARBON_TOKEN(Returned)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// An expression statement:
// _external_: expression
Expand All @@ -369,17 +356,15 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExprStatement, 1, CARBON_TOKEN(Semi))
// BreakStatement
CARBON_PARSE_NODE_KIND_CHILD_COUNT(BreakStatementStart, 0, CARBON_TOKEN(Break))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(BreakStatement, 1,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Break)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `continue`:
// ContinueStatementStart
// ContinueStatement
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ContinueStatementStart, 0,
CARBON_TOKEN(Continue))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ContinueStatement, 1,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Continue)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `return`:
// ReturnStatementStart
Expand All @@ -389,8 +374,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnStatementStart, 0,
CARBON_TOKEN(Return))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnVarModifier, 0, CARBON_TOKEN(Var))
CARBON_PARSE_NODE_KIND_BRACKET(ReturnStatement, ReturnStatementStart,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Return)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `for`:
// ForHeaderStart
Expand All @@ -405,13 +389,12 @@ CARBON_PARSE_NODE_KIND_BRACKET(ReturnStatement, ReturnStatementStart,
// Versus a normal `var`, ForIn replaces VariableDecl.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForHeaderStart, 0,
CARBON_TOKEN(OpenParen)
CARBON_IF_ERROR(CARBON_TOKEN(For)))
CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_BRACKET(ForIn, VariableIntroducer,
CARBON_TOKEN(In)
CARBON_IF_ERROR(CARBON_ANY_TOKEN))
CARBON_TOKEN(In) CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_BRACKET(ForHeader, ForHeaderStart,
CARBON_TOKEN(CloseParen)
CARBON_IF_ERROR(CARBON_TOKEN(For)))
CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForStatement, 2, CARBON_TOKEN(For))

// `if` statement + `else`:
Expand All @@ -426,10 +409,10 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForStatement, 2, CARBON_TOKEN(For))
// IfStatementElse and the following node are optional based on `else` presence.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfConditionStart, 0,
CARBON_TOKEN(OpenParen)
CARBON_IF_ERROR(CARBON_TOKEN(If)))
CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_BRACKET(IfCondition, IfConditionStart,
CARBON_TOKEN(CloseParen)
CARBON_IF_ERROR(CARBON_TOKEN(If)))
CARBON_ANY_TOKEN_ON_ERROR)
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfStatementElse, 0, CARBON_TOKEN(Else))
CARBON_PARSE_NODE_KIND_BRACKET(IfStatement, IfCondition, CARBON_TOKEN(If))

Expand Down Expand Up @@ -469,8 +452,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(IndexExpr, IndexExprStart,
//
// Expressions and TupleLiteralComma may repeat with TupleLiteralComma as a
// separator.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExprOpenParen, 0,
CARBON_TOKEN(OpenParen))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExprOpenParen, 0, CARBON_TOKEN(OpenParen))
CARBON_PARSE_NODE_KIND_BRACKET(ParenExpr, ExprOpenParen,
CARBON_TOKEN(CloseParen))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteralComma, 0, CARBON_TOKEN(Comma))
Expand Down Expand Up @@ -609,8 +591,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(PostfixOperatorStar, 1, CARBON_TOKEN(Star))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExprIf, 1, CARBON_TOKEN(If))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExprThen, 1, CARBON_TOKEN(Then))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExprElse, 3,
CARBON_TOKEN(Else)
CARBON_IF_ERROR(CARBON_TOKEN(If)))
CARBON_TOKEN(Else) CARBON_ANY_TOKEN_ON_ERROR)

// Struct literals, such as `{.a = 0}`:
// StructLiteralOrStructTypeLiteralStart
Expand Down Expand Up @@ -680,8 +661,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ClassDefinitionStart, ClassIntroducer,
CARBON_PARSE_NODE_KIND_BRACKET(ClassDefinition, ClassDefinitionStart,
CARBON_TOKEN(CloseCurlyBrace))
CARBON_PARSE_NODE_KIND_BRACKET(ClassDecl, ClassIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Class)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `base`:
// BaseIntroducer
Expand All @@ -692,8 +672,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ClassDecl, ClassIntroducer,
CARBON_PARSE_NODE_KIND_CHILD_COUNT(BaseIntroducer, 0, CARBON_TOKEN(Base))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(BaseColon, 0, CARBON_TOKEN(Colon))
CARBON_PARSE_NODE_KIND_BRACKET(BaseDecl, BaseIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Base)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `interface`:
// InterfaceIntroducer
Expand All @@ -713,8 +692,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(InterfaceDefinitionStart, InterfaceIntroducer,
CARBON_PARSE_NODE_KIND_BRACKET(InterfaceDefinition, InterfaceDefinitionStart,
CARBON_TOKEN(CloseCurlyBrace))
CARBON_PARSE_NODE_KIND_BRACKET(InterfaceDecl, InterfaceIntroducer,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Interface)))
CARBON_TOKEN(Semi) CARBON_ANY_TOKEN_ON_ERROR)

// `impl ... as`:
// ImplIntroducer
Expand Down Expand Up @@ -775,5 +753,4 @@ CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDecl, NamedConstraintIntroducer,
#undef CARBON_PARSE_NODE_KIND_TOKEN_LITERAL
#undef CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER
#undef CARBON_TOKEN
#undef CARBON_ANY_TOKEN
#undef CARBON_IF_ERROR
#undef CARBON_ANY_TOKEN_ON_ERROR
28 changes: 28 additions & 0 deletions toolchain/parse/testdata/class/fail_modifiers.carbon
Original file line number Diff line number Diff line change
@@ -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

virtual class B

// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:1: ERROR: `virtual` declarations must either end with a `;` or have a `{ ... }` block for a definition.
// CHECK:STDERR: impl class
// CHECK:STDERR: ^~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:6: ERROR: `class` introducer should be followed by a name.
// CHECK:STDERR: impl class
// CHECK:STDERR: ^~~~~
impl class

// CHECK:STDOUT: - filename: fail_modifiers.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'VirtualModifier', text: 'virtual'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'B'},
// CHECK:STDOUT: {kind: 'ClassDecl', text: 'virtual', has_error: yes, subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'InvalidParse', text: '', has_error: yes},
// CHECK:STDOUT: {kind: 'ClassDecl', text: 'class', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
28 changes: 28 additions & 0 deletions toolchain/parse/testdata/namespace/fail_modifiers.carbon
Original file line number Diff line number Diff line change
@@ -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

virtual namespace B

// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:1: ERROR: `namespace` declarations must end with a `;`.
// CHECK:STDERR: impl namespace
// CHECK:STDERR: ^~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:6: ERROR: `namespace` introducer should be followed by a name.
// CHECK:STDERR: impl namespace
// CHECK:STDERR: ^~~~~~~~~
impl namespace

// CHECK:STDOUT: - filename: fail_modifiers.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'},
// CHECK:STDOUT: {kind: 'VirtualModifier', text: 'virtual'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'B'},
// CHECK:STDOUT: {kind: 'Namespace', text: 'virtual', has_error: yes, subtree_size: 4},
// CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'},
// CHECK:STDOUT: {kind: 'InvalidParse', text: '', has_error: yes},
// CHECK:STDOUT: {kind: 'Namespace', text: 'namespace', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]

0 comments on commit 29528fe

Please sign in to comment.