tint/sem: Record diagnostic severity modifications

Each sem::Node has a map which stores the diagnostic modifications
applied to that node. The sem::Info class provides a query to get the
diagnostic severity for a given AST node, by walking up the semantic
tree to find the tightest diagnostic severity modification. The
default severity is used if it was not overridden.

This allows components outside of the Resolver/Validator to determine
the diagnostic severity while walking the AST, which is required for
the uniformity analysis.

Bug: tint:1809
Change-Id: I4caf99d7412fb22fb1183b2c8cfde349da2fefd3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117601
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
James Price 2023-01-25 01:24:46 +00:00
parent 613fbcf79a
commit 5853205342
12 changed files with 208 additions and 3 deletions

View File

@ -1335,6 +1335,7 @@ if (tint_build_unittests) {
tint_unittests_source_set("tint_unittests_sem_src") { tint_unittests_source_set("tint_unittests_sem_src") {
sources = [ sources = [
"sem/builtin_test.cc", "sem/builtin_test.cc",
"sem/diagnostic_severity_test.cc",
"sem/expression_test.cc", "sem/expression_test.cc",
"sem/struct_test.cc", "sem/struct_test.cc",
] ]

View File

@ -961,6 +961,7 @@ if(TINT_BUILD_TESTS)
resolver/variable_validation_test.cc resolver/variable_validation_test.cc
scope_stack_test.cc scope_stack_test.cc
sem/builtin_test.cc sem/builtin_test.cc
sem/diagnostic_severity_test.cc
sem/expression_test.cc sem/expression_test.cc
sem/struct_test.cc sem/struct_test.cc
source_test.cc source_test.cc

View File

@ -25,6 +25,7 @@
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <unordered_map>
#include "src/tint/ast/node.h" #include "src/tint/ast/node.h"
@ -84,6 +85,9 @@ constexpr const char* kDiagnosticRuleStrings[] = {
/// Convert a DiagnosticSeverity to the corresponding diag::Severity. /// Convert a DiagnosticSeverity to the corresponding diag::Severity.
diag::Severity ToSeverity(DiagnosticSeverity sc); diag::Severity ToSeverity(DiagnosticSeverity sc);
/// DiagnosticRuleSeverities is a map from diagnostic rule to diagnostic severity.
using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
/// A diagnostic control used for diagnostic directives and attributes. /// A diagnostic control used for diagnostic directives and attributes.
class DiagnosticControl : public Castable<DiagnosticControl, Node> { class DiagnosticControl : public Castable<DiagnosticControl, Node> {
public: public:

View File

@ -15,6 +15,7 @@ See:
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <unordered_map>
#include "src/tint/ast/node.h" #include "src/tint/ast/node.h"
@ -34,6 +35,9 @@ namespace tint::ast {
/// Convert a DiagnosticSeverity to the corresponding diag::Severity. /// Convert a DiagnosticSeverity to the corresponding diag::Severity.
diag::Severity ToSeverity(DiagnosticSeverity sc); diag::Severity ToSeverity(DiagnosticSeverity sc);
/// DiagnosticRuleSeverities is a map from diagnostic rule to diagnostic severity.
using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
/// A diagnostic control used for diagnostic directives and attributes. /// A diagnostic control used for diagnostic directives and attributes.
class DiagnosticControl : public Castable<DiagnosticControl, Node> { class DiagnosticControl : public Castable<DiagnosticControl, Node> {
public: public:

View File

@ -136,9 +136,11 @@ bool Resolver::Resolve() {
return false; return false;
} }
// Create the semantic module // Create the semantic module.
builder_->Sem().SetModule(builder_->create<sem::Module>( auto* mod = builder_->create<sem::Module>(std::move(dependencies_.ordered_globals),
std::move(dependencies_.ordered_globals), std::move(enabled_extensions_))); std::move(enabled_extensions_));
ApplyDiagnosticSeverities(mod);
builder_->Sem().SetModule(mod);
return result; return result;
} }
@ -1073,6 +1075,7 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
auto* func = auto* func =
builder_->create<sem::Function>(decl, return_type, return_location, std::move(parameters)); builder_->create<sem::Function>(decl, return_type, return_location, std::move(parameters));
ApplyDiagnosticSeverities(func);
builder_->Sem().Add(decl, func); builder_->Sem().Add(decl, func);
TINT_SCOPED_ASSIGNMENT(current_function_, func); TINT_SCOPED_ASSIGNMENT(current_function_, func);
@ -3878,6 +3881,13 @@ bool Resolver::Mark(const ast::Node* node) {
return false; return false;
} }
template <typename NODE>
void Resolver::ApplyDiagnosticSeverities(NODE* node) {
for (auto itr : validator_.DiagnosticFilters().Top()) {
node->SetDiagnosticSeverity(itr.key, itr.value);
}
}
void Resolver::AddError(const std::string& msg, const Source& source) const { void Resolver::AddError(const std::string& msg, const Source& source) const {
diagnostics_.add_error(diag::System::Resolver, msg, source); diagnostics_.add_error(diag::System::Resolver, msg, source);
} }

View File

@ -414,6 +414,11 @@ class Resolver {
/// @returns true on success, false on error /// @returns true on success, false on error
bool Mark(const ast::Node* node); bool Mark(const ast::Node* node);
/// Applies the diagnostic severities from the current scope to a semantic node.
/// @param node the semantic node to apply the diagnostic severities to
template <typename NODE>
void ApplyDiagnosticSeverities(NODE* node);
/// Adds the given error message to the diagnostics /// Adds the given error message to the diagnostics
void AddError(const std::string& msg, const Source& source) const; void AddError(const std::string& msg, const Source& source) const;

View File

@ -0,0 +1,68 @@
// 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/sem/test_helper.h"
#include "src/tint/sem/module.h"
using namespace tint::number_suffixes; // NOLINT
namespace tint::sem {
namespace {
class DiagnosticSeverityTest : public TestHelper {
protected:
/// Create a program with two functions, setting the severity for "chromium_unreachable_code"
/// using an attribute. Test that we correctly track the severity of the filter for the
/// functions and the statements with them.
/// @param global_severity the global severity of the "chromium_unreachable_code" filter
void Run(ast::DiagnosticSeverity global_severity) {
// @diagnostic(off, chromium_unreachable_code)
// fn foo() {
// return;
// }
//
// fn bar() {
// return;
// }
auto rule = ast::DiagnosticRule::kChromiumUnreachableCode;
auto func_severity = ast::DiagnosticSeverity::kOff;
auto* return_1 = Return();
auto* return_2 = Return();
auto* func_attr = DiagnosticAttribute(func_severity, Expr("chromium_unreachable_code"));
auto* foo = Func("foo", {}, ty.void_(), utils::Vector{return_1}, utils::Vector{func_attr});
auto* bar = Func("bar", {}, ty.void_(), utils::Vector{return_2});
auto p = Build();
EXPECT_TRUE(p.IsValid()) << p.Diagnostics().str();
EXPECT_EQ(p.Sem().DiagnosticSeverity(foo, rule), func_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_1, rule), func_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(bar, rule), global_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_2, rule), global_severity);
}
};
TEST_F(DiagnosticSeverityTest, WithDirective) {
DiagnosticDirective(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
Run(ast::DiagnosticSeverity::kError);
}
TEST_F(DiagnosticSeverityTest, WithoutDirective) {
Run(ast::DiagnosticSeverity::kWarning);
}
} // namespace
} // namespace tint::sem

View File

@ -20,6 +20,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/variable.h" #include "src/tint/ast/variable.h"
#include "src/tint/sem/call.h" #include "src/tint/sem/call.h"
#include "src/tint/utils/unique_vector.h" #include "src/tint/utils/unique_vector.h"
@ -256,6 +257,18 @@ class Function final : public Castable<Function, CallTarget> {
/// @return the location for the return, if provided /// @return the location for the return, if provided
std::optional<uint32_t> ReturnLocation() const { return return_location_; } std::optional<uint32_t> ReturnLocation() const { return return_location_; }
/// Modifies the severity of a specific diagnostic rule for this function.
/// @param rule the diagnostic rule
/// @param severity the new diagnostic severity
void SetDiagnosticSeverity(ast::DiagnosticRule rule, ast::DiagnosticSeverity severity) {
diagnostic_severities_[rule] = severity;
}
/// @returns the diagnostic severity modifications applied to this function
const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
return diagnostic_severities_;
}
private: private:
Function(const Function&) = delete; Function(const Function&) = delete;
Function(Function&&) = delete; Function(Function&&) = delete;
@ -276,6 +289,7 @@ class Function final : public Castable<Function, CallTarget> {
std::vector<const Function*> ancestor_entry_points_; std::vector<const Function*> ancestor_entry_points_;
const Statement* discard_stmt_ = nullptr; const Statement* discard_stmt_ = nullptr;
sem::Behaviors behaviors_{sem::Behavior::kNext}; sem::Behaviors behaviors_{sem::Behavior::kNext};
ast::DiagnosticRuleSeverities diagnostic_severities_;
std::optional<uint32_t> return_location_; std::optional<uint32_t> return_location_;
}; };

View File

@ -14,6 +14,11 @@
#include "src/tint/sem/info.h" #include "src/tint/sem/info.h"
#include "src/tint/sem/expression.h"
#include "src/tint/sem/function.h"
#include "src/tint/sem/module.h"
#include "src/tint/sem/statement.h"
namespace tint::sem { namespace tint::sem {
Info::Info() = default; Info::Info() = default;
@ -24,4 +29,61 @@ Info::~Info() = default;
Info& Info::operator=(Info&&) = default; Info& Info::operator=(Info&&) = default;
ast::DiagnosticSeverity Info::DiagnosticSeverity(const ast::Node* ast_node,
ast::DiagnosticRule rule) const {
// Get the diagnostic severity modification for a node.
auto check = [&](auto* node) {
auto& severities = node->DiagnosticSeverities();
auto itr = severities.find(rule);
if (itr != severities.end()) {
return itr->second;
}
return ast::DiagnosticSeverity::kUndefined;
};
// Get the diagnostic severity modification for a function.
auto check_func = [&](const sem::Function* func) {
auto severity = check(func);
if (severity != ast::DiagnosticSeverity::kUndefined) {
return severity;
}
// No severity set on the function, so check the module instead.
return check(module_);
};
// Get the diagnostic severity modification for a statement.
auto check_stmt = [&](const sem::Statement* stmt) {
// Walk up the statement hierarchy, checking for diagnostic severity modifications.
while (true) {
auto severity = check(stmt);
if (severity != ast::DiagnosticSeverity::kUndefined) {
return severity;
}
if (!stmt->Parent()) {
break;
}
stmt = stmt->Parent();
}
// No severity set on the statement, so check the function instead.
return check_func(stmt->Function());
};
// Query the diagnostic severity from the semantic node that corresponds to the AST node.
auto* sem = Get(ast_node);
TINT_ASSERT(Resolver, sem != nullptr);
auto severity = Switch(
sem, //
[&](const sem::Expression* expr) { return check_stmt(expr->Stmt()); },
[&](const sem::Statement* stmt) { return check_stmt(stmt); },
[&](const sem::Function* func) { return check_func(func); },
[&](Default) {
// Use the global severity set on the module.
return check(module_);
});
TINT_ASSERT(Resolver, severity != ast::DiagnosticSeverity::kUndefined);
return severity;
}
} // namespace tint::sem } // namespace tint::sem

View File

@ -20,6 +20,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/node.h" #include "src/tint/ast/node.h"
#include "src/tint/debug.h" #include "src/tint/debug.h"
#include "src/tint/sem/node.h" #include "src/tint/sem/node.h"
@ -144,6 +145,13 @@ class Info {
return &referenced_overrides_.at(from); return &referenced_overrides_.at(from);
} }
/// Determines the severity of a filterable diagnostic rule for the AST node `ast_node`.
/// @param ast_node the AST node
/// @param rule the diagnostic rule
/// @returns the severity of the rule for that AST node
ast::DiagnosticSeverity DiagnosticSeverity(const ast::Node* ast_node,
ast::DiagnosticRule rule) const;
private: private:
// AST node index to semantic node // AST node index to semantic node
std::vector<const CastableBase*> nodes_; std::vector<const CastableBase*> nodes_;

View File

@ -15,6 +15,7 @@
#ifndef SRC_TINT_SEM_MODULE_H_ #ifndef SRC_TINT_SEM_MODULE_H_
#define SRC_TINT_SEM_MODULE_H_ #define SRC_TINT_SEM_MODULE_H_
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/extension.h" #include "src/tint/ast/extension.h"
#include "src/tint/sem/node.h" #include "src/tint/sem/node.h"
#include "src/tint/utils/vector.h" #include "src/tint/utils/vector.h"
@ -46,9 +47,22 @@ class Module final : public Castable<Module, Node> {
/// @returns the list of enabled extensions in the module /// @returns the list of enabled extensions in the module
const ast::Extensions& Extensions() const { return extensions_; } const ast::Extensions& Extensions() const { return extensions_; }
/// Modifies the severity of a specific diagnostic rule for this module.
/// @param rule the diagnostic rule
/// @param severity the new diagnostic severity
void SetDiagnosticSeverity(ast::DiagnosticRule rule, ast::DiagnosticSeverity severity) {
diagnostic_severities_[rule] = severity;
}
/// @returns the diagnostic severity modifications applied to this module
const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
return diagnostic_severities_;
}
private: private:
const utils::Vector<const ast::Node*, 64> dep_ordered_decls_; const utils::Vector<const ast::Node*, 64> dep_ordered_decls_;
ast::Extensions extensions_; ast::Extensions extensions_;
ast::DiagnosticRuleSeverities diagnostic_severities_;
}; };
} // namespace tint::sem } // namespace tint::sem

View File

@ -15,6 +15,7 @@
#ifndef SRC_TINT_SEM_STATEMENT_H_ #ifndef SRC_TINT_SEM_STATEMENT_H_
#define SRC_TINT_SEM_STATEMENT_H_ #define SRC_TINT_SEM_STATEMENT_H_
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/sem/behavior.h" #include "src/tint/sem/behavior.h"
#include "src/tint/sem/node.h" #include "src/tint/sem/node.h"
#include "src/tint/symbol.h" #include "src/tint/symbol.h"
@ -109,12 +110,25 @@ class Statement : public Castable<Statement, Node> {
/// according to the behavior analysis /// according to the behavior analysis
void SetIsReachable(bool is_reachable) { is_reachable_ = is_reachable; } void SetIsReachable(bool is_reachable) { is_reachable_ = is_reachable; }
/// Modifies the severity of a specific diagnostic rule for this statement.
/// @param rule the diagnostic rule
/// @param severity the new diagnostic severity
void SetDiagnosticSeverity(ast::DiagnosticRule rule, ast::DiagnosticSeverity severity) {
diagnostic_severities_[rule] = severity;
}
/// @returns the diagnostic severity modifications applied to this statement
const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
return diagnostic_severities_;
}
private: private:
const ast::Statement* const declaration_; const ast::Statement* const declaration_;
const CompoundStatement* const parent_; const CompoundStatement* const parent_;
const sem::Function* const function_; const sem::Function* const function_;
sem::Behaviors behaviors_{sem::Behavior::kNext}; sem::Behaviors behaviors_{sem::Behavior::kNext};
bool is_reachable_ = true; bool is_reachable_ = true;
ast::DiagnosticRuleSeverities diagnostic_severities_;
}; };
/// CompoundStatement is the base class of statements that can hold other /// CompoundStatement is the base class of statements that can hold other