reader/wgsl: Improve errors for statements outside functions

Fixes: tint:328
Change-Id: I29c35605c2cf546080e28eaa9d983595b4a8d977
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33401
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
Ben Clayton 2020-11-19 14:19:31 +00:00 committed by Commit Bot service account
parent f2bfeda1c4
commit aa5f23e1ca
3 changed files with 63 additions and 10 deletions

View File

@ -185,11 +185,13 @@ ParserImpl::Failure::Errored ParserImpl::add_error(const Token& t,
ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
const std::string& err) {
if (silence_errors_ == 0) {
diag::Diagnostic diagnostic;
diagnostic.severity = diag::Severity::Error;
diagnostic.message = err;
diagnostic.source = source;
diags_.add(std::move(diagnostic));
}
return Failure::kErrored;
}
@ -329,12 +331,31 @@ Expect<bool> ParserImpl::expect_global_decl() {
if (errored)
return Failure::kErrored;
if (decos.value.size() > 0) {
add_error(next(), "expected declaration after decorations");
} else {
add_error(next(), "unexpected token");
// Invalid syntax found - try and determine the best error message
// We have decorations parsed, but nothing to consume them?
if (decos.value.size() > 0)
return add_error(next(), "expected declaration after decorations");
// We have a statement outside of a function?
auto t = peek();
auto stat = without_error([&] { return statement(); });
if (stat.matched) {
// Attempt to jump to the next '}' - the function might have just been
// missing an opening line.
sync_to(Token::Type::kBraceRight, true);
return add_error(t, "statement found outside of function body");
}
return Failure::kErrored;
if (!stat.errored) {
// No match, no error - the parser might not have progressed.
// Ensure we always make _some_ forward progress.
next();
}
// Exhausted all attempts to make sense of where we're at.
// Spew a generic error.
return add_error(t, "unexpected token");
}
// global_variable_decl
@ -3001,6 +3022,14 @@ bool ParserImpl::is_sync_token(const Token& t) const {
return false;
}
template <typename F, typename T>
T ParserImpl::without_error(F&& body) {
silence_errors_++;
auto result = body();
silence_errors_--;
return result;
}
} // namespace wgsl
} // namespace reader
} // namespace tint

View File

@ -711,6 +711,15 @@ class ParserImpl {
/// @see sync().
bool is_sync_token(const Token& t) const;
/// without_error() calls the function |func| muting any grammatical errors
/// found while executing the function. This can be used as a best-effort to
/// produce a meaningful error message when the parser is out of sync.
/// @param func a function or lambda with the signature: `Expect<Result>()` or
/// `Maybe<Result>()`.
/// @return the value returned by |func|.
template <typename F, typename T = ReturnType<F>>
T without_error(F&& func);
/// Downcasts all the decorations in |list| to the type |T|, raising a parser
/// error if any of the decorations aren't of the type |T|.
template <typename T>
@ -749,6 +758,7 @@ class ParserImpl {
std::deque<Token> token_queue_;
bool synchronized_ = true;
std::vector<Token::Type> sync_tokens_;
int silence_errors_ = 0;
std::unordered_map<std::string, ast::type::Type*> registered_constructs_;
ast::Module module_;
};

View File

@ -449,6 +449,20 @@ TEST_F(ParserImplErrorTest, FunctionDeclMissingRBrace) {
" ^\n");
}
TEST_F(ParserImplErrorTest, FunctionMissingOpenLine) {
EXPECT(R"(const bar : vec2<f32> = vec2<f32>(1., 2.);
var a : f32 = bar[0];
return;
})",
"test.wgsl:2:17 error: unknown constructed type 'bar'\n"
" var a : f32 = bar[0];\n"
" ^^^\n"
"\n"
"test.wgsl:3:3 error: statement found outside of function body\n"
" return;\n"
" ^^^^^^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclConstInvalidIdentifier) {
EXPECT("const ^ : i32 = 1;",
"test.wgsl:1:7 error: expected identifier for constant declaration\n"