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 <dsinclair@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 8cccd2c..d209256 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -123,7 +123,7 @@
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 @@
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 @@
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<ast::Variable>(
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 @@
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<ast::Variable>(decl.source, decl.name, sc, decl.type);
}
@@ -753,26 +745,22 @@
// 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 @@
}
auto decos = decoration_list();
- if (has_error()) {
+ if (has_error())
return nullptr;
- }
if (match(Token::Type::kArray)) {
auto array_decos = cast_decorations<ast::ArrayDecoration>(decos);
@@ -1133,9 +1120,8 @@
return nullptr;
auto body = expect_struct_body_decl();
- if (has_error()) {
+ if (has_error())
return nullptr;
- }
return std::make_unique<ast::type::StructType>(
name, std::make_unique<ast::Struct>(source, std::move(struct_decos),
@@ -1150,6 +1136,8 @@
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 @@
// : struct_member_decoration_decl+ variable_ident_decl SEMICOLON
std::unique_ptr<ast::StructMember> 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<ast::StructMemberDecoration>(decos);
@@ -1255,16 +1251,14 @@
// :
// | (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<ast::Variable>(
decl.source, decl.name, ast::StorageClass::kNone, decl.type);
@@ -1275,19 +1269,12 @@
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 @@
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 @@
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_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 @@
*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 74d82fb..da2f724 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -132,11 +132,11 @@
/// @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 @@
/// Parses a `variable_decl` grammar element
/// @returns the parsed variable or nullptr otherwise
std::unique_ptr<ast::Variable> 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 641c5cc..f07df6d 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, 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, 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, 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, GlobalDeclConstMissingAssignment) {
EXPECT("const i : vec2<i32>;",
- "test.wgsl:1:20 error: missing = for const declaration\n"
+ "test.wgsl:1:20 error: expected '=' for constant declaration\n"
"const i : vec2<i32>;\n"
" ^\n");
}
@@ -702,7 +702,7 @@
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, 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 b599e65..a8a8128 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 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 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 dfd053c..20a94ff 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 @@
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 be0535a..fd2719b 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 @@
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 fb027b5..f85eb71 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 @@
auto* p = parser("const a : vec2<i32>;");
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 1af5e61..1711106 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_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 @@
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 7ea358e..7e8d7d2 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 @@
} )");
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 97d0115..35edb24 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 @@
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 dc06863..f9eb7a7 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 @@
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_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 1330a7a..8a984c9 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 @@
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) {