tint/resolver: Handle diagnostic directives

The Resolver parses the diagnostic rule and sets the updated severity
in a ScopeStack, which is stored in the Validator.

Automatically generate the diagnostic rule enum and its parsing logic
using intrinsics.def.

Add a "chromium_unreachable_code" diagnostic rule to test this.

Bug: tint:1809
Change-Id: Ia94db4321b8019f01d31a84da0fda25dfdf72f5c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117566
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price
2023-01-24 21:01:36 +00:00
parent 098e3d8f90
commit 81e55754e1
16 changed files with 293 additions and 5 deletions

View File

@@ -0,0 +1,75 @@
// 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/resolver/resolver.h"
#include "src/tint/resolver/resolver_test_helper.h"
using namespace tint::number_suffixes; // NOLINT
namespace tint::resolver {
namespace {
using ResolverDiagnosticControlTest = ResolverTest;
TEST_F(ResolverDiagnosticControlTest, UnreachableCode_DefaultSeverity) {
auto stmts = utils::Vector{Return(), Return()};
Func("foo", {}, ty.void_(), stmts);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), R"(warning: code is unreachable)");
}
TEST_F(ResolverDiagnosticControlTest, UnreachableCode_ErrorViaDirective) {
DiagnosticDirective(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
auto stmts = utils::Vector{Return(), Return()};
Func("foo", {}, ty.void_(), stmts);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(error: code is unreachable)");
}
TEST_F(ResolverDiagnosticControlTest, UnreachableCode_WarningViaDirective) {
DiagnosticDirective(ast::DiagnosticSeverity::kWarning, Expr("chromium_unreachable_code"));
auto stmts = utils::Vector{Return(), Return()};
Func("foo", {}, ty.void_(), stmts);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), R"(warning: code is unreachable)");
}
TEST_F(ResolverDiagnosticControlTest, UnreachableCode_InfoViaDirective) {
DiagnosticDirective(ast::DiagnosticSeverity::kInfo, Expr("chromium_unreachable_code"));
auto stmts = utils::Vector{Return(), Return()};
Func("foo", {}, ty.void_(), stmts);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), R"(note: code is unreachable)");
}
TEST_F(ResolverDiagnosticControlTest, UnreachableCode_OffViaDirective) {
DiagnosticDirective(ast::DiagnosticSeverity::kOff, Expr("chromium_unreachable_code"));
auto stmts = utils::Vector{Return(), Return()};
Func("foo", {}, ty.void_(), stmts);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_TRUE(r()->error().empty());
}
} // namespace
} // namespace tint::resolver

View File

