Move diagnostic severity and rule to builtin.

This CL moves DiagnosticSeverity and DiagnosticRule into the builtin::
hierarchy.

Change-Id: I3884046f042d130fa44556f63163f0040381e69c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/120440
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 29f42db..e37ad6c 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -604,6 +604,7 @@
   public_deps = [ ":libtint_ast_hdrs" ]
   deps = [
     ":libtint_base_src",
+    ":libtint_builtins_src",
     ":libtint_program_src",
     ":libtint_type_src",
   ]
@@ -701,6 +702,10 @@
     "builtin/builtin.h",
     "builtin/builtin_value.cc",
     "builtin/builtin_value.h",
+    "builtin/diagnostic_rule.cc",
+    "builtin/diagnostic_rule.h",
+    "builtin/diagnostic_severity.cc",
+    "builtin/diagnostic_severity.h",
     "builtin/extension.cc",
     "builtin/extension.h",
     "builtin/texel_format.cc",
@@ -1298,6 +1303,8 @@
       "builtin/address_space_test.cc",
       "builtin/builtin_test.cc",
       "builtin/builtin_value_test.cc",
+      "builtin/diagnostic_rule_test.cc",
+      "builtin/diagnostic_severity_test.cc",
       "builtin/extension_test.cc",
       "builtin/texel_format_test.cc",
     ]
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 38f5072..eb4203d 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -552,9 +552,11 @@
 tint_generated(builtin/address_space BENCH TEST)
 tint_generated(builtin/builtin BENCH TEST)
 tint_generated(builtin/builtin_value BENCH TEST)
+tint_generated(builtin/diagnostic_rule BENCH TEST)
+tint_generated(builtin/diagnostic_severity BENCH TEST)
 tint_generated(builtin/extension BENCH TEST)
 tint_generated(builtin/texel_format BENCH TEST)
-tint_generated(ast/diagnostic_control BENCH TEST)
+tint_generated(ast/diagnostic_control TEST)
 tint_generated(ast/interpolate_attribute BENCH TEST)
 tint_generated(resolver/init_conv_intrinsic)
 tint_generated(sem/builtin_type)
diff --git a/src/tint/ast/block_statement_test.cc b/src/tint/ast/block_statement_test.cc
index 5fc7910..5b2819c 100644
--- a/src/tint/ast/block_statement_test.cc
+++ b/src/tint/ast/block_statement_test.cc
@@ -45,8 +45,8 @@
     auto* d = create<DiscardStatement>();
     auto* ptr = d;
 
-    auto* attr1 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff, "foo");
-    auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff, "bar");
+    auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
+    auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
     auto* b = create<BlockStatement>(utils::Vector{d}, utils::Vector{attr1, attr2});
 
     ASSERT_EQ(b->statements.Length(), 1u);
diff --git a/src/tint/ast/diagnostic_attribute_test.cc b/src/tint/ast/diagnostic_attribute_test.cc
index 531364d..ec6ec26 100644
--- a/src/tint/ast/diagnostic_attribute_test.cc
+++ b/src/tint/ast/diagnostic_attribute_test.cc
@@ -22,9 +22,9 @@
 
 TEST_F(DiagnosticAttributeTest, Creation) {
     auto* name = Ident("foo");
-    auto* d = DiagnosticAttribute(DiagnosticSeverity::kWarning, name);
+    auto* d = DiagnosticAttribute(builtin::DiagnosticSeverity::kWarning, name);
     EXPECT_EQ(d->Name(), "diagnostic");
-    EXPECT_EQ(d->control.severity, DiagnosticSeverity::kWarning);
+    EXPECT_EQ(d->control.severity, builtin::DiagnosticSeverity::kWarning);
     EXPECT_EQ(d->control.rule_name, name);
 }
 
diff --git a/src/tint/ast/diagnostic_control.cc b/src/tint/ast/diagnostic_control.cc
index 8fbe400..e269446 100644
--- a/src/tint/ast/diagnostic_control.cc
+++ b/src/tint/ast/diagnostic_control.cc
@@ -12,24 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-////////////////////////////////////////////////////////////////////////////////
-// File generated by tools/src/cmd/gen
-// using the template:
-//   src/tint/ast/diagnostic_control.cc.tmpl
-//
-// Do not modify this file directly
-////////////////////////////////////////////////////////////////////////////////
-
 #include "src/tint/ast/diagnostic_control.h"
 
 #include <string>
 
 #include "src/tint/ast/identifier.h"
 #include "src/tint/ast/templated_identifier.h"
+#include "src/tint/builtin/diagnostic_severity.h"
 
 namespace tint::ast {
 
-DiagnosticControl::DiagnosticControl(DiagnosticSeverity sev, const Identifier* rule)
+DiagnosticControl::DiagnosticControl(builtin::DiagnosticSeverity sev, const Identifier* rule)
     : severity(sev), rule_name(rule) {
     TINT_ASSERT(AST, rule != nullptr);
     if (rule) {
@@ -38,77 +31,4 @@
     }
 }
 
-diag::Severity ToSeverity(DiagnosticSeverity sc) {
-    switch (sc) {
-        case DiagnosticSeverity::kError:
-            return diag::Severity::Error;
-        case DiagnosticSeverity::kWarning:
-            return diag::Severity::Warning;
-        case DiagnosticSeverity::kInfo:
-            return diag::Severity::Note;
-        default:
-            return diag::Severity::InternalCompilerError;
-    }
-}
-
-/// ParseDiagnosticSeverity parses a DiagnosticSeverity from a string.
-/// @param str the string to parse
-/// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
-DiagnosticSeverity ParseDiagnosticSeverity(std::string_view str) {
-    if (str == "error") {
-        return DiagnosticSeverity::kError;
-    }
-    if (str == "info") {
-        return DiagnosticSeverity::kInfo;
-    }
-    if (str == "off") {
-        return DiagnosticSeverity::kOff;
-    }
-    if (str == "warning") {
-        return DiagnosticSeverity::kWarning;
-    }
-    return DiagnosticSeverity::kUndefined;
-}
-
-std::ostream& operator<<(std::ostream& out, DiagnosticSeverity value) {
-    switch (value) {
-        case DiagnosticSeverity::kUndefined:
-            return out << "undefined";
-        case DiagnosticSeverity::kError:
-            return out << "error";
-        case DiagnosticSeverity::kInfo:
-            return out << "info";
-        case DiagnosticSeverity::kOff:
-            return out << "off";
-        case DiagnosticSeverity::kWarning:
-            return out << "warning";
-    }
-    return out << "<unknown>";
-}
-
-/// ParseDiagnosticRule parses a DiagnosticRule from a string.
-/// @param str the string to parse
-/// @returns the parsed enum, or DiagnosticRule::kUndefined if the string could not be parsed.
-DiagnosticRule ParseDiagnosticRule(std::string_view str) {
-    if (str == "chromium_unreachable_code") {
-        return DiagnosticRule::kChromiumUnreachableCode;
-    }
-    if (str == "derivative_uniformity") {
-        return DiagnosticRule::kDerivativeUniformity;
-    }
-    return DiagnosticRule::kUndefined;
-}
-
-std::ostream& operator<<(std::ostream& out, DiagnosticRule value) {
-    switch (value) {
-        case DiagnosticRule::kUndefined:
-            return out << "undefined";
-        case DiagnosticRule::kChromiumUnreachableCode:
-            return out << "chromium_unreachable_code";
-        case DiagnosticRule::kDerivativeUniformity:
-            return out << "derivative_uniformity";
-    }
-    return out << "<unknown>";
-}
-
 }  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control.h b/src/tint/ast/diagnostic_control.h
index f4fdd5f..3a5cd1f 100644
--- a/src/tint/ast/diagnostic_control.h
+++ b/src/tint/ast/diagnostic_control.h
@@ -12,14 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-////////////////////////////////////////////////////////////////////////////////
-// File generated by tools/src/cmd/gen
-// using the template:
-//   src/tint/ast/diagnostic_control.h.tmpl
-//
-// Do not modify this file directly
-////////////////////////////////////////////////////////////////////////////////
-
 #ifndef SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
 #define SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
 
@@ -27,6 +19,7 @@
 #include <string>
 #include <unordered_map>
 
+#include "src/tint/builtin/diagnostic_severity.h"
 #include "src/tint/diagnostic/diagnostic.h"
 
 // Forward declarations
