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:
parent
f2bfeda1c4
commit
aa5f23e1ca
|
@ -185,11 +185,13 @@ ParserImpl::Failure::Errored ParserImpl::add_error(const Token& t,
|
||||||
|
|
||||||
ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
|
ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
|
||||||
const std::string& err) {
|
const std::string& err) {
|
||||||
|
if (silence_errors_ == 0) {
|
||||||
diag::Diagnostic diagnostic;
|
diag::Diagnostic diagnostic;
|
||||||
diagnostic.severity = diag::Severity::Error;
|
diagnostic.severity = diag::Severity::Error;
|
||||||
diagnostic.message = err;
|
diagnostic.message = err;
|
||||||
diagnostic.source = source;
|
diagnostic.source = source;
|
||||||
diags_.add(std::move(diagnostic));
|
diags_.add(std::move(diagnostic));
|
||||||
|
}
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,12 +331,31 @@ Expect<bool> ParserImpl::expect_global_decl() {
|
||||||
if (errored)
|
if (errored)
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
|
|
||||||
if (decos.value.size() > 0) {
|
// Invalid syntax found - try and determine the best error message
|
||||||
add_error(next(), "expected declaration after decorations");
|
|
||||||
} else {
|
// We have decorations parsed, but nothing to consume them?
|
||||||
add_error(next(), "unexpected token");
|
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
|
// global_variable_decl
|
||||||
|
@ -3001,6 +3022,14 @@ bool ParserImpl::is_sync_token(const Token& t) const {
|
||||||
return false;
|
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 wgsl
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -711,6 +711,15 @@ class ParserImpl {
|
||||||
/// @see sync().
|
/// @see sync().
|
||||||
bool is_sync_token(const Token& t) const;
|
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
|
/// 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|.
|
/// error if any of the decorations aren't of the type |T|.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -749,6 +758,7 @@ class ParserImpl {
|
||||||
std::deque<Token> token_queue_;
|
std::deque<Token> token_queue_;
|
||||||
bool synchronized_ = true;
|
bool synchronized_ = true;
|
||||||
std::vector<Token::Type> sync_tokens_;
|
std::vector<Token::Type> sync_tokens_;
|
||||||
|
int silence_errors_ = 0;
|
||||||
std::unordered_map<std::string, ast::type::Type*> registered_constructs_;
|
std::unordered_map<std::string, ast::type::Type*> registered_constructs_;
|
||||||
ast::Module module_;
|
ast::Module module_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -449,6 +449,20 @@ TEST_F(ParserImplErrorTest, FunctionDeclMissingRBrace) {
|
||||||
" ^\n");
|
" ^\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) {
|
TEST_F(ParserImplErrorTest, GlobalDeclConstInvalidIdentifier) {
|
||||||
EXPECT("const ^ : i32 = 1;",
|
EXPECT("const ^ : i32 = 1;",
|
||||||
"test.wgsl:1:7 error: expected identifier for constant declaration\n"
|
"test.wgsl:1:7 error: expected identifier for constant declaration\n"
|
||||||
|
|
Loading…
Reference in New Issue