From b8df12042a8529f209d92576e6dfdd7312da9ed3 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Mon, 9 Nov 2020 18:41:23 +0000 Subject: [PATCH] wsgl parser: expect for variable_ident_decl All the call sites of `variable_ident_decl()` add their own error handling, so transform this into `expect_variable_ident_decl()`. Also makes error messages more consistent. Bug: tint:282 Change-Id: I0b5ac984018ba78896ddec0320636f5b5c4ad0b2 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32100 Reviewed-by: dan sinclair Commit-Queue: dan sinclair --- src/reader/wgsl/parser_impl.cc | 122 ++++++++---------- src/reader/wgsl/parser_impl.h | 10 +- src/reader/wgsl/parser_impl_error_msg_test.cc | 20 +-- src/reader/wgsl/parser_impl_for_stmt_test.cc | 4 +- .../wgsl/parser_impl_function_header_test.cc | 2 +- .../parser_impl_global_constant_decl_test.cc | 2 +- .../wgsl/parser_impl_global_decl_test.cc | 2 +- .../wgsl/parser_impl_param_list_test.cc | 4 +- .../wgsl/parser_impl_struct_body_decl_test.cc | 2 +- .../wgsl/parser_impl_variable_decl_test.cc | 2 +- .../parser_impl_variable_ident_decl_test.cc | 32 ++--- .../wgsl/parser_impl_variable_stmt_test.cc | 2 +- 12 files changed, 89 insertions(+), 115 deletions(-) diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc index 8cccd2c1f2..d209256cf8 100644 --- a/src/reader/wgsl/parser_impl.cc +++ b/src/reader/wgsl/parser_impl.cc @@ -123,7 +123,7 @@ ParserImpl::ParserImpl(Context* ctx, Source::File const* file) ParserImpl::~ParserImpl() = default; -void ParserImpl::add_error(const Token& t, +void ParserImpl::add_error(const Source& source, const std::string& err, const std::string& use) { std::stringstream msg; @@ -131,7 +131,7 @@ void ParserImpl::add_error(const Token& t, if (!use.empty()) { msg << " for " << use; } - add_error(t, msg.str()); + add_error(source, msg.str()); } void ParserImpl::add_error(const Token& t, const std::string& err) { @@ -317,27 +317,23 @@ std::unique_ptr ParserImpl::global_constant_decl() { if (!match(Token::Type::kConst)) return nullptr; - auto decl = variable_ident_decl(); + const char* use = "constant declaration"; + + auto decl = expect_variable_ident_decl(use); if (has_error()) return nullptr; - if (decl.name.empty() || decl.type == nullptr) { - add_error(peek(), "error parsing constant variable identifier"); - return nullptr; - } auto var = std::make_unique( decl.source, decl.name, ast::StorageClass::kNone, decl.type); var->set_is_const(true); - auto t = next(); - if (!t.IsEqual()) { - add_error(t, "missing = for const declaration"); + if (!expect(use, Token::Type::kEqual)) return nullptr; - } auto init = expect_const_expr(); if (has_error()) return nullptr; + var->set_constructor(std::move(init)); return var; @@ -351,15 +347,11 @@ std::unique_ptr ParserImpl::variable_decl() { auto sc = variable_storage_decoration(); if (has_error()) - return {}; + return nullptr; - auto decl = variable_ident_decl(); + auto decl = expect_variable_ident_decl("variable declaration"); if (has_error()) return nullptr; - if (decl.name.empty() || decl.type == nullptr) { - add_error(peek(), "invalid identifier declaration"); - return nullptr; - } return std::make_unique(decl.source, decl.name, sc, decl.type); } @@ -753,26 +745,22 @@ ast::type::ImageFormat ParserImpl::image_storage_type() { // variable_ident_decl // : IDENT COLON type_decl -ParserImpl::TypedIdentifier ParserImpl::variable_ident_decl() { +ParserImpl::TypedIdentifier ParserImpl::expect_variable_ident_decl( + const std::string& use) { + std::string name; + Source source; + if (!expect_ident(use, &name, &source)) + return {}; + + if (!expect(use, Token::Type::kColon)) + return {}; + auto t = peek(); - if (!t.IsIdentifier()) - return {}; - - auto name = t.to_str(); - auto source = t.source(); - next(); // Consume the peek - - t = next(); - if (!t.IsColon()) { - add_error(t, "missing : for identifier declaration"); - return {}; - } - auto* type = type_decl(); if (has_error()) return {}; if (type == nullptr) { - add_error(peek(), "invalid type for identifier declaration"); + add_error(t.source(), "invalid type", use); return {}; } @@ -894,9 +882,8 @@ ast::type::Type* ParserImpl::type_decl() { } auto decos = decoration_list(); - if (has_error()) { + if (has_error()) return nullptr; - } if (match(Token::Type::kArray)) { auto array_decos = cast_decorations(decos); @@ -1133,9 +1120,8 @@ std::unique_ptr ParserImpl::struct_decl( return nullptr; auto body = expect_struct_body_decl(); - if (has_error()) { + if (has_error()) return nullptr; - } return std::make_unique( name, std::make_unique(source, std::move(struct_decos), @@ -1150,6 +1136,8 @@ ast::StructMemberList ParserImpl::expect_struct_body_decl() { while (!peek().IsBraceRight() && !peek().IsEof()) { auto decos = decoration_list(); + if (has_error()) + return ast::StructMemberList{}; auto mem = expect_struct_member(decos); if (has_error()) @@ -1166,15 +1154,23 @@ ast::StructMemberList ParserImpl::expect_struct_body_decl() { // : struct_member_decoration_decl+ variable_ident_decl SEMICOLON std::unique_ptr ParserImpl::expect_struct_member( ast::DecorationList& decos) { - auto t = peek(); - - auto decl = variable_ident_decl(); + // FUDGE - Abort early if we enter with an error state to avoid accumulating + // multiple error messages. This is a work around for the unit tests that + // call: + // auto decos = p->decoration_list(); + // auto m = p->expect_struct_member(decos); + // ... and expect a single error message due to bad decorations. + // While expect_struct_body_decl() aborts after checking for decoration parse + // errors (and so these tests do not currently reflect full-parse behaviour), + // they do test the long-term desired behavior where the parser can + // resynchronize at the ']]'. + // TODO(ben-clayton) - remove this once resynchronization is implemented. if (has_error()) return nullptr; - if (decl.name.empty() || decl.type == nullptr) { - add_error(peek(), "invalid identifier declaration"); + + auto decl = expect_variable_ident_decl("struct member"); + if (has_error()) return nullptr; - } auto member_decos = cast_decorations(decos); @@ -1255,16 +1251,14 @@ std::unique_ptr ParserImpl::function_header() { // : // | (variable_ident_decl COMMA)* variable_ident_decl ast::VariableList ParserImpl::expect_param_list() { - auto t = peek(); + if (!peek().IsIdentifier()) // Empty list + return ast::VariableList{}; - ast::VariableList ret; - - auto decl = variable_ident_decl(); + auto decl = expect_variable_ident_decl("parameter"); if (has_error()) return {}; - if (decl.name.empty() || decl.type == nullptr) - return {}; + ast::VariableList ret; for (;;) { auto var = std::make_unique( decl.source, decl.name, ast::StorageClass::kNone, decl.type); @@ -1275,19 +1269,12 @@ ast::VariableList ParserImpl::expect_param_list() { var->set_is_const(true); ret.push_back(std::move(var)); - t = peek(); - if (!t.IsComma()) + if (!match(Token::Type::kComma)) break; - next(); // Consume the peek - - decl = variable_ident_decl(); + decl = expect_variable_ident_decl("parameter"); if (has_error()) return {}; - if (decl.name.empty() || decl.type == nullptr) { - add_error(t, "found , but no variable declaration"); - return {}; - } } return ret; @@ -1518,19 +1505,12 @@ std::unique_ptr ParserImpl::variable_stmt() { if (t.IsConst()) { next(); // Consume the peek - auto decl = variable_ident_decl(); + auto decl = expect_variable_ident_decl("constant declaration"); if (has_error()) return nullptr; - if (decl.name.empty() || decl.type == nullptr) { - add_error(peek(), "unable to parse variable declaration"); - return nullptr; - } - t = next(); - if (!t.IsEqual()) { - add_error(t, "missing = for constant declaration"); + if (!expect("constant declaration", Token::Type::kEqual)) return nullptr; - } auto constructor = logical_or_expression(); if (has_error()) @@ -2760,9 +2740,9 @@ bool ParserImpl::decoration_bracketed_list(ast::DecorationList& decos) { return false; } - auto t = peek(); - if (match(Token::Type::kAttrRight)) { - add_error(t, "empty decoration list"); + Source source; + if (match(Token::Type::kAttrRight, &source)) { + add_error(source, "empty decoration list"); return false; } @@ -2954,7 +2934,7 @@ bool ParserImpl::expect(const std::string& use, Token::Type tok) { bool ParserImpl::expect_sint(const std::string& use, int32_t* out) { auto t = next(); if (!t.IsSintLiteral()) { - add_error(t, "expected signed integer literal", use); + add_error(t.source(), "expected signed integer literal", use); return false; } *out = t.to_i32(); @@ -2999,7 +2979,7 @@ bool ParserImpl::expect_ident(const std::string& use, *source = t.source(); if (!t.IsIdentifier()) { - add_error(t, "expected identifier", use); + add_error(t.source(), "expected identifier", use); return false; } diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h index 74d82fbe49..da2f724925 100644 --- a/src/reader/wgsl/parser_impl.h +++ b/src/reader/wgsl/parser_impl.h @@ -132,11 +132,11 @@ class ParserImpl { /// @param msg the error message void add_error(const Token& t, const std::string& msg); /// Appends an error raised when parsing |use| at |t| with the message |msg| - /// @param t the token to associate the error with + /// @param source the source to associate the error with /// @param msg the error message /// @param use a description of what was being parsed when the error was /// raised. - void add_error(const Token& t, + void add_error(const Source& source, const std::string& msg, const std::string& use); /// Appends an error at |source| with the message |msg| @@ -169,9 +169,11 @@ class ParserImpl { /// Parses a `variable_decl` grammar element /// @returns the parsed variable or nullptr otherwise std::unique_ptr variable_decl(); - /// Parses a `variable_ident_decl` grammar element + /// Parses a `variable_ident_decl` grammar element, erroring on parse + /// failure. + /// @param use a description of what was being parsed if an error was raised. /// @returns the identifier and type parsed or empty otherwise - TypedIdentifier variable_ident_decl(); + TypedIdentifier expect_variable_ident_decl(const std::string& use); /// Parses a `variable_storage_decoration` grammar element /// @returns the storage class or StorageClass::kNone if none matched ast::StorageClass variable_storage_decoration(); diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc index 641c5cc373..f07df6d9bd 100644 --- a/src/reader/wgsl/parser_impl_error_msg_test.cc +++ b/src/reader/wgsl/parser_impl_error_msg_test.cc @@ -169,14 +169,14 @@ TEST_F(ParserImplErrorTest, ConstructorExprMissingRParen) { TEST_F(ParserImplErrorTest, ConstVarStmtInvalid) { EXPECT("fn f() -> void { const >; }", - "test.wgsl:1:24 error: unable to parse variable declaration\n" + "test.wgsl:1:24 error: expected identifier for constant declaration\n" "fn f() -> void { const >; }\n" " ^\n"); } TEST_F(ParserImplErrorTest, ConstVarStmtMissingAssignment) { EXPECT("fn f() -> void { const a : i32; }", - "test.wgsl:1:31 error: missing = for constant declaration\n" + "test.wgsl:1:31 error: expected '=' for constant declaration\n" "fn f() -> void { const a : i32; }\n" " ^\n"); } @@ -416,23 +416,23 @@ TEST_F(ParserImplErrorTest, FunctionDeclInvalidReturnType) { TEST_F(ParserImplErrorTest, FunctionDeclParamMissingColon) { EXPECT("fn f(x) -> void {}", - "test.wgsl:1:7 error: missing : for identifier declaration\n" + "test.wgsl:1:7 error: expected ':' for parameter\n" "fn f(x) -> void {}\n" " ^\n"); } TEST_F(ParserImplErrorTest, FunctionDeclParamInvalidType) { EXPECT("fn f(x : 1) -> void {}", - "test.wgsl:1:10 error: invalid type for identifier declaration\n" + "test.wgsl:1:10 error: invalid type for parameter\n" "fn f(x : 1) -> void {}\n" " ^\n"); } TEST_F(ParserImplErrorTest, FunctionDeclParamMissing) { EXPECT("fn f(x : i32, ) -> void {}", - "test.wgsl:1:13 error: found , but no variable declaration\n" + "test.wgsl:1:15 error: expected identifier for parameter\n" "fn f(x : i32, ) -> void {}\n" - " ^\n"); + " ^\n"); } TEST_F(ParserImplErrorTest, FunctionDeclMissingLBrace) { @@ -451,7 +451,7 @@ TEST_F(ParserImplErrorTest, FunctionDeclMissingRBrace) { TEST_F(ParserImplErrorTest, GlobalDeclConstInvalidIdentifier) { EXPECT("const ^ : i32 = 1;", - "test.wgsl:1:7 error: error parsing constant variable identifier\n" + "test.wgsl:1:7 error: expected identifier for constant declaration\n" "const ^ : i32 = 1;\n" " ^\n"); } @@ -479,7 +479,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclConstMissingRParen) { TEST_F(ParserImplErrorTest, GlobalDeclConstMissingAssignment) { EXPECT("const i : vec2;", - "test.wgsl:1:20 error: missing = for const declaration\n" + "test.wgsl:1:20 error: expected '=' for constant declaration\n" "const i : vec2;\n" " ^\n"); } @@ -702,7 +702,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclStructMemberDecoMissingEnd) { TEST_F(ParserImplErrorTest, GlobalDeclStructMemberInvalidIdentifier) { EXPECT("struct S { 1 : i32; };", - "test.wgsl:1:12 error: invalid identifier declaration\n" + "test.wgsl:1:12 error: expected identifier for struct member\n" "struct S { 1 : i32; };\n" " ^\n"); } @@ -988,7 +988,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclVarDecoBindingSetValue) { TEST_F(ParserImplErrorTest, GlobalDeclVarInvalidIdentifier) { EXPECT("var ^ : mat4x4;", - "test.wgsl:1:5 error: invalid identifier declaration\n" + "test.wgsl:1:5 error: expected identifier for variable declaration\n" "var ^ : mat4x4;\n" " ^\n"); } diff --git a/src/reader/wgsl/parser_impl_for_stmt_test.cc b/src/reader/wgsl/parser_impl_for_stmt_test.cc index b599e65656..a8a81288ac 100644 --- a/src/reader/wgsl/parser_impl_for_stmt_test.cc +++ b/src/reader/wgsl/parser_impl_for_stmt_test.cc @@ -216,7 +216,7 @@ TEST_F(ForStmtErrorTest, MissingRightBrace) { // Test a for loop with an invalid initializer statement. TEST_F(ForStmtErrorTest, InvalidInitializerAsConstDecl) { std::string for_str = "for (const x: i32;;) { }"; - std::string error_str = "1:18: missing = for constant declaration"; + std::string error_str = "1:18: expected '=' for constant declaration"; TestForWithError(for_str, error_str); } @@ -267,7 +267,7 @@ TEST_F(ForStmtErrorTest, InvalidContinuingMatch) { // Test a for loop with an invalid body. TEST_F(ForStmtErrorTest, InvalidBody) { std::string for_str = "for (;;) { const x: i32; }"; - std::string error_str = "1:24: missing = for constant declaration"; + std::string error_str = "1:24: expected '=' for constant declaration"; TestForWithError(for_str, error_str); } diff --git a/src/reader/wgsl/parser_impl_function_header_test.cc b/src/reader/wgsl/parser_impl_function_header_test.cc index dfd053ccfb..20a94ff60d 100644 --- a/src/reader/wgsl/parser_impl_function_header_test.cc +++ b/src/reader/wgsl/parser_impl_function_header_test.cc @@ -65,7 +65,7 @@ TEST_F(ParserImplTest, FunctionHeader_InvalidParamList) { auto f = p->function_header(); ASSERT_TRUE(p->has_error()); ASSERT_EQ(f, nullptr); - EXPECT_EQ(p->error(), "1:15: found , but no variable declaration"); + EXPECT_EQ(p->error(), "1:16: expected identifier for parameter"); } TEST_F(ParserImplTest, FunctionHeader_MissingParenRight) { diff --git a/src/reader/wgsl/parser_impl_global_constant_decl_test.cc b/src/reader/wgsl/parser_impl_global_constant_decl_test.cc index be0535aa65..fd2719b97c 100644 --- a/src/reader/wgsl/parser_impl_global_constant_decl_test.cc +++ b/src/reader/wgsl/parser_impl_global_constant_decl_test.cc @@ -48,7 +48,7 @@ TEST_F(ParserImplTest, GlobalConstantDecl_MissingEqual) { auto e = p->global_constant_decl(); ASSERT_TRUE(p->has_error()); ASSERT_EQ(e, nullptr); - EXPECT_EQ(p->error(), "1:14: missing = for const declaration"); + EXPECT_EQ(p->error(), "1:14: expected '=' for constant declaration"); } TEST_F(ParserImplTest, GlobalConstantDecl_InvalidVariable) { diff --git a/src/reader/wgsl/parser_impl_global_decl_test.cc b/src/reader/wgsl/parser_impl_global_decl_test.cc index fb027b5b98..f85eb71285 100644 --- a/src/reader/wgsl/parser_impl_global_decl_test.cc +++ b/src/reader/wgsl/parser_impl_global_decl_test.cc @@ -71,7 +71,7 @@ TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_Invalid) { auto* p = parser("const a : vec2;"); p->expect_global_decl(); ASSERT_TRUE(p->has_error()); - EXPECT_EQ(p->error(), "1:20: missing = for const declaration"); + EXPECT_EQ(p->error(), "1:20: expected '=' for constant declaration"); } TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingSemicolon) { diff --git a/src/reader/wgsl/parser_impl_param_list_test.cc b/src/reader/wgsl/parser_impl_param_list_test.cc index 1af5e61d34..1711106057 100644 --- a/src/reader/wgsl/parser_impl_param_list_test.cc +++ b/src/reader/wgsl/parser_impl_param_list_test.cc @@ -87,7 +87,7 @@ TEST_F(ParserImplTest, ParamList_Multiple) { TEST_F(ParserImplTest, ParamList_Empty) { auto* p = parser(""); auto e = p->expect_param_list(); - ASSERT_FALSE(p->has_error()) << p->error(); + ASSERT_FALSE(p->has_error()); EXPECT_EQ(e.size(), 0u); } @@ -95,7 +95,7 @@ TEST_F(ParserImplTest, ParamList_HangingComma) { auto* p = parser("a : i32,"); auto e = p->expect_param_list(); ASSERT_TRUE(p->has_error()); - EXPECT_EQ(p->error(), "1:8: found , but no variable declaration"); + EXPECT_EQ(p->error(), "1:9: expected identifier for parameter"); } } // namespace diff --git a/src/reader/wgsl/parser_impl_struct_body_decl_test.cc b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc index 7ea358e446..7e8d7d24ea 100644 --- a/src/reader/wgsl/parser_impl_struct_body_decl_test.cc +++ b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc @@ -70,7 +70,7 @@ TEST_F(ParserImplTest, StructBodyDecl_InvalidToken) { } )"); auto m = p->expect_struct_body_decl(); ASSERT_TRUE(p->has_error()); - EXPECT_EQ(p->error(), "4:3: invalid identifier declaration"); + EXPECT_EQ(p->error(), "4:3: expected identifier for struct member"); } } // namespace diff --git a/src/reader/wgsl/parser_impl_variable_decl_test.cc b/src/reader/wgsl/parser_impl_variable_decl_test.cc index 97d0115d1d..35edb242f0 100644 --- a/src/reader/wgsl/parser_impl_variable_decl_test.cc +++ b/src/reader/wgsl/parser_impl_variable_decl_test.cc @@ -52,7 +52,7 @@ TEST_F(ParserImplTest, VariableDecl_InvalidIdentDecl) { auto v = p->variable_decl(); ASSERT_TRUE(p->has_error()); ASSERT_EQ(v, nullptr); - ASSERT_EQ(p->error(), "1:12: missing : for identifier declaration"); + ASSERT_EQ(p->error(), "1:12: expected ':' for variable declaration"); } TEST_F(ParserImplTest, VariableDecl_WithStorageClass) { diff --git a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc index dc06863132..f9eb7a744c 100644 --- a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc +++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc @@ -23,7 +23,7 @@ namespace { TEST_F(ParserImplTest, VariableIdentDecl_Parses) { auto* p = parser("my_var : f32"); - auto decl = p->variable_ident_decl(); + auto decl = p->expect_variable_ident_decl("test"); ASSERT_FALSE(p->has_error()); ASSERT_EQ(decl.name, "my_var"); ASSERT_NE(decl.type, nullptr); @@ -37,43 +37,35 @@ TEST_F(ParserImplTest, VariableIdentDecl_Parses) { TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) { auto* p = parser(": f32"); - auto decl = p->variable_ident_decl(); - ASSERT_FALSE(p->has_error()); - ASSERT_EQ(decl.name, ""); - ASSERT_EQ(decl.type, nullptr); - - auto t = p->next(); - ASSERT_TRUE(t.IsColon()); + auto decl = p->expect_variable_ident_decl("test"); + ASSERT_TRUE(p->has_error()); + ASSERT_EQ(p->error(), "1:1: expected identifier for test"); } TEST_F(ParserImplTest, VariableIdentDecl_MissingColon) { auto* p = parser("my_var f32"); - auto r = p->variable_ident_decl(); + auto r = p->expect_variable_ident_decl("test"); ASSERT_TRUE(p->has_error()); - ASSERT_EQ(p->error(), "1:8: missing : for identifier declaration"); + ASSERT_EQ(p->error(), "1:8: expected ':' for test"); } TEST_F(ParserImplTest, VariableIdentDecl_MissingType) { auto* p = parser("my_var :"); - auto r = p->variable_ident_decl(); + auto r = p->expect_variable_ident_decl("test"); ASSERT_TRUE(p->has_error()); - ASSERT_EQ(p->error(), "1:9: invalid type for identifier declaration"); + ASSERT_EQ(p->error(), "1:9: invalid type for test"); } TEST_F(ParserImplTest, VariableIdentDecl_InvalidIdent) { auto* p = parser("123 : f32"); - auto decl = p->variable_ident_decl(); - ASSERT_FALSE(p->has_error()); - ASSERT_EQ(decl.name, ""); - ASSERT_EQ(decl.type, nullptr); - - auto t = p->next(); - ASSERT_TRUE(t.IsSintLiteral()); + auto decl = p->expect_variable_ident_decl("test"); + ASSERT_TRUE(p->has_error()); + ASSERT_EQ(p->error(), "1:1: expected identifier for test"); } TEST_F(ParserImplTest, VariableIdentDecl_InvalidType) { auto* p = parser("my_var : invalid"); - auto r = p->variable_ident_decl(); + auto r = p->expect_variable_ident_decl("test"); ASSERT_TRUE(p->has_error()); ASSERT_EQ(p->error(), "1:10: unknown constructed type 'invalid'"); } diff --git a/src/reader/wgsl/parser_impl_variable_stmt_test.cc b/src/reader/wgsl/parser_impl_variable_stmt_test.cc index 1330a7a804..8a984c9163 100644 --- a/src/reader/wgsl/parser_impl_variable_stmt_test.cc +++ b/src/reader/wgsl/parser_impl_variable_stmt_test.cc @@ -100,7 +100,7 @@ TEST_F(ParserImplTest, VariableStmt_Const_MissingEqual) { auto e = p->variable_stmt(); ASSERT_TRUE(p->has_error()); ASSERT_EQ(e, nullptr); - EXPECT_EQ(p->error(), "1:15: missing = for constant declaration"); + EXPECT_EQ(p->error(), "1:15: expected '=' for constant declaration"); } TEST_F(ParserImplTest, VariableStmt_Const_MissingConstructor) {