@@ -36,60 +29,6 @@
 
 namespace tint::ast {
 
-/// The diagnostic severity control.
-enum class DiagnosticSeverity {
-    kUndefined,
-    kError,
-    kInfo,
-    kOff,
-    kWarning,
-};
-
-/// @param out the std::ostream to write to
-/// @param value the DiagnosticSeverity
-/// @returns `out` so calls can be chained
-std::ostream& operator<<(std::ostream& out, DiagnosticSeverity value);
-
-/// ParseDiagnosticSeverity parses a DiagnosticSeverity from a string.
-/// @param str the string to parse
-/// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
-DiagnosticSeverity ParseDiagnosticSeverity(std::string_view str);
-
-constexpr const char* kDiagnosticSeverityStrings[] = {
-    "error",
-    "info",
-    "off",
-    "warning",
-};
-
-/// The diagnostic rule.
-enum class DiagnosticRule {
-    kUndefined,
-    kChromiumUnreachableCode,
-    kDerivativeUniformity,
-};
-
-/// @param out the std::ostream to write to
-/// @param value the DiagnosticRule
-/// @returns `out` so calls can be chained
-std::ostream& operator<<(std::ostream& out, DiagnosticRule value);
-
-/// ParseDiagnosticRule parses a DiagnosticRule from a string.
-/// @param str the string to parse
-/// @returns the parsed enum, or DiagnosticRule::kUndefined if the string could not be parsed.
-DiagnosticRule ParseDiagnosticRule(std::string_view str);
-
-constexpr const char* kDiagnosticRuleStrings[] = {
-    "chromium_unreachable_code",
-    "derivative_uniformity",
-};
-
-/// Convert a DiagnosticSeverity to the corresponding diag::Severity.
-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.
 struct DiagnosticControl {
   public:
@@ -99,10 +38,10 @@
     /// Constructor
     /// @param sev the diagnostic severity
     /// @param rule the diagnostic rule name
-    DiagnosticControl(DiagnosticSeverity sev, const Identifier* rule);
+    DiagnosticControl(builtin::DiagnosticSeverity sev, const Identifier* rule);
 
     /// The diagnostic severity control.
-    DiagnosticSeverity severity;
+    builtin::DiagnosticSeverity severity;
 
     /// The diagnostic rule name.
     const Identifier* rule_name;
diff --git a/src/tint/ast/diagnostic_control.h.tmpl b/src/tint/ast/diagnostic_control.h.tmpl
deleted file mode 100644
index 4645d9c..0000000
--- a/src/tint/ast/diagnostic_control.h.tmpl
+++ /dev/null
@@ -1,61 +0,0 @@
-{{- /*
---------------------------------------------------------------------------------
-Template file for use with tools/src/cmd/gen to generate diagnostic_control.h
-
-See:
-* tools/src/cmd/gen for structures used by this template
-* https://golang.org/pkg/text/template/ for documentation on the template syntax
---------------------------------------------------------------------------------
-*/ -}}
-
-{{- Import "src/tint/templates/enums.tmpl.inc" -}}
-
-#ifndef SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
-#define SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
-
-#include <ostream>
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/diagnostic/diagnostic.h"
-
-// Forward declarations
-namespace tint::ast {
-class Identifier;
-}  // namespace tint::ast
-
-namespace tint::ast {
-
-/// The diagnostic severity control.
-{{ Eval "DeclareEnum" (Sem.Enum "diagnostic_severity") }}
-
-/// The diagnostic rule.
-{{ Eval "DeclareEnum" (Sem.Enum "diagnostic_rule") }}
-
-/// Convert a DiagnosticSeverity to the corresponding diag::Severity.
-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.
-struct DiagnosticControl {
-  public:
-    /// Default constructor.
-    DiagnosticControl() {}
-
-    /// Constructor
-    /// @param sev the diagnostic severity
-    /// @param rule the diagnostic rule name
-    DiagnosticControl(DiagnosticSeverity sev, const Identifier* rule);
-
-    /// The diagnostic severity control.
-    DiagnosticSeverity severity;
-
-    /// The diagnostic rule name.
-    const Identifier* rule_name;
-};
-
-}  // namespace tint::ast
-
-#endif  // SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
diff --git a/src/tint/ast/diagnostic_control_test.cc b/src/tint/ast/diagnostic_control_test.cc
index 73046ce..73dd2ea 100644
--- a/src/tint/ast/diagnostic_control_test.cc
+++ b/src/tint/ast/diagnostic_control_test.cc
@@ -12,19 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-////////////////////////////////////////////////////////////////////////////////
-// File generated by tools/src/cmd/gen
-// using the template:
-//   src/tint/ast/diagnostic_control_test.cc.tmpl
-//
-// Do not modify this file directly
-////////////////////////////////////////////////////////////////////////////////
-
 #include <string>
 
 #include "gtest/gtest-spi.h"
 #include "src/tint/ast/diagnostic_control.h"
 #include "src/tint/ast/test_helper.h"
+#include "src/tint/builtin/diagnostic_severity.h"
 
 namespace tint::ast {
 namespace {
@@ -35,119 +28,11 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            ast::DiagnosticControl control(DiagnosticSeverity::kWarning,
+            ast::DiagnosticControl control(builtin::DiagnosticSeverity::kWarning,
                                            b.Ident("name", "a", "b", "c"));
         },
         "internal compiler error");
 }
 
-namespace diagnostic_severity_tests {
-
-namespace parse_print_tests {
-
-struct Case {
-    const char* string;
-    DiagnosticSeverity value;
-};
-
-inline std::ostream& operator<<(std::ostream& out, Case c) {
-    return out << "'" << std::string(c.string) << "'";
-}
-
-static constexpr Case kValidCases[] = {
-    {"error", DiagnosticSeverity::kError},
-    {"info", DiagnosticSeverity::kInfo},
-    {"off", DiagnosticSeverity::kOff},
-    {"warning", DiagnosticSeverity::kWarning},
-};
-
-static constexpr Case kInvalidCases[] = {
-    {"erccr", DiagnosticSeverity::kUndefined},    {"3o", DiagnosticSeverity::kUndefined},
-    {"eVror", DiagnosticSeverity::kUndefined},    {"1nfo", DiagnosticSeverity::kUndefined},
-    {"iqfJ", DiagnosticSeverity::kUndefined},     {"illf77", DiagnosticSeverity::kUndefined},
-    {"oppqH", DiagnosticSeverity::kUndefined},    {"", DiagnosticSeverity::kUndefined},
-    {"Gb", DiagnosticSeverity::kUndefined},       {"warniivg", DiagnosticSeverity::kUndefined},
-    {"8WWrning", DiagnosticSeverity::kUndefined}, {"wxxning", DiagnosticSeverity::kUndefined},
-};
-
-using DiagnosticSeverityParseTest = testing::TestWithParam<Case>;
-
-TEST_P(DiagnosticSeverityParseTest, Parse) {
-    const char* string = GetParam().string;
-    DiagnosticSeverity expect = GetParam().value;
-    EXPECT_EQ(expect, ParseDiagnosticSeverity(string));
-}
-
-INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticSeverityParseTest, testing::ValuesIn(kValidCases));
-INSTANTIATE_TEST_SUITE_P(InvalidCases,
-                         DiagnosticSeverityParseTest,
-                         testing::ValuesIn(kInvalidCases));
-
-using DiagnosticSeverityPrintTest = testing::TestWithParam<Case>;
-
-TEST_P(DiagnosticSeverityPrintTest, Print) {
-    DiagnosticSeverity value = GetParam().value;
-    const char* expect = GetParam().string;
-    EXPECT_EQ(expect, utils::ToString(value));
-}
-
-INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticSeverityPrintTest, testing::ValuesIn(kValidCases));
-
-}  // namespace parse_print_tests
-
-}  // namespace diagnostic_severity_tests
-
-namespace diagnostic_rule_tests {
-
-namespace parse_print_tests {
-
-struct Case {
-    const char* string;
-    DiagnosticRule value;
-};
-
-inline std::ostream& operator<<(std::ostream& out, Case c) {
-    return out << "'" << std::string(c.string) << "'";
-}
-
-static constexpr Case kValidCases[] = {
-    {"chromium_unreachable_code", DiagnosticRule::kChromiumUnreachableCode},
-    {"derivative_uniformity", DiagnosticRule::kDerivativeUniformity},
-};
-
-static constexpr Case kInvalidCases[] = {
-    {"cXromggum_unreachable_cde", DiagnosticRule::kUndefined},
-    {"chroVium_unruchble_codX", DiagnosticRule::kUndefined},
-    {"chromium_3nreachable_code", DiagnosticRule::kUndefined},
-    {"derivatEve_uniformity", DiagnosticRule::kUndefined},
-    {"deTTPivative_uniformit", DiagnosticRule::kUndefined},
-    {"derivtive_uddxxformity", DiagnosticRule::kUndefined},
-};
-
-using DiagnosticRuleParseTest = testing::TestWithParam<Case>;
-
-TEST_P(DiagnosticRuleParseTest, Parse) {
-    const char* string = GetParam().string;
-    DiagnosticRule expect = GetParam().value;
-    EXPECT_EQ(expect, ParseDiagnosticRule(string));
-}
-
-INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticRuleParseTest, testing::ValuesIn(kValidCases));
-INSTANTIATE_TEST_SUITE_P(InvalidCases, DiagnosticRuleParseTest, testing::ValuesIn(kInvalidCases));
-
-using DiagnosticRulePrintTest = testing::TestWithParam<Case>;
-
-TEST_P(DiagnosticRulePrintTest, Print) {
-    DiagnosticRule value = GetParam().value;
-    const char* expect = GetParam().string;
-    EXPECT_EQ(expect, utils::ToString(value));
-}
-
-INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticRulePrintTest, testing::ValuesIn(kValidCases));
-
-}  // namespace parse_print_tests
-
-}  // namespace diagnostic_rule_tests
-
 }  // namespace
 }  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control_test.cc.tmpl b/src/tint/ast/diagnostic_control_test.cc.tmpl
deleted file mode 100644
index 9900556..0000000
--- a/src/tint/ast/diagnostic_control_test.cc.tmpl
+++ /dev/null
@@ -1,47 +0,0 @@
-{{- /*
---------------------------------------------------------------------------------
-Template file for use with tools/src/cmd/gen to generate diagnostic_control_test.cc
-
-See:
-* tools/src/cmd/gen for structures used by this template
-* https://golang.org/pkg/text/template/ for documentation on the template syntax
---------------------------------------------------------------------------------
-*/ -}}
-
-{{- Import "src/tint/templates/enums.tmpl.inc" -}}
-
-#include <string>
-
-#include "gtest/gtest-spi.h"
-#include "src/tint/ast/diagnostic_control.h"
-#include "src/tint/ast/test_helper.h"
-
-namespace tint::ast {
-namespace {
-
-using DiagnosticControlTest = TestHelper;
-
-TEST_F(DiagnosticControlTest, Assert_RuleNotTemplated) {
-    EXPECT_FATAL_FAILURE(
-        {
-            ProgramBuilder b;
-            ast::DiagnosticControl control(DiagnosticSeverity::kWarning,
-                                           b.Ident("name", "a", "b", "c"));
-        },
-        "internal compiler error");
-}
-
-namespace diagnostic_severity_tests {
-
-{{ Eval "TestParsePrintEnum" (Sem.Enum "diagnostic_severity")}}
-
-}  // namespace diagnostic_severity_tests
-
-namespace diagnostic_rule_tests {
-
-{{ Eval "TestParsePrintEnum" (Sem.Enum "diagnostic_rule")}}
-
-}  // namespace diagnostic_rule_tests
-
-}  // namespace
-}  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_directive_test.cc b/src/tint/ast/diagnostic_directive_test.cc
index fb88439..fe8fc5c 100644
--- a/src/tint/ast/diagnostic_directive_test.cc
+++ b/src/tint/ast/diagnostic_directive_test.cc
@@ -23,13 +23,13 @@
 
 TEST_F(DiagnosticDirectiveTest, Creation) {
     auto* name = Ident("foo");
-    DiagnosticControl control(ast::DiagnosticSeverity::kWarning, name);
+    DiagnosticControl control(builtin::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.severity, builtin::DiagnosticSeverity::kWarning);
     EXPECT_EQ(diag->control.rule_name, name);
 }
 
