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>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 40b37e5..cecf9b8 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -185,11 +185,13 @@
ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
const std::string& err) {
- diag::Diagnostic diagnostic;
- diagnostic.severity = diag::Severity::Error;
- diagnostic.message = err;
- diagnostic.source = source;
- diags_.add(std::move(diagnostic));
+ 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 @@
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 @@
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
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 09c5f5d..6604102 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -711,6 +711,15 @@
/// @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 @@
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_;
};
diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc
index 7e075a5..8a5c15d 100644
--- a/src/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/reader/wgsl/parser_impl_error_msg_test.cc
@@ -449,6 +449,20 @@
" ^\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"