@@ -151,6 +151,7 @@ bool Resolver::ResolveInternal() {
Mark(decl);
if (!Switch<bool>(
decl, //
[&](const ast::DiagnosticControl* dc) { return DiagnosticControl(dc); },
[&](const ast::Enable* e) { return Enable(e); },
[&](const ast::TypeDecl* td) { return TypeDecl(td); },
[&](const ast::Function* func) { return Function(func); },
@@ -3050,6 +3051,16 @@ sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
return sem;
}
bool Resolver::DiagnosticControl(const ast::DiagnosticControl* control) {
Mark(control->rule_name);
auto rule_name = builder_->Symbols().NameFor(control->rule_name->symbol);
auto rule = ast::ParseDiagnosticRule(rule_name);
if (rule != ast::DiagnosticRule::kUndefined) {
validator_.DiagnosticFilters().Set(rule, control->severity);
}
return true;
}
bool Resolver::Enable(const ast::Enable* enable) {
enabled_extensions_.Add(enable->extension);
return true;

View File

@@ -30,7 +30,6 @@
#include "src/tint/resolver/intrinsic_table.h"
#include "src/tint/resolver/sem_helper.h"
#include "src/tint/resolver/validator.h"
#include "src/tint/scope_stack.h"
#include "src/tint/sem/binding_point.h"
#include "src/tint/sem/block_statement.h"
#include "src/tint/sem/function.h"
@@ -262,6 +261,10 @@ class Resolver {
/// @param ty the ast::Type
type::Type* Type(const ast::Type* ty);
/// @param control the diagnostic control
/// @returns true on success, false on failure
bool DiagnosticControl(const ast::DiagnosticControl* control);
/// @param enable the enable declaration
/// @returns the resolved extension
bool Enable(const ast::Enable* enable);

View File

@@ -166,7 +166,11 @@ Validator::Validator(
sem_(sem),
enabled_extensions_(enabled_extensions),
atomic_composite_info_(atomic_composite_info),
valid_type_storage_layouts_(valid_type_storage_layouts) {}
valid_type_storage_layouts_(valid_type_storage_layouts) {
// Set default severities for filterable diagnostic rules.
diagnostic_filters_.Set(ast::DiagnosticRule::kChromiumUnreachableCode,
ast::DiagnosticSeverity::kWarning);
}
Validator::~Validator() = default;
@@ -182,6 +186,24 @@ void Validator::AddNote(const std::string& msg, const Source& source) const {
diagnostics_.add_note(diag::System::Resolver, msg, source);
}
bool Validator::AddDiagnostic(ast::DiagnosticRule rule,
const std::string& msg,
const Source& source) const {
auto severity = diagnostic_filters_.Get(rule);
if (severity != ast::DiagnosticSeverity::kOff) {
diag::Diagnostic d{};
d.severity = ToSeverity(severity);
d.system = diag::System::Resolver;
d.source = source;
d.message = msg;
diagnostics_.add(std::move(d));
if (severity == ast::DiagnosticSeverity::kError) {
return false;
}
}
return true;
}
// https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
bool Validator::IsPlain(const type::Type* type) const {
return type->is_scalar() ||
@@ -1358,9 +1380,10 @@ bool Validator::EvaluationStage(const sem::Expression* expr,
bool Validator::Statements(utils::VectorRef<const ast::Statement*> stmts) const {
for (auto* stmt : stmts) {
if (!sem_.Get(stmt)->IsReachable()) {
/// TODO(https://github.com/gpuweb/gpuweb/issues/2378): This may need to
/// become an error.
AddWarning("code is unreachable", stmt->source);
if (!AddDiagnostic(ast::DiagnosticRule::kChromiumUnreachableCode, "code is unreachable",
stmt->source)) {
return false;
}
break;
}
}

View File

@@ -22,6 +22,7 @@
#include "src/tint/ast/pipeline_stage.h"
#include "src/tint/program_builder.h"
#include "src/tint/resolver/sem_helper.h"
#include "src/tint/scope_stack.h"
#include "src/tint/sem/evaluation_stage.h"
#include "src/tint/source.h"
#include "src/tint/utils/hash.h"
@@ -84,6 +85,9 @@ struct TypeAndAddressSpace {
}
};
/// DiagnosticFilterStack is a scoped stack of diagnostic filters.
using DiagnosticFilterStack = ScopeStack<ast::DiagnosticRule, ast::DiagnosticSeverity>;
/// Validation logic for various ast nodes. The validations in general should
/// be shallow and depend on the resolver to call on children. The validations
/// also assume that sem changes have already been made. The validation checks
@@ -118,6 +122,18 @@ class Validator {
/// @param source the note source
void AddNote(const std::string& msg, const Source& source) const;
/// Adds the given message to the diagnostics with current severity for the given rule.
/// @param rule the diagnostic trigger rule
/// @param msg the diagnostic message
/// @param source the diagnostic source
/// @returns false if the diagnostic is an error for the given trigger rule
bool AddDiagnostic(ast::DiagnosticRule rule,
const std::string& msg,
const Source& source) const;
/// @returns the diagnostic filter stack
DiagnosticFilterStack& DiagnosticFilters() { return diagnostic_filters_; }
/// @param type the given type
/// @returns true if the given type is a plain type
bool IsPlain(const type::Type* type) const;
@@ -525,6 +541,7 @@ class Validator {
SymbolTable& symbols_;
diag::List& diagnostics_;
SemHelper& sem_;
DiagnosticFilterStack diagnostic_filters_;
const ast::Extensions& enabled_extensions_;
const utils::Hashmap<const type::Type*, const Source*, 8>& atomic_composite_info_;
utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts_;