diff --git a/src/tint/ast/module_test.cc b/src/tint/ast/module_test.cc
index d80033d..35c1b94 100644
--- a/src/tint/ast/module_test.cc
+++ b/src/tint/ast/module_test.cc
@@ -136,9 +136,9 @@
 
 TEST_F(ModuleTest, Directives) {
     auto* enable_1 = Enable(builtin::Extension::kF16);
-    auto* diagnostic_1 = DiagnosticDirective(DiagnosticSeverity::kWarning, "foo");
+    auto* diagnostic_1 = DiagnosticDirective(builtin::DiagnosticSeverity::kWarning, "foo");
     auto* enable_2 = Enable(builtin::Extension::kChromiumExperimentalFullPtrParameters);
-    auto* diagnostic_2 = DiagnosticDirective(DiagnosticSeverity::kOff, "bar");
+    auto* diagnostic_2 = DiagnosticDirective(builtin::DiagnosticSeverity::kOff, "bar");
 
     this->SetResolveOnBuild(false);
     Program program(std::move(*this));
diff --git a/src/tint/builtin/diagnostic_rule.cc b/src/tint/builtin/diagnostic_rule.cc
new file mode 100644
index 0000000..7b020d5
--- /dev/null
+++ b/src/tint/builtin/diagnostic_rule.cc
@@ -0,0 +1,55 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/builtin/diagnostic_rule.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/builtin/diagnostic_rule.h"
+
+#include <ostream>
+#include <string>
+
+namespace tint::builtin {
+
+/// ParseDiagnosticRule parses a DiagnosticRule from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticRule::kUndefined if the string could not be parsed.
+DiagnosticRule ParseDiagnosticRule(std::string_view str) {
+    if (str == "chromium_unreachable_code") {
+        return DiagnosticRule::kChromiumUnreachableCode;
+    }
+    if (str == "derivative_uniformity") {
+        return DiagnosticRule::kDerivativeUniformity;
+    }
+    return DiagnosticRule::kUndefined;
+}
+
+std::ostream& operator<<(std::ostream& out, DiagnosticRule value) {
+    switch (value) {
+        case DiagnosticRule::kUndefined:
+            return out << "undefined";
+        case DiagnosticRule::kChromiumUnreachableCode:
+            return out << "chromium_unreachable_code";
+        case DiagnosticRule::kDerivativeUniformity:
+            return out << "derivative_uniformity";
+    }
+    return out << "<unknown>";
+}
+
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_rule.cc.tmpl b/src/tint/builtin/diagnostic_rule.cc.tmpl
new file mode 100644
index 0000000..4cf05a4
--- /dev/null
+++ b/src/tint/builtin/diagnostic_rule.cc.tmpl
@@ -0,0 +1,24 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control.cc
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#include "src/tint/builtin/diagnostic_rule.h"
+
+#include <ostream>
+#include <string>
+
+namespace tint::builtin {
+
+{{ Eval "ParseEnum" (Sem.Enum "diagnostic_rule")}}
+
+{{ Eval "EnumOStream" (Sem.Enum "diagnostic_rule")}}
+
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_rule.h b/src/tint/builtin/diagnostic_rule.h
new file mode 100644
index 0000000..55a6aab
--- /dev/null
+++ b/src/tint/builtin/diagnostic_rule.h
@@ -0,0 +1,54 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/builtin/diagnostic_rule.h.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SRC_TINT_BUILTIN_DIAGNOSTIC_RULE_H_
+#define SRC_TINT_BUILTIN_DIAGNOSTIC_RULE_H_
+
+#include <string>
+
+namespace tint::builtin {
+
+/// The diagnostic rule.
+enum class DiagnosticRule {
+    kUndefined,
+    kChromiumUnreachableCode,
+    kDerivativeUniformity,
+};
+
+/// @param out the std::ostream to write to
+/// @param value the DiagnosticRule
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, DiagnosticRule value);
+
+/// ParseDiagnosticRule parses a DiagnosticRule from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticRule::kUndefined if the string could not be parsed.
+DiagnosticRule ParseDiagnosticRule(std::string_view str);
+
+constexpr const char* kDiagnosticRuleStrings[] = {
+    "chromium_unreachable_code",
+    "derivative_uniformity",
+};
+
+}  // namespace tint::builtin
+
+#endif  // SRC_TINT_BUILTIN_DIAGNOSTIC_RULE_H_
diff --git a/src/tint/builtin/diagnostic_rule.h.tmpl b/src/tint/builtin/diagnostic_rule.h.tmpl
new file mode 100644
index 0000000..2e6f7f9
--- /dev/null
+++ b/src/tint/builtin/diagnostic_rule.h.tmpl
@@ -0,0 +1,25 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control.h
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#ifndef SRC_TINT_BUILTIN_DIAGNOSTIC_RULE_H_
+#define SRC_TINT_BUILTIN_DIAGNOSTIC_RULE_H_
+
+#include <string>
+
+namespace tint::builtin {
+
+/// The diagnostic rule.
+{{ Eval "DeclareEnum" (Sem.Enum "diagnostic_rule") }}
+
+}  // namespace tint::builtin
+
+#endif  // SRC_TINT_BUILTIN_DIAGNOSTIC_RULE_H_
diff --git a/src/tint/builtin/diagnostic_rule_bench.cc b/src/tint/builtin/diagnostic_rule_bench.cc
new file mode 100644
index 0000000..c98bd46
--- /dev/null
+++ b/src/tint/builtin/diagnostic_rule_bench.cc
@@ -0,0 +1,51 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/builtin/diagnostic_rule_bench.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/builtin/diagnostic_rule.h"
+
+#include <array>
+
+#include "benchmark/benchmark.h"
+
+namespace tint::builtin {
+namespace {
+
+void DiagnosticRuleParser(::benchmark::State& state) {
+    const char* kStrings[] = {
+        "chromium_unrachaccle_code",   "clromium_unreachab3_oe",    "chromium_unreachable_Vode",
+        "chromium_unreachable_code",   "chro1ium_unreachable_code", "chromium_unreJchableqqcde",
+        "chromium77unreallhable_code", "dqqrvatiHHe_uniforppity",   "deriatcv_nvformity",
+        "derivatbe_unGformity",        "derivative_uniformity",     "derivative_iinifvrmity",
+        "derivat8WWe_uniformity",      "drivaxxive_uniformity",
+    };
+    for (auto _ : state) {
+        for (auto* str : kStrings) {
+            auto result = ParseDiagnosticRule(str);
+            benchmark::DoNotOptimize(result);
+        }
+    }
+}
+
+BENCHMARK(DiagnosticRuleParser);
+
+}  // namespace
+}  // namespace tint::builtin
diff --git a/src/tint/ast/diagnostic_control_bench.cc.tmpl b/src/tint/builtin/diagnostic_rule_bench.cc.tmpl
similarity index 78%
copy from src/tint/ast/diagnostic_control_bench.cc.tmpl
copy to src/tint/builtin/diagnostic_rule_bench.cc.tmpl
index 55d3cce..2605aae 100644
--- a/src/tint/ast/diagnostic_control_bench.cc.tmpl
+++ b/src/tint/builtin/diagnostic_rule_bench.cc.tmpl
@@ -10,18 +10,16 @@
 
 {{- Import "src/tint/templates/enums.tmpl.inc" -}}
 
-#include "src/tint/ast/diagnostic_control.h"
+#include "src/tint/builtin/diagnostic_rule.h"
 
 #include <array>
 
 #include "benchmark/benchmark.h"
 
-namespace tint::ast {
+namespace tint::builtin {
 namespace {
 
-{{ Eval "BenchmarkParseEnum" (Sem.Enum "diagnostic_severity")}}
-
 {{ Eval "BenchmarkParseEnum" (Sem.Enum "diagnostic_rule")}}
 
 }  // namespace
-}  // namespace tint::ast
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_rule_test.cc b/src/tint/builtin/diagnostic_rule_test.cc
new file mode 100644
index 0000000..9982061
--- /dev/null
+++ b/src/tint/builtin/diagnostic_rule_test.cc
@@ -0,0 +1,85 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/builtin/diagnostic_rule_test.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include <string>
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/builtin/diagnostic_rule.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::builtin {
+namespace {
+
+namespace diagnostic_rule_tests {
+
+namespace parse_print_tests {
+
+struct Case {
+    const char* string;
+    DiagnosticRule value;
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+    return out << "'" << std::string(c.string) << "'";
+}
+
+static constexpr Case kValidCases[] = {
+    {"chromium_unreachable_code", DiagnosticRule::kChromiumUnreachableCode},
+    {"derivative_uniformity", DiagnosticRule::kDerivativeUniformity},
+};
+
+static constexpr Case kInvalidCases[] = {
+    {"chromium_unrachaccle_code", DiagnosticRule::kUndefined},
+    {"clromium_unreachab3_oe", DiagnosticRule::kUndefined},
+    {"chromium_unreachable_Vode", DiagnosticRule::kUndefined},
+    {"derivative_uniform1ty", DiagnosticRule::kUndefined},
+    {"derivativeJunifqrmity", DiagnosticRule::kUndefined},
+    {"derivative_unifllrmit77", DiagnosticRule::kUndefined},
+};
+
+using DiagnosticRuleParseTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticRuleParseTest, Parse) {
+    const char* string = GetParam().string;
+    DiagnosticRule expect = GetParam().value;
+    EXPECT_EQ(expect, ParseDiagnosticRule(string));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticRuleParseTest, testing::ValuesIn(kValidCases));
+INSTANTIATE_TEST_SUITE_P(InvalidCases, DiagnosticRuleParseTest, testing::ValuesIn(kInvalidCases));
+
+using DiagnosticRulePrintTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticRulePrintTest, Print) {
+    DiagnosticRule value = GetParam().value;
+    const char* expect = GetParam().string;
+    EXPECT_EQ(expect, utils::ToString(value));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticRulePrintTest, testing::ValuesIn(kValidCases));
+
+}  // namespace parse_print_tests
+
+}  // namespace diagnostic_rule_tests
+
+}  // namespace
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_rule_test.cc.tmpl b/src/tint/builtin/diagnostic_rule_test.cc.tmpl
new file mode 100644
index 0000000..106f6fc
--- /dev/null
+++ b/src/tint/builtin/diagnostic_rule_test.cc.tmpl
@@ -0,0 +1,29 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control_test.cc
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#include <string>
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/builtin/diagnostic_rule.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::builtin {
+namespace {
+
+namespace diagnostic_rule_tests {
+
+{{ Eval "TestParsePrintEnum" (Sem.Enum "diagnostic_rule")}}
+
+}  // namespace diagnostic_rule_tests
+
+}  // namespace
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_severity.cc b/src/tint/builtin/diagnostic_severity.cc
new file mode 100644
index 0000000..8c14a9a
--- /dev/null
+++ b/src/tint/builtin/diagnostic_severity.cc
@@ -0,0 +1,77 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/builtin/diagnostic_severity.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/builtin/diagnostic_severity.h"
+
+#include <string>
+
+namespace tint::builtin {
+
+diag::Severity ToSeverity(DiagnosticSeverity sc) {
+    switch (sc) {
+        case DiagnosticSeverity::kError:
+            return diag::Severity::Error;
+        case DiagnosticSeverity::kWarning:
+            return diag::Severity::Warning;
+        case DiagnosticSeverity::kInfo:
+            return diag::Severity::Note;
+        default:
+            return diag::Severity::InternalCompilerError;
+    }
+}
+
+/// ParseDiagnosticSeverity parses a DiagnosticSeverity from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
+DiagnosticSeverity ParseDiagnosticSeverity(std::string_view str) {
+    if (str == "error") {
+        return DiagnosticSeverity::kError;
+    }
+    if (str == "info") {
+        return DiagnosticSeverity::kInfo;
+    }
+    if (str == "off") {
+        return DiagnosticSeverity::kOff;
+    }
+    if (str == "warning") {
+        return DiagnosticSeverity::kWarning;
+    }
+    return DiagnosticSeverity::kUndefined;
+}
+
+std::ostream& operator<<(std::ostream& out, DiagnosticSeverity value) {
+    switch (value) {
+        case DiagnosticSeverity::kUndefined:
+            return out << "undefined";
+        case DiagnosticSeverity::kError:
+            return out << "error";
+        case DiagnosticSeverity::kInfo:
+            return out << "info";
+        case DiagnosticSeverity::kOff:
+            return out << "off";
+        case DiagnosticSeverity::kWarning:
+            return out << "warning";
+    }
+    return out << "<unknown>";
+}
+
+}  // namespace tint::builtin
diff --git a/src/tint/ast/diagnostic_control.cc.tmpl b/src/tint/builtin/diagnostic_severity.cc.tmpl
similarity index 61%
rename from src/tint/ast/diagnostic_control.cc.tmpl
rename to src/tint/builtin/diagnostic_severity.cc.tmpl
index bfdf86a..3278bb1 100644
--- a/src/tint/ast/diagnostic_control.cc.tmpl
+++ b/src/tint/builtin/diagnostic_severity.cc.tmpl
@@ -10,23 +10,11 @@
 
 {{- Import "src/tint/templates/enums.tmpl.inc" -}}
 
-#include "src/tint/ast/diagnostic_control.h"
+#include "src/tint/builtin/diagnostic_severity.h"
 
 #include <string>
 
-#include "src/tint/ast/identifier.h"
-#include "src/tint/ast/templated_identifier.h"
-
-namespace tint::ast {
-
-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
-        TINT_ASSERT(AST, !rule->Is<TemplatedIdentifier>());
-    }
-}
+namespace tint::builtin {
 
 diag::Severity ToSeverity(DiagnosticSeverity sc) {
     switch (sc) {
@@ -45,8 +33,4 @@
 
 {{ Eval "EnumOStream" (Sem.Enum "diagnostic_severity")}}
 
-{{ Eval "ParseEnum" (Sem.Enum "diagnostic_rule")}}
-
-{{ Eval "EnumOStream" (Sem.Enum "diagnostic_rule")}}
-
-}  // namespace tint::ast
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_severity.h b/src/tint/builtin/diagnostic_severity.h
new file mode 100644
index 0000000..daef0f7
--- /dev/null
+++ b/src/tint/builtin/diagnostic_severity.h
@@ -0,0 +1,69 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/builtin/diagnostic_severity.h.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SRC_TINT_BUILTIN_DIAGNOSTIC_SEVERITY_H_
+#define SRC_TINT_BUILTIN_DIAGNOSTIC_SEVERITY_H_
+
+#include <ostream>
+#include <string>
+#include <unordered_map>
+
+#include "src/tint/builtin/diagnostic_rule.h"
+#include "src/tint/diagnostic/diagnostic.h"
+
+namespace tint::builtin {
+
+/// The diagnostic severity control.
+enum class DiagnosticSeverity {
+    kUndefined,
+    kError,
+    kInfo,
+    kOff,
+    kWarning,
+};
+
+/// @param out the std::ostream to write to
+/// @param value the DiagnosticSeverity
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, DiagnosticSeverity value);
+
+/// ParseDiagnosticSeverity parses a DiagnosticSeverity from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
+DiagnosticSeverity ParseDiagnosticSeverity(std::string_view str);
+
+constexpr const char* kDiagnosticSeverityStrings[] = {
+    "error",
+    "info",
+    "off",
+    "warning",
+};
+
+/// Convert a DiagnosticSeverity to the corresponding diag::Severity.
+diag::Severity ToSeverity(DiagnosticSeverity sc);
+
+/// DiagnosticRuleSeverities is a map from diagnostic rule to diagnostic severity.
+using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
+
+}  // namespace tint::builtin
+
+#endif  // SRC_TINT_BUILTIN_DIAGNOSTIC_SEVERITY_H_
diff --git a/src/tint/builtin/diagnostic_severity.h.tmpl b/src/tint/builtin/diagnostic_severity.h.tmpl
new file mode 100644
index 0000000..7580b2c
--- /dev/null
+++ b/src/tint/builtin/diagnostic_severity.h.tmpl
@@ -0,0 +1,36 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control.h
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#ifndef SRC_TINT_BUILTIN_DIAGNOSTIC_SEVERITY_H_
+#define SRC_TINT_BUILTIN_DIAGNOSTIC_SEVERITY_H_
+
+#include <ostream>
+#include <string>
+#include <unordered_map>
+
+#include "src/tint/builtin/diagnostic_rule.h"
+#include "src/tint/diagnostic/diagnostic.h"
+
+namespace tint::builtin {
+
+/// The diagnostic severity control.
+{{ Eval "DeclareEnum" (Sem.Enum "diagnostic_severity") }}
+
+/// Convert a DiagnosticSeverity to the corresponding diag::Severity.
+diag::Severity ToSeverity(DiagnosticSeverity sc);
+
+/// DiagnosticRuleSeverities is a map from diagnostic rule to diagnostic severity.
+using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
+
+}  // namespace tint::builtin
+
+#endif  // SRC_TINT_BUILTIN_DIAGNOSTIC_SEVERITY_H_
diff --git a/src/tint/ast/diagnostic_control_bench.cc b/src/tint/builtin/diagnostic_severity_bench.cc
similarity index 63%
rename from src/tint/ast/diagnostic_control_bench.cc
rename to src/tint/builtin/diagnostic_severity_bench.cc
index 6fb5014..13c3ff1 100644
--- a/src/tint/ast/diagnostic_control_bench.cc
+++ b/src/tint/builtin/diagnostic_severity_bench.cc
@@ -15,18 +15,18 @@
 ////////////////////////////////////////////////////////////////////////////////
 // File generated by tools/src/cmd/gen
 // using the template:
-//   src/tint/ast/diagnostic_control_bench.cc.tmpl
+//   src/tint/builtin/diagnostic_severity_bench.cc.tmpl
 //
 // Do not modify this file directly
 ////////////////////////////////////////////////////////////////////////////////
 
-#include "src/tint/ast/diagnostic_control.h"
+#include "src/tint/builtin/diagnostic_severity.h"
 
 #include <array>
 
 #include "benchmark/benchmark.h"
 
-namespace tint::ast {
+namespace tint::builtin {
 namespace {
 
 void DiagnosticSeverityParser(::benchmark::State& state) {
@@ -46,23 +46,5 @@
 
 BENCHMARK(DiagnosticSeverityParser);
 
-void DiagnosticRuleParser(::benchmark::State& state) {
-    const char* kStrings[] = {
-        "hromium_unyeachable_code",   "chrorrillmGunnreachable_c77de", "chromium_unreachable4cod00",
-        "chromium_unreachable_code",  "chromium_unracaboo_code",       "chromium_unrzzchabl_code",
-        "ciipp11ium_unreachable_cod", "derivXXtive_uniformity",        "55erivativeIIunifonn99ity",
-        "derirratHHaae_YniforSSity",  "derivative_uniformity",         "erivtive_unHkkormit",
-        "jerivaive_uniforRgty",       "derivatbve_unformiy",
-    };
-    for (auto _ : state) {
-        for (auto* str : kStrings) {
-            auto result = ParseDiagnosticRule(str);
-            benchmark::DoNotOptimize(result);
-        }
-    }
-}
-
-BENCHMARK(DiagnosticRuleParser);
-
 }  // namespace
-}  // namespace tint::ast
+}  // namespace tint::builtin
diff --git a/src/tint/ast/diagnostic_control_bench.cc.tmpl b/src/tint/builtin/diagnostic_severity_bench.cc.tmpl
similarity index 79%
rename from src/tint/ast/diagnostic_control_bench.cc.tmpl
rename to src/tint/builtin/diagnostic_severity_bench.cc.tmpl
index 55d3cce..4d2dcee3e 100644
--- a/src/tint/ast/diagnostic_control_bench.cc.tmpl
+++ b/src/tint/builtin/diagnostic_severity_bench.cc.tmpl
@@ -10,18 +10,16 @@
 
 {{- Import "src/tint/templates/enums.tmpl.inc" -}}
 
-#include "src/tint/ast/diagnostic_control.h"
+#include "src/tint/builtin/diagnostic_severity.h"
 
 #include <array>
 
 #include "benchmark/benchmark.h"
 
-namespace tint::ast {
+namespace tint::builtin {
 namespace {
 
 {{ Eval "BenchmarkParseEnum" (Sem.Enum "diagnostic_severity")}}
 
-{{ Eval "BenchmarkParseEnum" (Sem.Enum "diagnostic_rule")}}
-
 }  // namespace
-}  // namespace tint::ast
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_severity_test.cc b/src/tint/builtin/diagnostic_severity_test.cc
new file mode 100644
index 0000000..bfd51b4
--- /dev/null
+++ b/src/tint/builtin/diagnostic_severity_test.cc
@@ -0,0 +1,89 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/builtin/diagnostic_severity_test.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include <string>
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/builtin/diagnostic_severity.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::builtin {
+namespace {
+
+namespace diagnostic_severity_tests {
+
+namespace parse_print_tests {
+
+struct Case {
+    const char* string;
+    DiagnosticSeverity value;
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+    return out << "'" << std::string(c.string) << "'";
+}
+
+static constexpr Case kValidCases[] = {
+    {"error", DiagnosticSeverity::kError},
+    {"info", DiagnosticSeverity::kInfo},
+    {"off", DiagnosticSeverity::kOff},
+    {"warning", DiagnosticSeverity::kWarning},
+};
+
+static constexpr Case kInvalidCases[] = {
+    {"erccr", DiagnosticSeverity::kUndefined},    {"3o", DiagnosticSeverity::kUndefined},
+    {"eVror", DiagnosticSeverity::kUndefined},    {"1nfo", DiagnosticSeverity::kUndefined},
+    {"iqfJ", DiagnosticSeverity::kUndefined},     {"illf77", DiagnosticSeverity::kUndefined},
+    {"oppqH", DiagnosticSeverity::kUndefined},    {"", DiagnosticSeverity::kUndefined},
+    {"Gb", DiagnosticSeverity::kUndefined},       {"warniivg", DiagnosticSeverity::kUndefined},
+    {"8WWrning", DiagnosticSeverity::kUndefined}, {"wxxning", DiagnosticSeverity::kUndefined},
+};
+
+using DiagnosticSeverityParseTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticSeverityParseTest, Parse) {
+    const char* string = GetParam().string;
+    DiagnosticSeverity expect = GetParam().value;
+    EXPECT_EQ(expect, ParseDiagnosticSeverity(string));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticSeverityParseTest, testing::ValuesIn(kValidCases));
+INSTANTIATE_TEST_SUITE_P(InvalidCases,
+                         DiagnosticSeverityParseTest,
+                         testing::ValuesIn(kInvalidCases));
+
+using DiagnosticSeverityPrintTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticSeverityPrintTest, Print) {
+    DiagnosticSeverity value = GetParam().value;
+    const char* expect = GetParam().string;
+    EXPECT_EQ(expect, utils::ToString(value));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticSeverityPrintTest, testing::ValuesIn(kValidCases));
+
+}  // namespace parse_print_tests
+
+}  // namespace diagnostic_severity_tests
+
+}  // namespace
+}  // namespace tint::builtin
diff --git a/src/tint/builtin/diagnostic_severity_test.cc.tmpl b/src/tint/builtin/diagnostic_severity_test.cc.tmpl
new file mode 100644
index 0000000..1bd06b4
--- /dev/null
+++ b/src/tint/builtin/diagnostic_severity_test.cc.tmpl
@@ -0,0 +1,29 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control_test.cc
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#include <string>
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/builtin/diagnostic_severity.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::builtin {
+namespace {
+
+namespace diagnostic_severity_tests {
+
+{{ Eval "TestParsePrintEnum" (Sem.Enum "diagnostic_severity")}}
+
+}  // namespace diagnostic_severity_tests
+
+}  // namespace
+}  // namespace tint::builtin
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 1d3ccf6..0b678dc 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -3643,7 +3643,7 @@
     /// @returns the diagnostic attribute pointer
     template <typename NAME>
     const ast::DiagnosticAttribute* DiagnosticAttribute(const Source& source,
-                                                        ast::DiagnosticSeverity severity,
+                                                        builtin::DiagnosticSeverity severity,
                                                         NAME&& rule_name) {
         static_assert(!traits::IsType<traits::PtrElTy<NAME>, ast::TemplatedIdentifier>,
                       "it is invalid for a diagnostic rule name to be templated");
@@ -3656,7 +3656,7 @@
     /// @param rule_name the diagnostic rule name
     /// @returns the diagnostic attribute pointer
     template <typename NAME>
-    const ast::DiagnosticAttribute* DiagnosticAttribute(ast::DiagnosticSeverity severity,
+    const ast::DiagnosticAttribute* DiagnosticAttribute(builtin::DiagnosticSeverity severity,
                                                         NAME&& rule_name) {
         return create<ast::DiagnosticAttribute>(
             source_, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
@@ -3669,7 +3669,7 @@
     /// @returns the diagnostic directive pointer
     template <typename NAME>
     const ast::DiagnosticDirective* DiagnosticDirective(const Source& source,
-                                                        ast::DiagnosticSeverity severity,
+                                                        builtin::DiagnosticSeverity severity,
                                                         NAME&& rule_name) {
         auto* directive = create<ast::DiagnosticDirective>(
             source, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
@@ -3682,7 +3682,7 @@
     /// @param rule_name the diagnostic rule name
     /// @returns the diagnostic directive pointer
     template <typename NAME>
-    const ast::DiagnosticDirective* DiagnosticDirective(ast::DiagnosticSeverity severity,
+    const ast::DiagnosticDirective* DiagnosticDirective(builtin::DiagnosticSeverity severity,
                                                         NAME&& rule_name) {
         auto* directive = create<ast::DiagnosticDirective>(
             source_, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
diff --git a/src/tint/reader/spirv/parser.cc b/src/tint/reader/spirv/parser.cc
index be0d238..2a1ae18 100644
--- a/src/tint/reader/spirv/parser.cc
+++ b/src/tint/reader/spirv/parser.cc
@@ -41,7 +41,7 @@
     if (options.allow_non_uniform_derivatives) {
         // Suppress errors regarding non-uniform derivative operations if requested, by adding a
         // diagnostic directive to the module.
-        builder.DiagnosticDirective(ast::DiagnosticSeverity::kOff, "derivative_uniformity");
+        builder.DiagnosticDirective(builtin::DiagnosticSeverity::kOff, "derivative_uniformity");
     }
 
     // The SPIR-V parser can construct disjoint AST nodes, which is invalid for
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index c9a8576..ba9f7eb 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -3207,9 +3207,9 @@
 //   | 'warning'
 //   | 'info'
 //   | 'off'
-Expect<ast::DiagnosticSeverity> ParserImpl::expect_severity_control_name() {
-    return expect_enum("severity control", ast::ParseDiagnosticSeverity,
-                       ast::kDiagnosticSeverityStrings);
+Expect<builtin::DiagnosticSeverity> ParserImpl::expect_severity_control_name() {
+    return expect_enum("severity control", builtin::ParseDiagnosticSeverity,
+                       builtin::kDiagnosticSeverityStrings);
 }
 
 // diagnostic_control
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index a034010..e67d745 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -649,7 +649,7 @@
     Expect<const ast::Attribute*> expect_attribute();
     /// Parses a severity_control_name grammar element.
     /// @return the parsed severity control name, or nullptr on error.
-    Expect<ast::DiagnosticSeverity> expect_severity_control_name();
+    Expect<builtin::DiagnosticSeverity> expect_severity_control_name();
     /// Parses a diagnostic_control grammar element.
     /// @return the parsed diagnostic control, or nullptr on error.
     Expect<ast::DiagnosticControl> expect_diagnostic_control();
diff --git a/src/tint/reader/wgsl/parser_impl_diagnostic_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_diagnostic_attribute_test.cc
index 6c5e6f6..5c54e93 100644
--- a/src/tint/reader/wgsl/parser_impl_diagnostic_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_diagnostic_attribute_test.cc
@@ -27,7 +27,7 @@
     EXPECT_TRUE(a.matched);
     auto* d = a.value->As<ast::DiagnosticAttribute>();
     ASSERT_NE(d, nullptr);
-    EXPECT_EQ(d->control.severity, ast::DiagnosticSeverity::kOff);
+    EXPECT_EQ(d->control.severity, builtin::DiagnosticSeverity::kOff);
     auto* r = d->control.rule_name;
     ASSERT_NE(r, nullptr);
     ast::CheckIdentifier(p->builder().Symbols(), r, "foo");
diff --git a/src/tint/reader/wgsl/parser_impl_diagnostic_control_test.cc b/src/tint/reader/wgsl/parser_impl_diagnostic_control_test.cc
index 4454a9d..81ce178 100644
--- a/src/tint/reader/wgsl/parser_impl_diagnostic_control_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_diagnostic_control_test.cc
@@ -20,7 +20,7 @@
 namespace tint::reader::wgsl {
 namespace {
 
-using SeverityPair = std::pair<std::string, ast::DiagnosticSeverity>;
+using SeverityPair = std::pair<std::string, builtin::DiagnosticSeverity>;
 class DiagnosticControlParserTest : public ParserImplTestWithParam<SeverityPair> {};
 
 TEST_P(DiagnosticControlParserTest, DiagnosticControl_Valid) {
@@ -37,17 +37,18 @@
 }
 INSTANTIATE_TEST_SUITE_P(DiagnosticControlParserTest,
                          DiagnosticControlParserTest,
-                         testing::Values(SeverityPair{"error", ast::DiagnosticSeverity::kError},
-                                         SeverityPair{"warning", ast::DiagnosticSeverity::kWarning},
-                                         SeverityPair{"info", ast::DiagnosticSeverity::kInfo},
-                                         SeverityPair{"off", ast::DiagnosticSeverity::kOff}));
+                         testing::Values(SeverityPair{"error", builtin::DiagnosticSeverity::kError},
+                                         SeverityPair{"warning",
+                                                      builtin::DiagnosticSeverity::kWarning},
+                                         SeverityPair{"info", builtin::DiagnosticSeverity::kInfo},
+                                         SeverityPair{"off", builtin::DiagnosticSeverity::kOff}));
 
 TEST_F(ParserImplTest, DiagnosticControl_Valid_TrailingComma) {
     auto p = parser("(error, foo,)");
     auto e = p->expect_diagnostic_control();
     EXPECT_FALSE(e.errored);
     EXPECT_FALSE(p->has_error()) << p->error();
-    EXPECT_EQ(e->severity, ast::DiagnosticSeverity::kError);
+    EXPECT_EQ(e->severity, builtin::DiagnosticSeverity::kError);
 
     auto* r = e->rule_name;
     ASSERT_NE(r, nullptr);
diff --git a/src/tint/reader/wgsl/parser_impl_diagnostic_directive_test.cc b/src/tint/reader/wgsl/parser_impl_diagnostic_directive_test.cc
index ab50f34..b2f987c 100644
--- a/src/tint/reader/wgsl/parser_impl_diagnostic_directive_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_diagnostic_directive_test.cc
@@ -27,7 +27,7 @@
     auto& ast = p->builder().AST();
     ASSERT_EQ(ast.DiagnosticDirectives().Length(), 1u);
     auto* directive = ast.DiagnosticDirectives()[0];
-    EXPECT_EQ(directive->control.severity, ast::DiagnosticSeverity::kOff);
+    EXPECT_EQ(directive->control.severity, builtin::DiagnosticSeverity::kOff);
     ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
     EXPECT_EQ(ast.GlobalDeclarations()[0], directive);
 
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index fa7664b..982b865 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -97,7 +97,7 @@
         case AttributeKind::kBuiltin:
             return {builder.Builtin(source, builtin::BuiltinValue::kPosition)};
         case AttributeKind::kDiagnostic:
-            return {builder.DiagnosticAttribute(source, ast::DiagnosticSeverity::kInfo,
+            return {builder.DiagnosticAttribute(source, builtin::DiagnosticSeverity::kInfo,
                                                 "chromium_unreachable_code")};
         case AttributeKind::kGroup:
             return {builder.Group(source, 1_a)};
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index c8cc60a..8f15ad9 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1097,7 +1097,7 @@
     auto* var_1 = GlobalVar("SYMBOL1", ty.i32());
     auto* enable = Enable(builtin::Extension::kF16);
     auto* var_2 = GlobalVar("SYMBOL2", ty.f32());
-    auto* diagnostic = DiagnosticDirective(ast::DiagnosticSeverity::kWarning, "foo");
+    auto* diagnostic = DiagnosticDirective(builtin::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));
diff --git a/src/tint/resolver/diagnostic_control_test.cc b/src/tint/resolver/diagnostic_control_test.cc
index 6d892fb..d7143c7 100644
--- a/src/tint/resolver/diagnostic_control_test.cc
+++ b/src/tint/resolver/diagnostic_control_test.cc
@@ -32,7 +32,7 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_ErrorViaDirective) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError, "chromium_unreachable_code");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts);
@@ -42,7 +42,7 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_WarningViaDirective) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kWarning, "chromium_unreachable_code");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kWarning, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts);
@@ -52,7 +52,7 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_InfoViaDirective) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kInfo, "chromium_unreachable_code");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kInfo, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts);
@@ -62,7 +62,7 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_OffViaDirective) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kOff, "chromium_unreachable_code");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kOff, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts);
@@ -72,7 +72,8 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_ErrorViaAttribute) {
-    auto* attr = DiagnosticAttribute(ast::DiagnosticSeverity::kError, "chromium_unreachable_code");
+    auto* attr =
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kError, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
@@ -83,7 +84,7 @@
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_WarningViaAttribute) {
     auto* attr =
-        DiagnosticAttribute(ast::DiagnosticSeverity::kWarning, "chromium_unreachable_code");
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kWarning, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
@@ -93,7 +94,8 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_InfoViaAttribute) {
-    auto* attr = DiagnosticAttribute(ast::DiagnosticSeverity::kInfo, "chromium_unreachable_code");
+    auto* attr =
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kInfo, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
@@ -103,7 +105,8 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnreachableCode_OffViaAttribute) {
-    auto* attr = DiagnosticAttribute(ast::DiagnosticSeverity::kOff, "chromium_unreachable_code");
+    auto* attr =
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
@@ -119,9 +122,9 @@
     //   return;
     //   return; // Should produce a warning
     // }
-    DiagnosticDirective(ast::DiagnosticSeverity::kError, "chromium_unreachable_code");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError, "chromium_unreachable_code");
     auto* attr =
-        DiagnosticAttribute(ast::DiagnosticSeverity::kWarning, "chromium_unreachable_code");
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kWarning, "chromium_unreachable_code");
 
     auto stmts = utils::Vector{Return(), Return()};
     Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
@@ -147,7 +150,7 @@
     // }
     {
         auto* attr =
-            DiagnosticAttribute(ast::DiagnosticSeverity::kOff, "chromium_unreachable_code");
+            DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "chromium_unreachable_code");
         Func("foo", {}, ty.void_(),
              utils::Vector{
                  Return(),
@@ -164,7 +167,7 @@
     }
     {
         auto* attr =
-            DiagnosticAttribute(ast::DiagnosticSeverity::kInfo, "chromium_unreachable_code");
+            DiagnosticAttribute(builtin::DiagnosticSeverity::kInfo, "chromium_unreachable_code");
         Func("zoo", {}, ty.void_(),
              utils::Vector{
                  Return(),
@@ -215,7 +218,7 @@
                                     Return(),
                                     Return(Source{{34, 43}}),
                                 },
-                                attr(ast::DiagnosticSeverity::kInfo)),
+                                attr(builtin::DiagnosticSeverity::kInfo)),
                             Else(Block(utils::Vector{
                                 While(
                                     Expr(true), Block(
@@ -223,15 +226,15 @@
                                                         Return(),
                                                         Return(Source{{56, 65}}),
                                                     },
-                                                    attr(ast::DiagnosticSeverity::kOff))),
+                                                    attr(builtin::DiagnosticSeverity::kOff))),
                                 Return(),
                                 Return(Source{{78, 87}}),
                             }))),
                      },
