tint/ast: Make DiagnosticControl a plain struct

Introduce DiagnosticDirective, which contains a DiagnosticControl
struct (as does DiagnosticAttribute).

Bug: tint:1809
Change-Id: I204001266ee472d2ee2827614a306acad53f12ba
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/118060
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
James Price 2023-02-04 12:20:55 +00:00 committed by Dawn LUCI CQ
parent ededebbcf3
commit 98dc5a86cc
36 changed files with 297 additions and 253 deletions

View File

@ -264,6 +264,7 @@ libtint_source_set("libtint_syntax_tree_src") {
"ast/depth_texture.h",
"ast/diagnostic_attribute.h",
"ast/diagnostic_control.h",
"ast/diagnostic_directive.h",
"ast/disable_validation_attribute.h",
"ast/discard_statement.h",
"ast/enable.h",
@ -597,6 +598,8 @@ libtint_source_set("libtint_ast_src") {
"ast/diagnostic_attribute.h",
"ast/diagnostic_control.cc",
"ast/diagnostic_control.h",
"ast/diagnostic_directive.cc",
"ast/diagnostic_directive.h",
"ast/disable_validation_attribute.cc",
"ast/disable_validation_attribute.h",
"ast/discard_statement.cc",
@ -1321,6 +1324,7 @@ if (tint_build_unittests) {
"ast/continue_statement_test.cc",
"ast/diagnostic_attribute_test.cc",
"ast/diagnostic_control_test.cc",
"ast/diagnostic_directive_test.cc",
"ast/discard_statement_test.cc",
"ast/enable_test.cc",
"ast/extension_test.cc",

View File

@ -130,6 +130,8 @@ list(APPEND TINT_LIB_SRCS
ast/diagnostic_attribute.h
ast/diagnostic_control.cc
ast/diagnostic_control.h
ast/diagnostic_directive.cc
ast/diagnostic_directive.h
ast/disable_validation_attribute.cc
ast/disable_validation_attribute.h
ast/discard_statement.cc
@ -843,6 +845,7 @@ if(TINT_BUILD_TESTS)
ast/depth_texture_test.cc
ast/diagnostic_attribute_test.cc
ast/diagnostic_control_test.cc
ast/diagnostic_directive_test.cc
ast/discard_statement_test.cc
ast/enable_test.cc
ast/external_texture_test.cc

View File

@ -15,6 +15,7 @@
#include "src/tint/ast/diagnostic_attribute.h"
#include <string>
#include <utility>
#include "src/tint/program_builder.h"
@ -25,8 +26,8 @@ namespace tint::ast {
DiagnosticAttribute::DiagnosticAttribute(ProgramID pid,
NodeID nid,
const Source& src,
const ast::DiagnosticControl* dc)
: Base(pid, nid, src), control(dc) {}
ast::DiagnosticControl&& dc)
: Base(pid, nid, src), control(std::move(dc)) {}
DiagnosticAttribute::~DiagnosticAttribute() = default;
@ -37,8 +38,9 @@ std::string DiagnosticAttribute::Name() const {
const DiagnosticAttribute* DiagnosticAttribute::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source);
auto dc = ctx->Clone(control);
return ctx->dst->create<DiagnosticAttribute>(src, dc);
auto rule = ctx->Clone(control.rule_name);
DiagnosticControl dc(control.severity, rule);
return ctx->dst->create<DiagnosticAttribute>(src, std::move(dc));
}
} // namespace tint::ast

View File

@ -30,10 +30,7 @@ class DiagnosticAttribute final : public Castable<DiagnosticAttribute, Attribute
/// @param nid the unique node identifier
/// @param src the source of this node
/// @param dc the diagnostic control
DiagnosticAttribute(ProgramID pid,
NodeID nid,
const Source& src,
const ast::DiagnosticControl* dc);
DiagnosticAttribute(ProgramID pid, NodeID nid, const Source& src, ast::DiagnosticControl&& dc);
~DiagnosticAttribute() override;
/// @returns the WGSL name for the attribute
@ -44,8 +41,8 @@ class DiagnosticAttribute final : public Castable<DiagnosticAttribute, Attribute
/// @return the newly cloned node
const DiagnosticAttribute* Clone(CloneContext* ctx) const override;
/// The diagnostic control expression.
const ast::DiagnosticControl* const control;
/// The diagnostic control.
const ast::DiagnosticControl control;
};
} // namespace tint::ast

View File

@ -24,8 +24,8 @@ TEST_F(DiagnosticAttributeTest, Creation) {
auto* name = Ident("foo");
auto* d = DiagnosticAttribute(DiagnosticSeverity::kWarning, name);
EXPECT_EQ(d->Name(), "diagnostic");
EXPECT_EQ(d->control->severity, DiagnosticSeverity::kWarning);
EXPECT_EQ(d->control->rule_name, name);
EXPECT_EQ(d->control.severity, DiagnosticSeverity::kWarning);
EXPECT_EQ(d->control.rule_name, name);
}
} // namespace

View File

