wgsl/reader: Parse diagnostic directives
Bug: tint:1809 Change-Id: I9f129685f22f51f577b67050edeef8aeea0c0bc5 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117569 Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
02855b5060
commit
8dd35110c2
|
@ -1594,6 +1594,8 @@ if (tint_build_unittests) {
|
|||
"reader/wgsl/parser_impl_continuing_stmt_test.cc",
|
||||
"reader/wgsl/parser_impl_core_lhs_expression_test.cc",
|
||||
"reader/wgsl/parser_impl_depth_texture_test.cc",
|
||||
"reader/wgsl/parser_impl_diagnostic_control_test.cc",
|
||||
"reader/wgsl/parser_impl_diagnostic_directive_test.cc",
|
||||
"reader/wgsl/parser_impl_element_count_expression_test.cc",
|
||||
"reader/wgsl/parser_impl_enable_directive_test.cc",
|
||||
"reader/wgsl/parser_impl_error_msg_test.cc",
|
||||
|
|
|
@ -1097,6 +1097,8 @@ if(TINT_BUILD_TESTS)
|
|||
reader/wgsl/parser_impl_continuing_stmt_test.cc
|
||||
reader/wgsl/parser_impl_core_lhs_expression_test.cc
|
||||
reader/wgsl/parser_impl_depth_texture_test.cc
|
||||
reader/wgsl/parser_impl_diagnostic_control_test.cc
|
||||
reader/wgsl/parser_impl_diagnostic_directive_test.cc
|
||||
reader/wgsl/parser_impl_element_count_expression_test.cc
|
||||
reader/wgsl/parser_impl_enable_directive_test.cc
|
||||
reader/wgsl/parser_impl_error_msg_test.cc
|
||||
|
|
|
@ -360,14 +360,47 @@ void ParserImpl::translation_unit() {
|
|||
}
|
||||
|
||||
// global_directive
|
||||
// : enable_directive
|
||||
// : diagnostic_directive
|
||||
// | enable_directive
|
||||
Maybe<Void> ParserImpl::global_directive(bool have_parsed_decl) {
|
||||
auto& p = peek();
|
||||
auto ed = enable_directive();
|
||||
if (ed.matched && have_parsed_decl) {
|
||||
return add_error(p, "enable directives must come before all global declarations");
|
||||
Maybe<Void> result = diagnostic_directive();
|
||||
if (!result.errored && !result.matched) {
|
||||
result = enable_directive();
|
||||
}
|
||||
return ed;
|
||||
|
||||
if (result.matched && have_parsed_decl) {
|
||||
return add_error(p, "directives must come before all global declarations");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// diagnostic_directive
|
||||
// : diagnostic diagnostic_control SEMICOLON
|
||||
Maybe<Void> ParserImpl::diagnostic_directive() {
|
||||
auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
|
||||
if (!match(Token::Type::kDiagnostic)) {
|
||||
return Failure::kNoMatch;
|
||||
}
|
||||
|
||||
auto control = expect_diagnostic_control();
|
||||
if (control.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
if (!expect("diagnostic directive", Token::Type::kSemicolon)) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
builder_.AST().AddDiagnosticControl(std::move(control.value));
|
||||
|
||||
return kSuccess;
|
||||
});
|
||||
|
||||
if (decl.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
return decl;
|
||||
}
|
||||
|
||||
// enable_directive
|
||||
|
@ -3697,6 +3730,43 @@ bool ParserImpl::expect_attributes_consumed(utils::VectorRef<const ast::Attribut
|
|||
return false;
|
||||
}
|
||||
|
||||
// severity_control_name
|
||||
// : 'error'
|
||||
// | 'warning'
|
||||
// | 'info'
|
||||
// | 'off'
|
||||
Expect<ast::DiagnosticSeverity> ParserImpl::expect_severity_control_name() {
|
||||
return expect_enum("severity control", ast::ParseDiagnosticSeverity,
|
||||
ast::kDiagnosticSeverityStrings);
|
||||
}
|
||||
|
||||
// diagnostic_control
|
||||
// : PAREN_LEFT severity_control_name COMMA ident_pattern_token COMMA ? PAREN_RIGHT
|
||||
Expect<const ast::DiagnosticControl*> ParserImpl::expect_diagnostic_control() {
|
||||
auto source = last_source();
|
||||
return expect_paren_block("diagnostic control", [&]() -> Expect<const ast::DiagnosticControl*> {
|
||||
auto severity_control = expect_severity_control_name();
|
||||
if (severity_control.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
if (!expect("diagnostic control", Token::Type::kComma)) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
auto rule_name = expect_ident("diagnostic control");
|
||||
if (rule_name.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
match(Token::Type::kComma);
|
||||
|
||||
return create<ast::DiagnosticControl>(
|
||||
source, severity_control.value,
|
||||
create<ast::IdentifierExpression>(rule_name.source,
|
||||
builder_.Symbols().Register(rule_name.value)));
|
||||
});
|
||||
}
|
||||
|
||||
bool ParserImpl::match(Token::Type tok, Source* source /*= nullptr*/) {
|
||||
auto& t = peek();
|
||||
|
||||
|
|
|
@ -402,6 +402,9 @@ class ParserImpl {
|
|||
/// @param has_parsed_decl flag indicating if the parser has consumed a global declaration.
|
||||
/// @return true on parse success, otherwise an error or no-match.
|
||||
Maybe<Void> global_directive(bool has_parsed_decl);
|
||||
/// Parses the `diagnostic_directive` grammar element, erroring on parse failure.
|
||||
/// @return true on parse success, otherwise an error or no-match.
|
||||
Maybe<Void> diagnostic_directive();
|
||||
/// Parses the `enable_directive` grammar element, erroring on parse failure.
|
||||
/// @return true on parse success, otherwise an error or no-match.
|
||||
Maybe<Void> enable_directive();
|
||||
|
@ -702,6 +705,12 @@ class ParserImpl {
|
|||
/// @see #attribute for the full list of attributes this method parses.
|
||||
/// @return the parsed attribute, or nullptr on error.
|
||||
Expect<const ast::Attribute*> expect_attribute();
|
||||
/// Parses a severity_control_name grammar element.
|
||||
/// @return the parsed severity control name, or nullptr on error.
|
||||
Expect<ast::DiagnosticSeverity> expect_severity_control_name();
|
||||
/// Parses a diagnostic_control grammar element.
|
||||
/// @return the parsed diagnostic control, or nullptr on error.
|
||||
Expect<const ast::DiagnosticControl*> expect_diagnostic_control();
|
||||
|
||||
/// Splits a peekable token into to parts filling in the peekable fields.
|
||||
/// @param lhs the token to set in the current position
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2023 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
|
||||
|
||||
#include "src/tint/ast/diagnostic_control.h"
|
||||
|
||||
namespace tint::reader::wgsl {
|
||||
namespace {
|
||||
|
||||
using SeverityPair = std::pair<std::string, ast::DiagnosticSeverity>;
|
||||
class DiagnosticControlParserTest : public ParserImplTestWithParam<SeverityPair> {};
|
||||
|
||||
TEST_P(DiagnosticControlParserTest, DiagnosticControl_Valid) {
|
||||
auto& params = GetParam();
|
||||
auto p = parser("(" + params.first + ", foo)");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_FALSE(e.errored);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_NE(e.value, nullptr);
|
||||
ASSERT_TRUE(e->Is<ast::DiagnosticControl>());
|
||||
EXPECT_EQ(e->severity, params.second);
|
||||
|
||||
auto* r = As<ast::IdentifierExpression>(e->rule_name);
|
||||
ASSERT_NE(r, nullptr);
|
||||
EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(DiagnosticControlParserTest,
|
||||
DiagnosticControlParserTest,
|
||||
testing::Values(SeverityPair{"error", ast::DiagnosticSeverity::kError},
|
||||
SeverityPair{"warning", ast::DiagnosticSeverity::kWarning},
|
||||
SeverityPair{"info", ast::DiagnosticSeverity::kInfo},
|
||||
SeverityPair{"off", ast::DiagnosticSeverity::kOff}));
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_Valid_TrailingComma) {
|
||||
auto p = parser("(error, foo,)");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_FALSE(e.errored);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_NE(e.value, nullptr);
|
||||
ASSERT_TRUE(e->Is<ast::DiagnosticControl>());
|
||||
EXPECT_EQ(e->severity, ast::DiagnosticSeverity::kError);
|
||||
|
||||
auto* r = As<ast::IdentifierExpression>(e->rule_name);
|
||||
ASSERT_NE(r, nullptr);
|
||||
EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_MissingOpenParen) {
|
||||
auto p = parser("off, foo)");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), R"(1:1: expected '(' for diagnostic control)");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_MissingCloseParen) {
|
||||
auto p = parser("(off, foo");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), R"(1:10: expected ')' for diagnostic control)");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_MissingDiagnosticSeverity) {
|
||||
auto p = parser("(, foo");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), R"(1:2: expected severity control
|
||||
Possible values: 'error', 'info', 'off', 'warning')");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_InvalidDiagnosticSeverity) {
|
||||
auto p = parser("(fatal, foo)");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), R"(1:2: expected severity control
|
||||
Possible values: 'error', 'info', 'off', 'warning')");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_MissingComma) {
|
||||
auto p = parser("(off foo");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), R"(1:6: expected ',' for diagnostic control)");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_MissingRuleName) {
|
||||
auto p = parser("(off,)");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), R"(1:6: expected identifier for diagnostic control)");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticControl_InvalidRuleName) {
|
||||
auto p = parser("(off, foo$bar)");
|
||||
auto e = p->expect_diagnostic_control();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), R"(1:10: invalid character found)");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::reader::wgsl
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2023 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
|
||||
|
||||
#include "src/tint/ast/diagnostic_control.h"
|
||||
|
||||
namespace tint::reader::wgsl {
|
||||
namespace {
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticDirective_Valid) {
|
||||
auto p = parser("diagnostic(off, foo);");
|
||||
p->diagnostic_directive();
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
auto& ast = p->builder().AST();
|
||||
ASSERT_EQ(ast.DiagnosticControls().Length(), 1u);
|
||||
auto* control = ast.DiagnosticControls()[0];
|
||||
EXPECT_EQ(control->severity, ast::DiagnosticSeverity::kOff);
|
||||
ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
|
||||
EXPECT_EQ(ast.GlobalDeclarations()[0], control);
|
||||
|
||||
auto* r = As<ast::IdentifierExpression>(control->rule_name);
|
||||
ASSERT_NE(r, nullptr);
|
||||
EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticDirective_MissingSemicolon) {
|
||||
auto p = parser("diagnostic(off, foo)");
|
||||
p->translation_unit();
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "1:21: expected ';' for diagnostic directive");
|
||||
auto program = p->program();
|
||||
auto& ast = program.AST();
|
||||
EXPECT_EQ(ast.DiagnosticControls().Length(), 0u);
|
||||
EXPECT_EQ(ast.GlobalDeclarations().Length(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticDirective_FollowingOtherGlobalDecl) {
|
||||
auto p = parser(R"(
|
||||
var<private> t: f32 = 0f;
|
||||
diagnostic(off, foo);
|
||||
)");
|
||||
p->translation_unit();
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DiagnosticDirective_FollowingEmptySemicolon) {
|
||||
auto p = parser(R"(
|
||||
;
|
||||
diagnostic(off, foo);
|
||||
)");
|
||||
p->translation_unit();
|
||||
// An empty semicolon is treated as a global declaration.
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::reader::wgsl
|
|
@ -161,7 +161,7 @@ enable f16;
|
|||
)");
|
||||
p->translation_unit();
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "3:1: enable directives must come before all global declarations");
|
||||
EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
|
||||
auto program = p->program();
|
||||
auto& ast = program.AST();
|
||||
// Accept the enable directive although it caused an error
|
||||
|
@ -181,7 +181,7 @@ enable f16;
|
|||
p->translation_unit();
|
||||
// An empty semicolon is treated as a global declaration
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "3:1: enable directives must come before all global declarations");
|
||||
EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
|
||||
auto program = p->program();
|
||||
auto& ast = program.AST();
|
||||
// Accept the enable directive although it cause an error
|
||||
|
|
Loading…
Reference in New Issue