-                     attr(ast::DiagnosticSeverity::kWarning)),
+                     attr(builtin::DiagnosticSeverity::kWarning)),
              }),
          },
-         attr(ast::DiagnosticSeverity::kOff));
+         attr(builtin::DiagnosticSeverity::kOff));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(), R"(34:43 note: code is unreachable
@@ -239,7 +242,7 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnrecognizedRuleName_Directive) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError,
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError,
                         Ident(Source{{12, 34}}, "chromium_unreachable_cod"));
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
@@ -249,7 +252,7 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, UnrecognizedRuleName_Attribute) {
-    auto* attr = DiagnosticAttribute(ast::DiagnosticSeverity::kError,
+    auto* attr = DiagnosticAttribute(builtin::DiagnosticSeverity::kError,
                                      Ident(Source{{12, 34}}, "chromium_unreachable_cod"));
     Func("foo", {}, ty.void_(), {}, utils::Vector{attr});
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -260,17 +263,17 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameSameSeverity_Directive) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError,
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError,
                         Ident(Source{{12, 34}}, "chromium_unreachable_code"));
-    DiagnosticDirective(ast::DiagnosticSeverity::kError,
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError,
                         Ident(Source{{56, 78}}, "chromium_unreachable_code"));
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameDifferentSeverity_Directive) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError,
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError,
                         Ident(Source{{12, 34}}, "chromium_unreachable_code"));