@ -24,18 +24,13 @@
#include <string>
#include "src/tint/program_builder.h"
TINT_INSTANTIATE_TYPEINFO(tint::ast::DiagnosticControl);
#include "src/tint/ast/identifier.h"
#include "src/tint/ast/templated_identifier.h"
namespace tint::ast {
DiagnosticControl::DiagnosticControl(ProgramID pid,
NodeID nid,
const Source& src,
DiagnosticSeverity sev,
const Identifier* rule)
: Base(pid, nid, src), severity(sev), rule_name(rule) {
DiagnosticControl::DiagnosticControl(DiagnosticSeverity sev, const Identifier* rule)
: severity(sev), rule_name(rule) {
TINT_ASSERT(AST, rule != nullptr);
if (rule) {
// It is invalid for a diagnostic rule name to be templated
@ -43,14 +38,6 @@ DiagnosticControl::DiagnosticControl(ProgramID pid,
}
}
DiagnosticControl::~DiagnosticControl() = default;
const DiagnosticControl* DiagnosticControl::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto rule = ctx->Clone(rule_name);
return ctx->dst->create<DiagnosticControl>(src, severity, rule);
}
diag::Severity ToSeverity(DiagnosticSeverity sc) {
switch (sc) {
case DiagnosticSeverity::kError:

View File

@ -14,18 +14,13 @@ See:
#include <string>
#include "src/tint/program_builder.h"
TINT_INSTANTIATE_TYPEINFO(tint::ast::DiagnosticControl);
#include "src/tint/ast/identifier.h"
#include "src/tint/ast/templated_identifier.h"
namespace tint::ast {
DiagnosticControl::DiagnosticControl(ProgramID pid,
NodeID nid,
const Source& src,
DiagnosticSeverity sev,
const Identifier* rule)
: Base(pid, nid, src), severity(sev), rule_name(rule) {
DiagnosticControl::DiagnosticControl(DiagnosticSeverity sev, const Identifier* rule)
: severity(sev), rule_name(rule) {
TINT_ASSERT(AST, rule != nullptr);
if (rule) {
// It is invalid for a diagnostic rule name to be templated
@ -33,14 +28,6 @@ DiagnosticControl::DiagnosticControl(ProgramID pid,
}
}
DiagnosticControl::~DiagnosticControl() = default;
const DiagnosticControl* DiagnosticControl::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto rule = ctx->Clone(rule_name);
return ctx->dst->create<DiagnosticControl>(src, severity, rule);
}
diag::Severity ToSeverity(DiagnosticSeverity sc) {
switch (sc) {
case DiagnosticSeverity::kError:

View File

@ -27,7 +27,7 @@
#include <string>
#include <unordered_map>
#include "src/tint/ast/node.h"
#include "src/tint/diagnostic/diagnostic.h"
// Forward declarations
namespace tint::ast {
@ -91,26 +91,15 @@ diag::Severity ToSeverity(DiagnosticSeverity sc);
using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
/// A diagnostic control used for diagnostic directives and attributes.
class DiagnosticControl : public Castable<DiagnosticControl, Node> {
struct DiagnosticControl {
public:
/// Default constructor.
DiagnosticControl() {}
/// Constructor
/// @param pid the identifier of the program that owns this node
/// @param nid the unique node identifier
/// @param src the source of this node
/// @param sev the diagnostic severity
/// @param rule the diagnostic rule name
DiagnosticControl(ProgramID pid,
NodeID nid,
const Source& src,
DiagnosticSeverity sev,
const Identifier* rule);
~DiagnosticControl() override;
/// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned node
const DiagnosticControl* Clone(CloneContext* ctx) const override;
DiagnosticControl(DiagnosticSeverity sev, const Identifier* rule);
/// The diagnostic severity control.
DiagnosticSeverity severity;

View File

@ -17,7 +17,7 @@ See:
#include <string>
#include <unordered_map>
#include "src/tint/ast/node.h"
#include "src/tint/diagnostic/diagnostic.h"
// Forward declarations
namespace tint::ast {
@ -39,26 +39,15 @@ diag::Severity ToSeverity(DiagnosticSeverity sc);
using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
/// A diagnostic control used for diagnostic directives and attributes.
class DiagnosticControl : public Castable<DiagnosticControl, Node> {
struct DiagnosticControl {
public:
/// Default constructor.
DiagnosticControl() {}
/// Constructor
/// @param pid the identifier of the program that owns this node
/// @param nid the unique node identifier
/// @param src the source of this node
/// @param sev the diagnostic severity
/// @param rule the diagnostic rule name
DiagnosticControl(ProgramID pid,
NodeID nid,
const Source& src,
DiagnosticSeverity sev,
const Identifier* rule);
~DiagnosticControl() override;
/// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned node
const DiagnosticControl* Clone(CloneContext* ctx) const override;
DiagnosticControl(DiagnosticSeverity sev, const Identifier* rule);
/// The diagnostic severity control.
DiagnosticSeverity severity;

View File

@ -31,26 +31,12 @@ namespace {
using DiagnosticControlTest = TestHelper;
TEST_F(DiagnosticControlTest, Creation) {
auto* name = Ident("foo");
Source source;
source.range.begin = Source::Location{20, 2};
source.range.end = Source::Location{20, 5};
auto* control = create<ast::DiagnosticControl>(source, DiagnosticSeverity::kWarning, name);
EXPECT_EQ(control->source.range.begin.line, 20u);
EXPECT_EQ(control->source.range.begin.column, 2u);
EXPECT_EQ(control->source.range.end.line, 20u);
EXPECT_EQ(control->source.range.end.column, 5u);
EXPECT_EQ(control->severity, DiagnosticSeverity::kWarning);
EXPECT_EQ(control->rule_name, name);
}
TEST_F(DiagnosticControlTest, Assert_RuleNotTemplated) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<ast::DiagnosticControl>(DiagnosticSeverity::kWarning,
b.Ident("name", "a", "b", "c"));
ast::DiagnosticControl control(DiagnosticSeverity::kWarning,
b.Ident("name", "a", "b", "c"));
},
"internal compiler error");
}

View File

@ -21,26 +21,12 @@ namespace {
using DiagnosticControlTest = TestHelper;
TEST_F(DiagnosticControlTest, Creation) {
auto* name = Ident("foo");
Source source;
source.range.begin = Source::Location{20, 2};
source.range.end = Source::Location{20, 5};
auto* control = create<ast::DiagnosticControl>(source, DiagnosticSeverity::kWarning, name);
EXPECT_EQ(control->source.range.begin.line, 20u);
EXPECT_EQ(control->source.range.begin.column, 2u);
EXPECT_EQ(control->source.range.end.line, 20u);
EXPECT_EQ(control->source.range.end.column, 5u);
EXPECT_EQ(control->severity, DiagnosticSeverity::kWarning);
EXPECT_EQ(control->rule_name, name);
}
TEST_F(DiagnosticControlTest, Assert_RuleNotTemplated) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<ast::DiagnosticControl>(DiagnosticSeverity::kWarning,
b.Ident("name", "a", "b", "c"));
ast::DiagnosticControl control(DiagnosticSeverity::kWarning,
b.Ident("name", "a", "b", "c"));
},
"internal compiler error");
}

View File

@ -0,0 +1,39 @@
// 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/ast/diagnostic_directive.h"
#include "src/tint/program_builder.h"
TINT_INSTANTIATE_TYPEINFO(tint::ast::DiagnosticDirective);
namespace tint::ast {
DiagnosticDirective::DiagnosticDirective(ProgramID pid,
NodeID nid,
const Source& src,
DiagnosticControl&& dc)
: Base(pid, nid, src), control(std::move(dc)) {}
DiagnosticDirective::DiagnosticDirective(DiagnosticDirective&&) = default;
DiagnosticDirective::~DiagnosticDirective() = default;
const DiagnosticDirective* DiagnosticDirective::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto rule = ctx->Clone(control.rule_name);
DiagnosticControl dc(control.severity, rule);
return ctx->dst->create<DiagnosticDirective>(src, std::move(dc));
}
} // namespace tint::ast

View File

@ -0,0 +1,58 @@
// 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.
#ifndef SRC_TINT_AST_DIAGNOSTIC_DIRECTIVE_H_
#define SRC_TINT_AST_DIAGNOSTIC_DIRECTIVE_H_
#include <string>
#include <utility>
#include <vector>
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/node.h"
namespace tint::ast {
/// A "diagnostic" directive. Example:
/// ```
/// // Turn off diagnostics for derivative uniformity violations.
/// diagnostic(off, derivative_uniformity);
/// ```
class DiagnosticDirective final : public Castable<DiagnosticDirective, Node> {
public:
/// Create a extension
/// @param pid the identifier of the program that owns this node
/// @param nid the unique node identifier
/// @param src the source of this node
/// @param dc the diagnostic control
DiagnosticDirective(ProgramID pid, NodeID nid, const Source& src, DiagnosticControl&& dc);
/// Move constructor
DiagnosticDirective(DiagnosticDirective&&);
/// Destructor
~DiagnosticDirective() override;
/// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned node
const DiagnosticDirective* Clone(CloneContext* ctx) const override;
/// The diagnostic control.
const DiagnosticControl control;
};
} // namespace tint::ast
#endif // SRC_TINT_AST_DIAGNOSTIC_DIRECTIVE_H_

View File

@ -0,0 +1,37 @@
// Copyright 2022 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/ast/diagnostic_directive.h"
#include "src/tint/ast/test_helper.h"
namespace tint::ast {
namespace {
using DiagnosticDirectiveTest = TestHelper;
TEST_F(DiagnosticDirectiveTest, Creation) {
auto* name = Ident("foo");
DiagnosticControl control(ast::DiagnosticSeverity::kWarning, name);
auto* diag = create<ast::DiagnosticDirective>(Source{{{10, 5}, {10, 15}}}, std::move(control));
EXPECT_EQ(diag->source.range.begin.line, 10u);
EXPECT_EQ(diag->source.range.begin.column, 5u);
EXPECT_EQ(diag->source.range.end.line, 10u);
EXPECT_EQ(diag->source.range.end.column, 15u);
EXPECT_EQ(diag->control.severity, ast::DiagnosticSeverity::kWarning);
EXPECT_EQ(diag->control.rule_name, name);
}
} // namespace
} // namespace tint::ast

View File

@ -71,9 +71,9 @@ void Module::BinGlobalDeclaration(const tint::ast::Node* decl, diag::List& diags
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, var, program_id);
global_variables_.Push(var);
},
[&](const DiagnosticControl* diag_control) {
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, diag_control, program_id);
diagnostic_controls_.Push(diag_control);
[&](const DiagnosticDirective* diagnostic) {
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, diagnostic, program_id);
diagnostic_directives_.Push(diagnostic);
},
[&](const Enable* enable) {
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, enable, program_id);
@ -86,11 +86,11 @@ void Module::BinGlobalDeclaration(const tint::ast::Node* decl, diag::List& diags
[&](Default) { TINT_ICE(AST, diags) << "Unknown global declaration type"; });
}
void Module::AddDiagnosticControl(const ast::DiagnosticControl* control) {
TINT_ASSERT(AST, control);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, control, program_id);
global_declarations_.Push(control);
diagnostic_controls_.Push(control);
void Module::AddDiagnosticDirective(const ast::DiagnosticDirective* directive) {
TINT_ASSERT(AST, directive);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, directive, program_id);
global_declarations_.Push(directive);
diagnostic_directives_.Push(directive);
}
void Module::AddEnable(const ast::Enable* enable) {
@ -143,6 +143,7 @@ void Module::Copy(CloneContext* ctx, const Module* src) {
functions_.Clear();
global_variables_.Clear();
enables_.Clear();
diagnostic_directives_.Clear();
for (auto* decl : global_declarations_) {
if (TINT_UNLIKELY(!decl)) {

View File

@ -18,7 +18,7 @@
#include <string>
#include "src/tint/ast/const_assert.h"
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/diagnostic_directive.h"
#include "src/tint/ast/enable.h"
#include "src/tint/ast/function.h"
#include "src/tint/ast/type.h"
@ -93,16 +93,16 @@ class Module final : public Castable<Module, Node> {
return out;
}
/// Add a global diagnostic control to the module
/// @param control the diagnostic control to add
void AddDiagnosticControl(const DiagnosticControl* control);
/// Add a diagnostic directive to the module
/// @param diagnostic the diagnostic directive to add
void AddDiagnosticDirective(const DiagnosticDirective* diagnostic);
/// Add a enable directive to the module
/// @param ext the enable directive to add
void AddEnable(const Enable* ext);
/// @returns the global diagnostic controls for the module
const auto& DiagnosticControls() const { return diagnostic_controls_; }
/// @returns the diagnostic directives for the module
const auto& DiagnosticDirectives() const { return diagnostic_directives_; }
/// @returns the extension set for the module
const auto& Enables() const { return enables_; }
@ -154,7 +154,7 @@ class Module final : public Castable<Module, Node> {
utils::Vector<const TypeDecl*, 16> type_decls_;
FunctionList functions_;
utils::Vector<const Variable*, 32> global_variables_;
utils::Vector<const DiagnosticControl*, 8> diagnostic_controls_;
utils::Vector<const DiagnosticDirective*, 8> diagnostic_directives_;
utils::Vector<const Enable*, 8> enables_;
utils::Vector<const ConstAssert*, 8> const_asserts_;
};

View File

@ -149,10 +149,10 @@ TEST_F(ModuleTest, Directives) {
enable_1,
enable_2,
}));
EXPECT_THAT(program.AST().DiagnosticControls(), ::testing::ContainerEq(utils::Vector{
diagnostic_1,
diagnostic_2,
}));
EXPECT_THAT(program.AST().DiagnosticDirectives(), ::testing::ContainerEq(utils::Vector{
diagnostic_1,
diagnostic_2,
}));
}
} // namespace

View File

@ -43,6 +43,7 @@
#include "src/tint/ast/depth_texture.h"
#include "src/tint/ast/diagnostic_attribute.h"
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/diagnostic_directive.h"
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/enable.h"
@ -3309,7 +3310,7 @@ class ProgramBuilder {
static_assert(!traits::IsType<traits::PtrElTy<NAME>, ast::TemplatedIdentifier>,
"it is invalid for a diagnostic rule name to be templated");
return create<ast::DiagnosticAttribute>(
source, DiagnosticControl(source, severity, std::forward<NAME>(rule_name)));
source, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
}
/// Creates an ast::DiagnosticAttribute
@ -3320,57 +3321,35 @@ class ProgramBuilder {
const ast::DiagnosticAttribute* DiagnosticAttribute(ast::DiagnosticSeverity severity,
NAME&& rule_name) {
return create<ast::DiagnosticAttribute>(
source_, DiagnosticControl(source_, severity, std::forward<NAME>(rule_name)));
source_, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
}
/// Creates an ast::DiagnosticControl
/// Add a diagnostic directive to the module.
/// @param source the source information
/// @param severity the diagnostic severity control
/// @param rule_name the diagnostic rule name
/// @returns the diagnostic control pointer
/// @returns the diagnostic directive pointer
template <typename NAME>
const ast::DiagnosticControl* DiagnosticControl(const Source& source,
ast::DiagnosticSeverity severity,
NAME&& rule_name) {
return create<ast::DiagnosticControl>(source, severity,
Ident(std::forward<NAME>(rule_name)));
const ast::DiagnosticDirective* DiagnosticDirective(const Source& source,
ast::DiagnosticSeverity severity,
NAME&& rule_name) {
auto* directive = create<ast::DiagnosticDirective>(
source, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
AST().AddDiagnosticDirective(directive);
return directive;
}
/// Creates an ast::DiagnosticControl
/// Add a diagnostic directive to the module.
/// @param severity the diagnostic severity control
/// @param rule_name the diagnostic rule name
/// @returns the diagnostic control pointer
/// @returns the diagnostic directive pointer
template <typename NAME>
const ast::DiagnosticControl* DiagnosticControl(ast::DiagnosticSeverity severity,
NAME&& rule_name) {
return create<ast::DiagnosticControl>(source_, severity,
Ident(std::forward<NAME>(rule_name)));
}
/// Add a global diagnostic control to the module.
/// @param source the source information
/// @param severity the diagnostic severity control
/// @param rule_name the diagnostic rule name
/// @returns the diagnostic control pointer
template <typename NAME>
const ast::DiagnosticControl* DiagnosticDirective(const Source& source,
ast::DiagnosticSeverity severity,
NAME&& rule_name) {
auto* control = DiagnosticControl(source, severity, Ident(std::forward<NAME>(rule_name)));
AST().AddDiagnosticControl(control);
return control;
}
/// Add a global diagnostic control to the module.
/// @param severity the diagnostic severity control
/// @param rule_name the diagnostic rule name
/// @returns the diagnostic control pointer
template <typename NAME>
const ast::DiagnosticControl* DiagnosticDirective(ast::DiagnosticSeverity severity,
NAME&& rule_name) {
auto* control = DiagnosticControl(source_, severity, Ident(std::forward<NAME>(rule_name)));
AST().AddDiagnosticControl(control);
return control;
const ast::DiagnosticDirective* DiagnosticDirective(ast::DiagnosticSeverity severity,
NAME&& rule_name) {
auto* directive = create<ast::DiagnosticDirective>(
source_, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
AST().AddDiagnosticDirective(directive);
return directive;
}
/// Sets the current builder source to `src`

View File

@ -383,6 +383,7 @@ Maybe<Void> ParserImpl::diagnostic_directive() {
return Failure::kNoMatch;
}
auto source = last_source();
auto control = expect_diagnostic_control();
if (control.errored) {
return Failure::kErrored;
@ -392,7 +393,8 @@ Maybe<Void> ParserImpl::diagnostic_directive() {
return Failure::kErrored;
}
builder_.AST().AddDiagnosticControl(std::move(control.value));
auto* directive = create<ast::DiagnosticDirective>(source, std::move(control.value));
builder_.AST().AddDiagnosticDirective(directive);
return kSuccess;
});
@ -3587,7 +3589,7 @@ Maybe<const ast::Attribute*> ParserImpl::attribute() {
if (control.errored) {
return Failure::kErrored;
}
return create<ast::DiagnosticAttribute>(t.source(), control.value);
return create<ast::DiagnosticAttribute>(t.source(), std::move(control.value));
}
if (t == "fragment") {
@ -3756,9 +3758,8 @@ Expect<ast::DiagnosticSeverity> ParserImpl::expect_severity_control_name() {
// diagnostic_control
// : PAREN_LEFT severity_control_name COMMA ident_pattern_token COMMA ? PAREN_RIGHT
Expect<const ast::DiagnosticControl*> ParserImpl::expect_diagnostic_control() {
auto source = last_source();
return expect_paren_block("diagnostic control", [&]() -> Expect<const ast::DiagnosticControl*> {
Expect<ast::DiagnosticControl> ParserImpl::expect_diagnostic_control() {
return expect_paren_block("diagnostic control", [&]() -> Expect<ast::DiagnosticControl> {
auto severity_control = expect_severity_control_name();
if (severity_control.errored) {
return Failure::kErrored;
@ -3774,8 +3775,8 @@ Expect<const ast::DiagnosticControl*> ParserImpl::expect_diagnostic_control() {
}
match(Token::Type::kComma);
return create<ast::DiagnosticControl>(source, severity_control.value,
builder_.Ident(rule_name.source, rule_name.value));
return ast::DiagnosticControl(severity_control.value,
builder_.Ident(rule_name.source, rule_name.value));
});
}

View File

@ -717,7 +717,7 @@ class ParserImpl {
Expect<ast::DiagnosticSeverity> expect_severity_control_name();
/// Parses a diagnostic_control grammar element.
/// @return the parsed diagnostic control, or nullptr on error.
Expect<const ast::DiagnosticControl*> expect_diagnostic_control();
Expect<ast::DiagnosticControl> expect_diagnostic_control();
/// Splits a peekable token into to parts filling in the peekable fields.
/// @param lhs the token to set in the current position

View File

@ -26,8 +26,8 @@ TEST_F(ParserImplTest, DiagnosticAttribute_Valid) {
EXPECT_TRUE(a.matched);
auto* d = a.value->As<ast::DiagnosticAttribute>();
ASSERT_NE(d, nullptr);
EXPECT_EQ(d->control->severity, ast::DiagnosticSeverity::kOff);
auto* r = d->control->rule_name;
EXPECT_EQ(d->control.severity, ast::DiagnosticSeverity::kOff);
auto* r = d->control.rule_name;
ASSERT_NE(r, nullptr);
EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
}

View File

@ -28,8 +28,6 @@ TEST_P(DiagnosticControlParserTest, DiagnosticControl_Valid) {
auto e = p->expect_diagnostic_control();
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::DiagnosticControl>());
EXPECT_EQ(e->severity, params.second);
auto* r = e->rule_name;
@ -48,8 +46,6 @@ TEST_F(ParserImplTest, DiagnosticControl_Valid_TrailingComma) {
auto e = p->expect_diagnostic_control();
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::DiagnosticControl>());
EXPECT_EQ(e->severity, ast::DiagnosticSeverity::kError);
auto* r = e->rule_name;

View File

@ -24,13 +24,13 @@ TEST_F(ParserImplTest, DiagnosticDirective_Valid) {
p->diagnostic_directive();
EXPECT_FALSE(p->has_error()) << p->error();
auto& ast = p->builder().AST();
ASSERT_EQ(ast.DiagnosticControls().Length(), 1u);
auto* control = ast.DiagnosticControls()[0];
EXPECT_EQ(control->severity, ast::DiagnosticSeverity::kOff);
ASSERT_EQ(ast.DiagnosticDirectives().Length(), 1u);
auto* directive = ast.DiagnosticDirectives()[0];
EXPECT_EQ(directive->control.severity, ast::DiagnosticSeverity::kOff);
ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
EXPECT_EQ(ast.GlobalDeclarations()[0], control);
EXPECT_EQ(ast.GlobalDeclarations()[0], directive);
auto* r = control->rule_name;
auto* r = directive->control.rule_name;
ASSERT_NE(r, nullptr);
EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
}
@ -42,7 +42,7 @@ TEST_F(ParserImplTest, DiagnosticDirective_MissingSemicolon) {
EXPECT_EQ(p->error(), "1:21: expected ';' for diagnostic directive");
auto program = p->program();
auto& ast = program.AST();
EXPECT_EQ(ast.DiagnosticControls().Length(), 0u);
EXPECT_EQ(ast.DiagnosticDirectives().Length(), 0u);
EXPECT_EQ(ast.GlobalDeclarations().Length(), 0u);
}

View File

@ -207,8 +207,8 @@ class DependencyScanner {
TraverseExpression(var->initializer);
}
},
[&](const ast::DiagnosticControl*) {
// Diagnostic control directives do not affect the dependency graph.
[&](const ast::DiagnosticDirective*) {
// Diagnostic directives do not affect the dependency graph.
},
[&](const ast::Enable*) {
// Enable directives do not affect the dependency graph.
@ -561,7 +561,7 @@ struct DependencyAnalysis {
[&](const ast::TypeDecl* td) { return td->name; },
[&](const ast::Function* func) { return func->symbol; },
[&](const ast::Variable* var) { return var->symbol; },
[&](const ast::DiagnosticControl*) { return Symbol(); },
[&](const ast::DiagnosticDirective*) { return Symbol(); },
[&](const ast::Enable*) { return Symbol(); },
[&](const ast::ConstAssert*) { return Symbol(); },
[&](Default) {
@ -672,13 +672,13 @@ struct DependencyAnalysis {
// Make sure all directives go before any other global declarations.
for (auto* global : declaration_order_) {
if (global->node->IsAnyOf<ast::DiagnosticControl, ast::Enable>()) {
if (global->node->IsAnyOf<ast::DiagnosticDirective, ast::Enable>()) {
sorted_.Add(global->node);
}
}
for (auto* global : declaration_order_) {
if (global->node->IsAnyOf<ast::DiagnosticControl, ast::Enable>()) {
if (global->node->IsAnyOf<ast::DiagnosticDirective, ast::Enable>()) {
// Skip directives here, as they are already added.
continue;
}

View File

@ -1095,8 +1095,7 @@ TEST_F(ResolverDependencyGraphOrderedGlobalsTest, DirectiveFirst) {
auto* var_1 = GlobalVar("SYMBOL1", ty.i32());
auto* enable = Enable(ast::Extension::kF16);
auto* var_2 = GlobalVar("SYMBOL2", ty.f32());
auto* diagnostic = DiagnosticControl(ast::DiagnosticSeverity::kWarning, "foo");
AST().AddDiagnosticControl(diagnostic);
auto* diagnostic = DiagnosticDirective(ast::DiagnosticSeverity::kWarning, "foo");
EXPECT_THAT(AST().GlobalDeclarations(), ElementsAre(var_1, enable, var_2, diagnostic));
EXPECT_THAT(Build().ordered_globals, ElementsAre(enable, diagnostic, var_1, var_2));

View File

@ -268,10 +268,10 @@ TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameSameSeverity_Directive) {
}
TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameDifferentSeverity_Directive) {
DiagnosticDirective(Source{{12, 34}}, ast::DiagnosticSeverity::kError,
"chromium_unreachable_code");
DiagnosticDirective(Source{{56, 78}}, ast::DiagnosticSeverity::kOff,
"chromium_unreachable_code");
DiagnosticDirective(ast::DiagnosticSeverity::kError,
Ident(Source{{12, 34}}, "chromium_unreachable_code"));
DiagnosticDirective(ast::DiagnosticSeverity::kOff,
Ident(Source{{56, 78}}, "chromium_unreachable_code"));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(56:78 error: conflicting diagnostic directive
@ -279,16 +279,16 @@ TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameDifferentSeverity_Directi
}
TEST_F(ResolverDiagnosticControlTest, Conflict_SameUnknownNameDifferentSeverity_Directive) {
DiagnosticDirective(Source{{12, 34}}, ast::DiagnosticSeverity::kError,
"chromium_unreachable_codes");
DiagnosticDirective(Source{{56, 78}}, ast::DiagnosticSeverity::kOff,
"chromium_unreachable_codes");
DiagnosticDirective(ast::DiagnosticSeverity::kError,
Ident(Source{{12, 34}}, "chromium_unreachable_codes"));
DiagnosticDirective(ast::DiagnosticSeverity::kOff,
Ident(Source{{56, 78}}, "chromium_unreachable_codes"));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
R"(12:34 warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
Did you mean 'chromium_unreachable_code'?
Possible values: 'chromium_unreachable_code', 'derivative_uniformity'
warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
56:78 warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
Did you mean 'chromium_unreachable_code'?
Possible values: 'chromium_unreachable_code', 'derivative_uniformity'
56:78 error: conflicting diagnostic directive
@ -296,10 +296,8 @@ Possible values: 'chromium_unreachable_code', 'derivative_uniformity'
}
TEST_F(ResolverDiagnosticControlTest, Conflict_DifferentUnknownNameDifferentSeverity_Directive) {
DiagnosticDirective(Source{{12, 34}}, ast::DiagnosticSeverity::kError,
"chromium_unreachable_codes");
DiagnosticDirective(Source{{56, 78}}, ast::DiagnosticSeverity::kOff,
"chromium_unreachable_codex");
DiagnosticDirective(ast::DiagnosticSeverity::kError, "chromium_unreachable_codes");
DiagnosticDirective(ast::DiagnosticSeverity::kOff, "chromium_unreachable_codex");
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@ -313,10 +311,10 @@ TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameSameSeverity_Attribute) {
}
TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameDifferentSeverity_Attribute) {
auto* attr1 = DiagnosticAttribute(Source{{12, 34}}, ast::DiagnosticSeverity::kError,
"chromium_unreachable_code");
auto* attr2 = DiagnosticAttribute(Source{{56, 78}}, ast::DiagnosticSeverity::kOff,
"chromium_unreachable_code");
auto* attr1 = DiagnosticAttribute(ast::DiagnosticSeverity::kError,
Ident(Source{{12, 34}}, "chromium_unreachable_code"));
auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff,
Ident(Source{{56, 78}}, "chromium_unreachable_code"));
Func("foo", {}, ty.void_(), {}, utils::Vector{attr1, attr2});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
@ -325,17 +323,17 @@ TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameDifferentSeverity_Attribu
}
TEST_F(ResolverDiagnosticControlTest, Conflict_SameUnknownNameDifferentSeverity_Attribute) {
auto* attr1 = DiagnosticAttribute(Source{{12, 34}}, ast::DiagnosticSeverity::kError,
"chromium_unreachable_codes");
auto* attr2 = DiagnosticAttribute(Source{{56, 78}}, ast::DiagnosticSeverity::kOff,
"chromium_unreachable_codes");
auto* attr1 = DiagnosticAttribute(ast::DiagnosticSeverity::kError,
Ident(Source{{12, 34}}, "chromium_unreachable_codes"));
auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff,
Ident(Source{{56, 78}}, "chromium_unreachable_codes"));
Func("foo", {}, ty.void_(), {}, utils::Vector{attr1, attr2});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
R"(12:34 warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
Did you mean 'chromium_unreachable_code'?
Possible values: 'chromium_unreachable_code', 'derivative_uniformity'
warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
56:78 warning: unrecognized diagnostic rule 'chromium_unreachable_codes'
Did you mean 'chromium_unreachable_code'?
Possible values: 'chromium_unreachable_code', 'derivative_uniformity'
56:78 error: conflicting diagnostic attribute
@ -343,10 +341,9 @@ Possible values: 'chromium_unreachable_code', 'derivative_uniformity'
}
TEST_F(ResolverDiagnosticControlTest, Conflict_DifferentUnknownNameDifferentSeverity_Attribute) {
auto* attr1 = DiagnosticAttribute(Source{{12, 34}}, ast::DiagnosticSeverity::kError,
"chromium_unreachable_codes");
auto* attr2 = DiagnosticAttribute(Source{{56, 78}}, ast::DiagnosticSeverity::kOff,
"chromium_unreachable_codex");
auto* attr1 =
DiagnosticAttribute(ast::DiagnosticSeverity::kError, "chromium_unreachable_codes");
auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff, "chromium_unreachable_codex");
Func("foo", {}, ty.void_(), {}, utils::Vector{attr1, attr2});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}

View File

@ -158,11 +158,15 @@ bool Resolver::ResolveInternal() {
Mark(&builder_->AST());
// Process all module-scope declarations in dependency order.
utils::Vector<const ast::DiagnosticControl*, 4> diagnostic_controls;
for (auto* decl : dependencies_.ordered_globals) {
Mark(decl);
if (!Switch<bool>(
decl, //
[&](const ast::DiagnosticControl* dc) { return DiagnosticControl(dc); },
[&](const ast::DiagnosticDirective* d) {
diagnostic_controls.Push(&d->control);
return DiagnosticControl(d->control);
},
[&](const ast::Enable* e) { return Enable(e); },
[&](const ast::TypeDecl* td) { return TypeDecl(td); },
[&](const ast::Function* func) { return Function(func); },
@ -183,7 +187,7 @@ bool Resolver::ResolveInternal() {
SetShadows();
if (!validator_.DiagnosticControls(builder_->AST().DiagnosticControls(), "directive")) {
if (!validator_.DiagnosticControls(diagnostic_controls, "directive")) {
return false;
}
@ -989,7 +993,6 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
for (auto* attr : decl->attributes) {
Mark(attr);
if (auto* dc = attr->As<ast::DiagnosticAttribute>()) {
Mark(dc->control);
if (!DiagnosticControl(dc->control)) {
return nullptr;
}
@ -3069,18 +3072,18 @@ sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
return sem;
}
bool Resolver::DiagnosticControl(const ast::DiagnosticControl* control) {
Mark(control->rule_name);
bool Resolver::DiagnosticControl(const ast::DiagnosticControl& control) {
Mark(control.rule_name);
auto rule_name = builder_->Symbols().NameFor(control->rule_name->symbol);
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);
validator_.DiagnosticFilters().Set(rule, control.severity);
} else {
std::ostringstream ss;
ss << "unrecognized diagnostic rule '" << rule_name << "'\n";
utils::SuggestAlternatives(rule_name, ast::kDiagnosticRuleStrings, ss);
AddWarning(ss.str(), control->rule_name->source);
AddWarning(ss.str(), control.rule_name->source);
}
return true;
}
@ -3863,7 +3866,6 @@ SEM* Resolver::StatementScope(const ast::Statement* ast, SEM* sem, F&& callback)
for (auto* attr : stmt->attributes) {
Mark(attr);
if (auto* dc = attr->template As<ast::DiagnosticAttribute>()) {
Mark(dc->control);
if (!DiagnosticControl(dc->control)) {
return false;
}

View File

@ -263,7 +263,7 @@ class Resolver {
/// @param control the diagnostic control
/// @returns true on success, false on failure
bool DiagnosticControl(const ast::DiagnosticControl* control);
bool DiagnosticControl(const ast::DiagnosticControl& control);
/// @param enable the enable declaration
/// @returns the resolved extension

View File

@ -2433,7 +2433,7 @@ bool Validator::NoDuplicateAttributes(utils::VectorRef<const ast::Attribute*> at
for (auto* d : attributes) {
if (auto* diag = d->As<ast::DiagnosticAttribute>()) {
// Allow duplicate diagnostic attributes, and check for conflicts later.
diagnostic_controls.Push(diag->control);
diagnostic_controls.Push(&diag->control);
} else {
auto added = seen.Add(&d->TypeInfo(), d->source);
if (!added && !d->Is<ast::InternalAttribute>()) {
@ -2457,13 +2457,13 @@ bool Validator::DiagnosticControls(utils::VectorRef<const ast::DiagnosticControl
{
std::ostringstream ss;
ss << "conflicting diagnostic " << use;
AddError(ss.str(), dc->source);
AddError(ss.str(), dc->rule_name->source);
}
{
std::ostringstream ss;
ss << "severity of '" << symbols_.NameFor(dc->rule_name->symbol) << "' set to '"
<< dc->severity << "' here";
AddNote(ss.str(), (*diag_added.value)->source);
AddNote(ss.str(), (*diag_added.value)->rule_name->source);
}
return false;
}

View File

@ -1311,8 +1311,11 @@ Transform::ApplyResult Renamer::Apply(const Program* src,
});
}
},
[&](const ast::DiagnosticControl* diagnostic) {
preserved_identifiers.Add(diagnostic->rule_name);
[&](const ast::DiagnosticAttribute* diagnostic) {
preserved_identifiers.Add(diagnostic->control.rule_name);
},
[&](const ast::DiagnosticDirective* diagnostic) {
preserved_identifiers.Add(diagnostic->control.rule_name);
},
[&](const ast::TypeName* type_name) {
if (is_type_short_name(type_name->name->symbol)) {

View File

@ -114,7 +114,9 @@ Transform::ApplyResult SingleEntryPoint::Apply(const Program* src,
}
},
[&](const ast::Enable* ext) { b.AST().AddEnable(ctx.Clone(ext)); },
[&](const ast::DiagnosticControl* dc) { b.AST().AddDiagnosticControl(ctx.Clone(dc)); },
[&](const ast::DiagnosticDirective* d) {
b.AST().AddDiagnosticDirective(ctx.Clone(d));
},
[&](Default) {
TINT_UNREACHABLE(Transform, b.Diagnostics())
<< "unhandled global declaration: " << decl->TypeInfo().name;

View File

@ -250,7 +250,7 @@ bool GeneratorImpl::Generate() {
auto* mod = builder_.Sem().Module();
for (auto* decl : mod->DependencyOrderedDeclarations()) {
if (decl->IsAnyOf<ast::Alias, ast::ConstAssert, ast::DiagnosticControl>()) {
if (decl->IsAnyOf<ast::Alias, ast::ConstAssert, ast::DiagnosticDirective>()) {
continue; // These are not emitted.
}

View File

@ -310,7 +310,7 @@ bool GeneratorImpl::Generate() {
auto* mod = builder_.Sem().Module();
for (auto* decl : mod->DependencyOrderedDeclarations()) {
if (decl->IsAnyOf<ast::Alias, ast::DiagnosticControl, ast::Enable, ast::ConstAssert>()) {
if (decl->IsAnyOf<ast::Alias, ast::DiagnosticDirective, ast::Enable, ast::ConstAssert>()) {
continue; // These are not emitted.
}

View File

@ -313,7 +313,7 @@ bool GeneratorImpl::Generate() {
}
return EmitFunction(func);
},
[&](const ast::DiagnosticControl*) {
[&](const ast::DiagnosticDirective*) {
// Do nothing for diagnostic directives in MSL
return true;
},

View File

@ -71,9 +71,9 @@ bool GeneratorImpl::Generate() {
}
has_directives = true;
}
for (auto diagnostic : program_->AST().DiagnosticControls()) {
for (auto diagnostic : program_->AST().DiagnosticDirectives()) {
auto out = line();
if (!EmitDiagnosticControl(out, diagnostic)) {
if (!EmitDiagnosticControl(out, diagnostic->control)) {
return false;
}
out << ";";
@ -84,7 +84,7 @@ bool GeneratorImpl::Generate() {
}
// Generate global declarations in the order they appear in the module.
for (auto* decl : program_->AST().GlobalDeclarations()) {
if (decl->IsAnyOf<ast::DiagnosticControl, ast::Enable>()) {
if (decl->IsAnyOf<ast::DiagnosticDirective, ast::Enable>()) {
continue;
}
if (!Switch(
@ -108,9 +108,9 @@ bool GeneratorImpl::Generate() {
}
bool GeneratorImpl::EmitDiagnosticControl(std::ostream& out,
const ast::DiagnosticControl* diagnostic) {
out << "diagnostic(" << diagnostic->severity << ", "
<< program_->Symbols().NameFor(diagnostic->rule_name->symbol) << ")";
const ast::DiagnosticControl& diagnostic) {
out << "diagnostic(" << diagnostic.severity << ", "
<< program_->Symbols().NameFor(diagnostic.rule_name->symbol) << ")";
return true;
}

View File

@ -56,7 +56,7 @@ class GeneratorImpl : public TextGenerator {
/// @param out the output of the expression stream
/// @param diagnostic the diagnostic control node
/// @returns true if the diagnostic control was emitted
bool EmitDiagnosticControl(std::ostream& out, const ast::DiagnosticControl* diagnostic);
bool EmitDiagnosticControl(std::ostream& out, const ast::DiagnosticControl& diagnostic);
/// Handles generating an enable directive
/// @param enable the enable node
/// @returns true if the enable directive was emitted