-    DiagnosticDirective(ast::DiagnosticSeverity::kOff,
+    DiagnosticDirective(builtin::DiagnosticSeverity::kOff,
                         Ident(Source{{56, 78}}, "chromium_unreachable_code"));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -279,9 +282,9 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_SameUnknownNameDifferentSeverity_Directive) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError,
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError,
                         Ident(Source{{12, 34}}, "chromium_unreachable_codes"));
-    DiagnosticDirective(ast::DiagnosticSeverity::kOff,
+    DiagnosticDirective(builtin::DiagnosticSeverity::kOff,
                         Ident(Source{{56, 78}}, "chromium_unreachable_codes"));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -296,24 +299,24 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_DifferentUnknownNameDifferentSeverity_Directive) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError, "chromium_unreachable_codes");
-    DiagnosticDirective(ast::DiagnosticSeverity::kOff, "chromium_unreachable_codex");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError, "chromium_unreachable_codes");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kOff, "chromium_unreachable_codex");
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameSameSeverity_Attribute) {
-    auto* attr1 = DiagnosticAttribute(ast::DiagnosticSeverity::kError,
+    auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kError,
                                       Ident(Source{{12, 34}}, "chromium_unreachable_code"));
-    auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kError,
+    auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kError,
                                       Ident(Source{{56, 78}}, "chromium_unreachable_code"));
     Func("foo", {}, ty.void_(), {}, utils::Vector{attr1, attr2});
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_SameNameDifferentSeverity_Attribute) {
-    auto* attr1 = DiagnosticAttribute(ast::DiagnosticSeverity::kError,
+    auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kError,
                                       Ident(Source{{12, 34}}, "chromium_unreachable_code"));
-    auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff,
+    auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff,
                                       Ident(Source{{56, 78}}, "chromium_unreachable_code"));
     Func("foo", {}, ty.void_(), {}, utils::Vector{attr1, attr2});
     EXPECT_FALSE(r()->Resolve());
@@ -323,9 +326,9 @@
 }
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_SameUnknownNameDifferentSeverity_Attribute) {
-    auto* attr1 = DiagnosticAttribute(ast::DiagnosticSeverity::kError,
+    auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kError,
                                       Ident(Source{{12, 34}}, "chromium_unreachable_codes"));
-    auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff,
+    auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff,
                                       Ident(Source{{56, 78}}, "chromium_unreachable_codes"));
     Func("foo", {}, ty.void_(), {}, utils::Vector{attr1, attr2});
     EXPECT_FALSE(r()->Resolve());
@@ -342,8 +345,9 @@
 
 TEST_F(ResolverDiagnosticControlTest, Conflict_DifferentUnknownNameDifferentSeverity_Attribute) {
     auto* attr1 =
-        DiagnosticAttribute(ast::DiagnosticSeverity::kError, "chromium_unreachable_codes");
-    auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff, "chromium_unreachable_codex");
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kError, "chromium_unreachable_codes");
+    auto* attr2 =
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "chromium_unreachable_codex");
     Func("foo", {}, ty.void_(), {}, utils::Vector{attr1, attr2});
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index f5432b1..2bbf15d 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -3311,13 +3311,13 @@
     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) {
+    auto rule = builtin::ParseDiagnosticRule(rule_name);
+    if (rule != builtin::DiagnosticRule::kUndefined) {
         validator_.DiagnosticFilters().Set(rule, control.severity);
     } else {
         std::ostringstream ss;
         ss << "unrecognized diagnostic rule '" << rule_name << "'\n";
-        utils::SuggestAlternatives(rule_name, ast::kDiagnosticRuleStrings, ss);
+        utils::SuggestAlternatives(rule_name, builtin::kDiagnosticRuleStrings, ss);
         AddWarning(ss.str(), control.rule_name->source);
     }
     return true;
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index 35763ef..549c0cc 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -71,7 +71,7 @@
         CallSiteRequiredToBeUniform,
         CallSiteNoRestriction,
     } tag;
-    ast::DiagnosticSeverity severity = ast::DiagnosticSeverity::kUndefined;
+    builtin::DiagnosticSeverity severity = builtin::DiagnosticSeverity::kUndefined;
 };
 
 /// FunctionTag describes a functions effects on uniformity.
@@ -87,7 +87,7 @@
         ParameterContentsRequiredToBeUniform,
         ParameterNoRestriction,
     } tag;
-    ast::DiagnosticSeverity severity = ast::DiagnosticSeverity::kUndefined;
+    builtin::DiagnosticSeverity severity = builtin::DiagnosticSeverity::kUndefined;
 };
 
 /// Node represents a node in the graph of control flow and value nodes within the analysis of a
@@ -257,13 +257,13 @@
     };
 
     /// @returns the RequiredToBeUniform node that corresponds to `severity`
-    Node* RequiredToBeUniform(ast::DiagnosticSeverity severity) {
+    Node* RequiredToBeUniform(builtin::DiagnosticSeverity severity) {
         switch (severity) {
-            case ast::DiagnosticSeverity::kError:
+            case builtin::DiagnosticSeverity::kError:
                 return required_to_be_uniform_error;
-            case ast::DiagnosticSeverity::kWarning:
+            case builtin::DiagnosticSeverity::kWarning:
                 return required_to_be_uniform_warning;
-            case ast::DiagnosticSeverity::kInfo:
+            case builtin::DiagnosticSeverity::kInfo:
                 return required_to_be_uniform_info;
             default:
                 TINT_ASSERT(Resolver, false && "unhandled severity");
@@ -455,7 +455,7 @@
         // Look at which nodes are reachable from "RequiredToBeUniform".
         {
             utils::UniqueVector<Node*, 4> reachable;
-            auto traverse = [&](ast::DiagnosticSeverity severity) {
+            auto traverse = [&](builtin::DiagnosticSeverity severity) {
                 Traverse(current_function_->RequiredToBeUniform(severity), &reachable);
                 if (reachable.Contains(current_function_->may_be_non_uniform)) {
                     MakeError(*current_function_, current_function_->may_be_non_uniform, severity);
@@ -478,11 +478,11 @@
                 }
                 return true;
             };
-            if (!traverse(ast::DiagnosticSeverity::kError)) {
+            if (!traverse(builtin::DiagnosticSeverity::kError)) {
                 return false;
             } else {
-                if (traverse(ast::DiagnosticSeverity::kWarning)) {
-                    traverse(ast::DiagnosticSeverity::kInfo);
+                if (traverse(builtin::DiagnosticSeverity::kWarning)) {
+                    traverse(builtin::DiagnosticSeverity::kInfo);
                 }
             }
         }
@@ -1497,8 +1497,8 @@
         result->type = Node::kFunctionCallReturnValue;
         Node* cf_after = CreateNode({"CF_after_", name}, call);
 
-        auto default_severity = kUniformityFailuresAsError ? ast::DiagnosticSeverity::kError
-                                                           : ast::DiagnosticSeverity::kWarning;
+        auto default_severity = kUniformityFailuresAsError ? builtin::DiagnosticSeverity::kError
+                                                           : builtin::DiagnosticSeverity::kWarning;
 
         // Get tags for the callee.
         CallSiteTag callsite_tag = {CallSiteTag::CallSiteNoRestriction};
@@ -1519,9 +1519,9 @@
                            builtin->Type() == sem::BuiltinType::kTextureSampleBias ||
                            builtin->Type() == sem::BuiltinType::kTextureSampleCompare) {
                     // Get the severity of derivative uniformity violations in this context.
-                    auto severity =
-                        sem_.DiagnosticSeverity(call, ast::DiagnosticRule::kDerivativeUniformity);
-                    if (severity != ast::DiagnosticSeverity::kOff) {
+                    auto severity = sem_.DiagnosticSeverity(
+                        call, builtin::DiagnosticRule::kDerivativeUniformity);
+                    if (severity != builtin::DiagnosticSeverity::kOff) {
                         callsite_tag = {CallSiteTag::CallSiteRequiredToBeUniform, severity};
                     }
                     function_tag = ReturnValueMayBeNonUniform;
@@ -1686,8 +1686,9 @@
 
     /// Recursively descend through the function called by `call` and the functions that it calls in
     /// order to find a call to a builtin function that requires uniformity with the given severity.
-    const ast::CallExpression* FindBuiltinThatRequiresUniformity(const ast::CallExpression* call,
-                                                                 ast::DiagnosticSeverity severity) {
+    const ast::CallExpression* FindBuiltinThatRequiresUniformity(
+        const ast::CallExpression* call,
+        builtin::DiagnosticSeverity severity) {
         auto* target = SemCall(call)->Target();
         if (target->Is<sem::Builtin>()) {
             // This is a call to a builtin, so we must be done.
@@ -1848,11 +1849,13 @@
     /// @param function the function that the diagnostic is being produced for
     /// @param source_node the node that has caused a uniformity issue in `function`
     /// @param severity the severity of the diagnostic
-    void MakeError(FunctionInfo& function, Node* source_node, ast::DiagnosticSeverity severity) {
+    void MakeError(FunctionInfo& function,
+                   Node* source_node,
+                   builtin::DiagnosticSeverity severity) {
         // Helper to produce a diagnostic message, as a note or with the global failure severity.
         auto report = [&](Source source, std::string msg, bool note) {
             diag::Diagnostic error{};
-            error.severity = note ? diag::Severity::Note : ast::ToSeverity(severity);
+            error.severity = note ? diag::Severity::Note : builtin::ToSeverity(severity);
             error.system = diag::System::Resolver;
             error.source = source;
             error.message = msg;
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index 26154a7..5718d02 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -7871,16 +7871,16 @@
 
 class UniformityAnalysisDiagnosticFilterTest
     : public UniformityAnalysisTestBase,
-      public ::testing::TestWithParam<ast::DiagnosticSeverity> {
+      public ::testing::TestWithParam<builtin::DiagnosticSeverity> {
   protected:
     // TODO(jrprice): Remove this in favour of utils::ToString() when we change "note" to "info".
-    const char* ToStr(ast::DiagnosticSeverity severity) {
+    const char* ToStr(builtin::DiagnosticSeverity severity) {
         switch (severity) {
-            case ast::DiagnosticSeverity::kError:
+            case builtin::DiagnosticSeverity::kError:
                 return "error";
-            case ast::DiagnosticSeverity::kWarning:
+            case builtin::DiagnosticSeverity::kWarning:
                 return "warning";
-            case ast::DiagnosticSeverity::kInfo:
+            case builtin::DiagnosticSeverity::kInfo:
                 return "note";
             default:
                 return "<undefined>";
@@ -7904,9 +7904,9 @@
 }
 )";
 
-    RunTest(ss.str(), param != ast::DiagnosticSeverity::kError);
+    RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
 
-    if (param == ast::DiagnosticSeverity::kOff) {
+    if (param == builtin::DiagnosticSeverity::kOff) {
         EXPECT_TRUE(error_.empty());
     } else {
         std::ostringstream err;
@@ -7932,8 +7932,8 @@
 }
 )";
 
-    RunTest(ss.str(), param != ast::DiagnosticSeverity::kError);
-    if (param == ast::DiagnosticSeverity::kOff) {
+    RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+    if (param == builtin::DiagnosticSeverity::kOff) {
         EXPECT_TRUE(error_.empty());
     } else {
         std::ostringstream err;
@@ -7958,8 +7958,8 @@
 }
 )";
 
-    RunTest(ss.str(), param != ast::DiagnosticSeverity::kError);
-    if (param == ast::DiagnosticSeverity::kOff) {
+    RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+    if (param == builtin::DiagnosticSeverity::kOff) {
         EXPECT_TRUE(error_.empty());
     } else {
         std::ostringstream err;
@@ -7970,10 +7970,10 @@
 
 INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
                          UniformityAnalysisDiagnosticFilterTest,
-                         ::testing::Values(ast::DiagnosticSeverity::kError,
-                                           ast::DiagnosticSeverity::kWarning,
-                                           ast::DiagnosticSeverity::kInfo,
-                                           ast::DiagnosticSeverity::kOff));
+                         ::testing::Values(builtin::DiagnosticSeverity::kError,
+                                           builtin::DiagnosticSeverity::kWarning,
+                                           builtin::DiagnosticSeverity::kInfo,
+                                           builtin::DiagnosticSeverity::kOff));
 
 TEST_F(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction_CalledByAnotherFunction) {
     std::string src = R"(
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 9577d59..2ef7d1e 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -159,10 +159,10 @@
       atomic_composite_info_(atomic_composite_info),
       valid_type_storage_layouts_(valid_type_storage_layouts) {
     // Set default severities for filterable diagnostic rules.
-    diagnostic_filters_.Set(ast::DiagnosticRule::kDerivativeUniformity,
-                            ast::DiagnosticSeverity::kError);
-    diagnostic_filters_.Set(ast::DiagnosticRule::kChromiumUnreachableCode,
-                            ast::DiagnosticSeverity::kWarning);
+    diagnostic_filters_.Set(builtin::DiagnosticRule::kDerivativeUniformity,
+                            builtin::DiagnosticSeverity::kError);
+    diagnostic_filters_.Set(builtin::DiagnosticRule::kChromiumUnreachableCode,
+                            builtin::DiagnosticSeverity::kWarning);
 }
 
 Validator::~Validator() = default;
@@ -179,18 +179,18 @@
     diagnostics_.add_note(diag::System::Resolver, msg, source);
 }
 
-bool Validator::AddDiagnostic(ast::DiagnosticRule rule,
+bool Validator::AddDiagnostic(builtin::DiagnosticRule rule,
                               const std::string& msg,
                               const Source& source) const {
     auto severity = diagnostic_filters_.Get(rule);
-    if (severity != ast::DiagnosticSeverity::kOff) {
+    if (severity != builtin::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) {
+        if (severity == builtin::DiagnosticSeverity::kError) {
             return false;
         }
     }
@@ -1375,8 +1375,8 @@
 bool Validator::Statements(utils::VectorRef<const ast::Statement*> stmts) const {
     for (auto* stmt : stmts) {
         if (!sem_.Get(stmt)->IsReachable()) {
-            if (!AddDiagnostic(ast::DiagnosticRule::kChromiumUnreachableCode, "code is unreachable",
-                               stmt->source)) {
+            if (!AddDiagnostic(builtin::DiagnosticRule::kChromiumUnreachableCode,
+                               "code is unreachable", stmt->source)) {
                 return false;
             }
             break;
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 6757c06..3128ae1 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -86,7 +86,7 @@
 };
 
 /// DiagnosticFilterStack is a scoped stack of diagnostic filters.
-using DiagnosticFilterStack = ScopeStack<ast::DiagnosticRule, ast::DiagnosticSeverity>;
+using DiagnosticFilterStack = ScopeStack<builtin::DiagnosticRule, builtin::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
@@ -127,7 +127,7 @@
     /// @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,
+    bool AddDiagnostic(builtin::DiagnosticRule rule,
                        const std::string& msg,
                        const Source& source) const;
 
diff --git a/src/tint/sem/diagnostic_severity_test.cc b/src/tint/sem/diagnostic_severity_test.cc
index f91277b..fa57b55 100644
--- a/src/tint/sem/diagnostic_severity_test.cc
+++ b/src/tint/sem/diagnostic_severity_test.cc
@@ -27,7 +27,7 @@
     /// 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) {
+    void Run(builtin::DiagnosticSeverity global_severity) {
         // @diagnostic(off, chromium_unreachable_code)
         // fn foo() {
         //   @diagnostic(info, chromium_unreachable_code) {
@@ -44,10 +44,10 @@
         //     }
         //   }
         // }
-        auto rule = ast::DiagnosticRule::kChromiumUnreachableCode;
-        auto func_severity = ast::DiagnosticSeverity::kOff;
-        auto block_severity = ast::DiagnosticSeverity::kInfo;
-        auto if_severity = ast::DiagnosticSeverity::kInfo;
+        auto rule = builtin::DiagnosticRule::kChromiumUnreachableCode;
+        auto func_severity = builtin::DiagnosticSeverity::kOff;
+        auto block_severity = builtin::DiagnosticSeverity::kInfo;
+        auto if_severity = builtin::DiagnosticSeverity::kInfo;
         auto attr = [&](auto severity) {
             return utils::Vector{DiagnosticAttribute(severity, "chromium_unreachable_code")};
         };
@@ -74,12 +74,12 @@
 };
 
 TEST_F(DiagnosticSeverityTest, WithDirective) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError, "chromium_unreachable_code");
-    Run(ast::DiagnosticSeverity::kError);
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError, "chromium_unreachable_code");
+    Run(builtin::DiagnosticSeverity::kError);
 }
 
 TEST_F(DiagnosticSeverityTest, WithoutDirective) {
-    Run(ast::DiagnosticSeverity::kWarning);
+    Run(builtin::DiagnosticSeverity::kWarning);
 }
 
 }  // namespace
diff --git a/src/tint/sem/function.h b/src/tint/sem/function.h
index 25bacda..528ebef 100644
--- a/src/tint/sem/function.h
+++ b/src/tint/sem/function.h
@@ -260,12 +260,12 @@
     /// 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) {
+    void SetDiagnosticSeverity(builtin::DiagnosticRule rule, builtin::DiagnosticSeverity severity) {
         diagnostic_severities_[rule] = severity;
     }
 
     /// @returns the diagnostic severity modifications applied to this function
-    const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
+    const builtin::DiagnosticRuleSeverities& DiagnosticSeverities() const {
         return diagnostic_severities_;
     }
 
@@ -289,7 +289,7 @@
     std::vector<const Function*> ancestor_entry_points_;
     const Statement* discard_stmt_ = nullptr;
     sem::Behaviors behaviors_{sem::Behavior::kNext};
-    ast::DiagnosticRuleSeverities diagnostic_severities_;
+    builtin::DiagnosticRuleSeverities diagnostic_severities_;
 
     std::optional<uint32_t> return_location_;
 };
diff --git a/src/tint/sem/info.cc b/src/tint/sem/info.cc
index a6a563e..e124c1c 100644
--- a/src/tint/sem/info.cc
+++ b/src/tint/sem/info.cc
@@ -29,8 +29,8 @@
 
 Info& Info::operator=(Info&&) = default;
 
-ast::DiagnosticSeverity Info::DiagnosticSeverity(const ast::Node* ast_node,
-                                                 ast::DiagnosticRule rule) const {
+builtin::DiagnosticSeverity Info::DiagnosticSeverity(const ast::Node* ast_node,
+                                                     builtin::DiagnosticRule rule) const {
     // Get the diagnostic severity modification for a node.
     auto check = [&](auto* node) {
         auto& severities = node->DiagnosticSeverities();
@@ -38,13 +38,13 @@
         if (itr != severities.end()) {
             return itr->second;
         }
-        return ast::DiagnosticSeverity::kUndefined;
+        return builtin::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) {
+        if (severity != builtin::DiagnosticSeverity::kUndefined) {
             return severity;
         }
 
@@ -57,7 +57,7 @@
         // Walk up the statement hierarchy, checking for diagnostic severity modifications.
         while (true) {
             auto severity = check(stmt);
-            if (severity != ast::DiagnosticSeverity::kUndefined) {
+            if (severity != builtin::DiagnosticSeverity::kUndefined) {
                 return severity;
             }
             if (!stmt->Parent()) {
@@ -82,7 +82,7 @@
             // Use the global severity set on the module.
             return check(module_);
         });
-    TINT_ASSERT(Resolver, severity != ast::DiagnosticSeverity::kUndefined);
+    TINT_ASSERT(Resolver, severity != builtin::DiagnosticSeverity::kUndefined);
     return severity;
 }
 
diff --git a/src/tint/sem/info.h b/src/tint/sem/info.h
index 2ce46f5..29b3bc4 100644
--- a/src/tint/sem/info.h
+++ b/src/tint/sem/info.h
@@ -161,8 +161,8 @@
     /// @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;
+    builtin::DiagnosticSeverity DiagnosticSeverity(const ast::Node* ast_node,
+                                                   builtin::DiagnosticRule rule) const;
 
   private:
     // AST node index to semantic node
diff --git a/src/tint/sem/module.h b/src/tint/sem/module.h
index 6bcd802..ba4e0a9 100644
--- a/src/tint/sem/module.h
+++ b/src/tint/sem/module.h
@@ -50,19 +50,19 @@
     /// 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) {
+    void SetDiagnosticSeverity(builtin::DiagnosticRule rule, builtin::DiagnosticSeverity severity) {
         diagnostic_severities_[rule] = severity;
     }
 
     /// @returns the diagnostic severity modifications applied to this module
-    const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
+    const builtin::DiagnosticRuleSeverities& DiagnosticSeverities() const {
         return diagnostic_severities_;
     }
 
   private:
     const utils::Vector<const ast::Node*, 64> dep_ordered_decls_;
     builtin::Extensions extensions_;
-    ast::DiagnosticRuleSeverities diagnostic_severities_;
+    builtin::DiagnosticRuleSeverities diagnostic_severities_;
 };
 
 }  // namespace tint::sem
diff --git a/src/tint/sem/statement.h b/src/tint/sem/statement.h
index 2d17295..373b9f6 100644
--- a/src/tint/sem/statement.h
+++ b/src/tint/sem/statement.h
@@ -113,12 +113,12 @@
     /// 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) {
+    void SetDiagnosticSeverity(builtin::DiagnosticRule rule, builtin::DiagnosticSeverity severity) {
         diagnostic_severities_[rule] = severity;
     }
 
     /// @returns the diagnostic severity modifications applied to this statement
-    const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
+    const builtin::DiagnosticRuleSeverities& DiagnosticSeverities() const {
         return diagnostic_severities_;
     }
 
@@ -128,7 +128,7 @@
     const sem::Function* const function_;
     sem::Behaviors behaviors_{sem::Behavior::kNext};
     bool is_reachable_ = true;
-    ast::DiagnosticRuleSeverities diagnostic_severities_;
+    builtin::DiagnosticRuleSeverities diagnostic_severities_;
 };
 
 /// CompoundStatement is the base class of statements that can hold other
diff --git a/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc b/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
index b996312..301bff4 100644
--- a/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
@@ -20,7 +20,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_DiagnosticDirective) {
-    DiagnosticDirective(ast::DiagnosticSeverity::kError, "chromium_unreachable_code");
+    DiagnosticDirective(builtin::DiagnosticSeverity::kError, "chromium_unreachable_code");
 
     GeneratorImpl& gen = Build();
 
@@ -31,7 +31,8 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_DiagnosticAttribute) {
-    auto* attr = DiagnosticAttribute(ast::DiagnosticSeverity::kError, "chromium_unreachable_code");
+    auto* attr =
+        DiagnosticAttribute(builtin::DiagnosticSeverity::kError, "chromium_unreachable_code");
     Func("foo", {}, ty.void_(), {}, utils::Vector{attr});
 
     GeneratorImpl& gen = Build();