Import Tint changes from Dawn

Changes:
  - 95cd216962b6d70e233b725f70f2a1bac8d112c1 [tint][wgsl] Polish struct address space diagnostic messa... by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 95cd216962b6d70e233b725f70f2a1bac8d112c1
Change-Id: I92b345d6503bb39e8a5fc07613ec1ecde5f49619
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/182080
Commit-Queue: dan sinclair <dsinclair@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/tint/cmd/info/main.cc b/src/tint/cmd/info/main.cc
index a68a46b..26f6df2 100644
--- a/src/tint/cmd/info/main.cc
+++ b/src/tint/cmd/info/main.cc
@@ -28,6 +28,8 @@
 
 #include <iostream>
 
+#include "src/tint/utils/text/styled_text_printer.h"
+
 #if TINT_BUILD_SPV_READER
 #include "spirv-tools/libspirv.hpp"
 #endif  // TINT_BUILD_SPV_READER
@@ -293,6 +295,7 @@
 }
 
 void EmitText(const tint::Program& program) {
+    auto printer = tint::StyledTextPrinter::Create(stdout);
     tint::inspector::Inspector inspector(program);
     if (!inspector.GetUsedExtensionNames().empty()) {
         std::cout << "Extensions:" << std::endl;
@@ -320,7 +323,7 @@
                 continue;
             }
             const auto* s = ty->As<tint::core::type::Struct>();
-            std::cout << s->Layout() << std::endl << std::endl;
+            printer->Print(s->Layout() << "\n\n");
         }
     }
 }
diff --git a/src/tint/lang/core/type/struct.cc b/src/tint/lang/core/type/struct.cc
index b26042a..f403c1e 100644
--- a/src/tint/lang/core/type/struct.cc
+++ b/src/tint/lang/core/type/struct.cc
@@ -30,12 +30,15 @@
 #include <cmath>
 #include <iomanip>
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include "src/tint/lang/core/type/manager.h"
 #include "src/tint/utils/math/hash.h"
 #include "src/tint/utils/symbol/symbol_table.h"
 #include "src/tint/utils/text/string_stream.h"
+#include "src/tint/utils/text/styled_text.h"
+#include "src/tint/utils/text/text_style.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::core::type::Struct);
 TINT_INSTANTIATE_TYPEINFO(tint::core::type::StructMember);
@@ -115,8 +118,15 @@
     return name_.Name();
 }
 
-std::string Struct::Layout() const {
-    StringStream ss;
+StyledText Struct::Layout() const {
+    static constexpr auto Code = style::CodeNoQuote;
+    static constexpr auto Comment = style::Comment + style::CodeNoQuote;
+    static constexpr auto Keyword = style::Keyword + style::CodeNoQuote;
+    static constexpr auto Type = style::Type + style::CodeNoQuote;
+    static constexpr auto Variable = style::Variable + style::CodeNoQuote;
+    static constexpr auto Plain = style::Plain;
+
+    StyledText out;
 
     auto member_name_of = [&](const StructMember* sm) { return sm->Name().Name(); };
 
@@ -131,20 +141,23 @@
     const auto size_w = static_cast<int>(::log10(Size())) + 1;
     const auto align_w = static_cast<int>(::log10(Align())) + 1;
 
+    auto pad = [](int n) { return std::string(static_cast<size_t>(n), ' '); };
+
     auto print_struct_begin_line = [&](size_t align, size_t size, std::string struct_name) {
-        ss << "/*          " << std::setw(offset_w) << " "
-           << "align(" << std::setw(align_w) << align << ") size(" << std::setw(size_w) << size
-           << ") */ struct " << struct_name << " {\n";
+        out << Comment("/*          ", pad(offset_w), "align(", std::setw(align_w), align,
+                       ") size(", std::setw(size_w), size, ") */")
+            << Keyword(" struct ") << Type(struct_name) << Code(" {") << Plain("\n");
     };
 
     auto print_struct_end_line = [&] {
-        ss << "/*                         " << std::setw(offset_w + size_w + align_w) << " "
-           << "*/ };";
+        out << Comment("/*                         ", pad(offset_w + size_w + align_w), "*/")
+            << Code(" };");
     };
 
-    auto print_member_line = [&](size_t offset, size_t align, size_t size, std::string s) {
-        ss << "/* offset(" << std::setw(offset_w) << offset << ") align(" << std::setw(align_w)
-           << align << ") size(" << std::setw(size_w) << size << ") */   " << s << ";\n";
+    auto print_member_line = [&](size_t offset, size_t align, size_t size, StyledText s) {
+        out << Comment("/* offset(", std::setw(offset_w), offset, ") align(", std::setw(align_w),
+                       align, ") size(", std::setw(size_w), size, ") */ ")
+            << s << Plain("\n");
     };
 
     print_struct_begin_line(Align(), Size(), UnwrapRef()->FriendlyName());
@@ -159,26 +172,31 @@
             if (padding > 0) {
                 size_t padding_offset = m->Offset() - padding;
                 print_member_line(padding_offset, 1, padding,
-                                  "// -- implicit field alignment padding --");
+                                  StyledText{}
+                                      << Code("  ")
+                                      << Comment("// -- implicit field alignment padding --"));
             }
         }
 
         // Output member
         std::string member_name = member_name_of(m);
         print_member_line(m->Offset(), m->Align(), m->Size(),
-                          member_name + " : " + m->Type()->UnwrapRef()->FriendlyName());
+                          StyledText{} << Code("  ") << Variable(member_name) << Code(" : ")
+                                       << Type(m->Type()->UnwrapRef()->FriendlyName())
+                                       << Code(","));
     }
 
     // Output struct size padding, if any
     uint32_t struct_padding = Size() - last_member_struct_padding_offset;
     if (struct_padding > 0) {
-        print_member_line(last_member_struct_padding_offset, 1, struct_padding,
-                          "// -- implicit struct size padding --");
+        print_member_line(
+            last_member_struct_padding_offset, 1, struct_padding,
+            StyledText{} << Code("  ") << Comment("// -- implicit struct size padding --"));
     }
 
     print_struct_end_line();
 
-    return ss.str();
+    return out;
 }
 
 TypeAndCount Struct::Elements(const Type* type_if_invalid /* = nullptr */,
diff --git a/src/tint/lang/core/type/struct.h b/src/tint/lang/core/type/struct.h
index 1ea2b0b..d95c084 100644
--- a/src/tint/lang/core/type/struct.h
+++ b/src/tint/lang/core/type/struct.h
@@ -41,6 +41,7 @@
 #include "src/tint/utils/containers/hashset.h"
 #include "src/tint/utils/containers/vector.h"
 #include "src/tint/utils/symbol/symbol.h"
+#include "src/tint/utils/text/styled_text.h"
 
 // Forward declarations
 namespace tint::core::type {
@@ -169,7 +170,7 @@
 
     /// @returns a multiline string that describes the layout of this struct,
     /// including size and alignment information.
-    std::string Layout() const;
+    StyledText Layout() const;
 
     /// @param concrete the conversion-rank ordered concrete versions of this abstract structure.
     void SetConcreteTypes(VectorRef<const Struct*> concrete) { concrete_types_ = concrete; }
diff --git a/src/tint/lang/core/type/struct_test.cc b/src/tint/lang/core/type/struct_test.cc
index b545645..e47ab66 100644
--- a/src/tint/lang/core/type/struct_test.cc
+++ b/src/tint/lang/core/type/struct_test.cc
@@ -83,22 +83,22 @@
     auto* sem_inner_st = p.Sem().Get(inner_st);
     auto* sem_outer_st = p.Sem().Get(outer_st);
 
-    EXPECT_EQ(sem_inner_st->Layout(),
+    EXPECT_EQ(sem_inner_st->Layout().Plain(),
               R"(/*            align(16) size(64) */ struct Inner {
-/* offset( 0) align( 4) size( 4) */   a : i32;
-/* offset( 4) align( 4) size( 4) */   b : u32;
-/* offset( 8) align( 4) size( 4) */   c : f32;
-/* offset(12) align( 1) size( 4) */   // -- implicit field alignment padding --;
-/* offset(16) align(16) size(12) */   d : vec3<f32>;
-/* offset(28) align( 1) size( 4) */   // -- implicit field alignment padding --;
-/* offset(32) align( 8) size(32) */   e : mat4x2<f32>;
+/* offset( 0) align( 4) size( 4) */   a : i32,
+/* offset( 4) align( 4) size( 4) */   b : u32,
+/* offset( 8) align( 4) size( 4) */   c : f32,
+/* offset(12) align( 1) size( 4) */   // -- implicit field alignment padding --
+/* offset(16) align(16) size(12) */   d : vec3<f32>,
+/* offset(28) align( 1) size( 4) */   // -- implicit field alignment padding --
+/* offset(32) align( 8) size(32) */   e : mat4x2<f32>,
 /*                               */ };)");
 
-    EXPECT_EQ(sem_outer_st->Layout(),
+    EXPECT_EQ(sem_outer_st->Layout().Plain(),
               R"(/*            align(16) size(80) */ struct Outer {
-/* offset( 0) align(16) size(64) */   inner : Inner;
-/* offset(64) align( 4) size( 4) */   a : i32;
-/* offset(68) align( 1) size(12) */   // -- implicit struct size padding --;
+/* offset( 0) align(16) size(64) */   inner : Inner,
+/* offset(64) align( 4) size( 4) */   a : i32,
+/* offset(68) align( 1) size(12) */   // -- implicit struct size padding --
 /*                               */ };)");
 }
 
diff --git a/src/tint/lang/wgsl/resolver/address_space_layout_validation_test.cc b/src/tint/lang/wgsl/resolver/address_space_layout_validation_test.cc
index 4fe832a..96b1340 100644
--- a/src/tint/lang/wgsl/resolver/address_space_layout_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/address_space_layout_validation_test.cc
@@ -47,7 +47,7 @@
     // @group(0) @binding(0)
     // var<storage> a : S;
 
-    Structure(Source{{12, 34}}, "S",
+    Structure(Ident(Source{{12, 34}}, "S"),
               Vector{
                   Member("a", ty.f32(), Vector{MemberSize(5_a)}),
                   Member(Source{{34, 56}}, "b", ty.f32(), Vector{MemberAlign(1_i)}),
@@ -62,9 +62,9 @@
         R"(34:56 error: the offset of a struct member of type 'f32' in address space 'storage' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting '@align(4)' on this member
 12:34 note: see layout of struct:
 /*           align(4) size(12) */ struct S {
-/* offset(0) align(4) size( 5) */   a : f32;
-/* offset(5) align(1) size( 4) */   b : f32;
-/* offset(9) align(1) size( 3) */   // -- implicit struct size padding --;
+/* offset(0) align(4) size( 5) */   a : f32,
+/* offset(5) align(1) size( 4) */   b : f32,
+/* offset(9) align(1) size( 3) */   // -- implicit struct size padding --
 /*                             */ };
 78:90 note: 'S' used in address space 'storage' here)");
 }
@@ -77,14 +77,12 @@
     // @group(0) @binding(0)
     // var<storage> a : S;
 
-    Structure(Source{{12, 34}}, "S",
-              Vector{
-                  Member("a", ty.f32(), Vector{MemberSize(5_a)}),
-                  Member(Source{{34, 56}}, "b", ty.f32(), Vector{MemberAlign(4_i)}),
-              });
+    Structure("S", Vector{
+                       Member("a", ty.f32(), Vector{MemberSize(5_a)}),
+                       Member("b", ty.f32(), Vector{MemberAlign(4_i)}),
+                   });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("S"), core::AddressSpace::kStorage, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("S"), core::AddressSpace::kStorage, Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -103,16 +101,14 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    Structure(Source{{12, 34}}, "Inner",
-              Vector{
-                  Member("scalar", ty.i32()),
-              });
+    Structure(Ident(Source{{12, 34}}, "Inner"), Vector{
+                                                    Member("scalar", ty.i32()),
+                                                });
 
-    Structure(Source{{34, 56}}, "Outer",
-              Vector{
-                  Member("scalar", ty.f32()),
-                  Member(Source{{56, 78}}, "inner", ty("Inner")),
-              });
+    Structure(Ident(Source{{34, 56}}, "Outer"), Vector{
+                                                    Member("scalar", ty.f32()),
+                                                    Member(Source{{56, 78}}, "inner", ty("Inner")),
+                                                });
 
     GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
@@ -123,12 +119,12 @@
         R"(56:78 error: the offset of a struct member of type 'Inner' in address space 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting '@align(16)' on this member
 34:56 note: see layout of struct:
 /*           align(4) size(8) */ struct Outer {
-/* offset(0) align(4) size(4) */   scalar : f32;
-/* offset(4) align(4) size(4) */   inner : Inner;
+/* offset(0) align(4) size(4) */   scalar : f32,
+/* offset(4) align(4) size(4) */   inner : Inner,
 /*                            */ };
 12:34 note: and layout of struct member:
 /*           align(4) size(4) */ struct Inner {
-/* offset(0) align(4) size(4) */   scalar : i32;
+/* offset(0) align(4) size(4) */   scalar : i32,
 /*                            */ };
 78:90 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -147,19 +143,16 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    Structure(Source{{12, 34}}, "Inner",
-              Vector{
-                  Member("scalar", ty.i32()),
-              });
+    Structure("Inner", Vector{
+                           Member("scalar", ty.i32()),
+                       });
 
-    Structure(Source{{34, 56}}, "Outer",
-              Vector{
-                  Member("scalar", ty.f32()),
-                  Member(Source{{56, 78}}, "inner", ty("Inner"), Vector{MemberAlign(16_i)}),
-              });
+    Structure("Outer", Vector{
+                           Member("scalar", ty.f32()),
+                           Member("inner", ty("Inner"), Vector{MemberAlign(16_i)}),
+                       });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -177,11 +170,10 @@
     // var<uniform> a : Outer;
     Alias("Inner", ty.array<f32, 10>(Vector{Stride(16)}));
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("scalar", ty.f32()),
-                  Member(Source{{56, 78}}, "inner", ty("Inner")),
-              });
+    Structure(Ident(Source{{12, 34}}, "Outer"), Vector{
+                                                    Member("scalar", ty.f32()),
+                                                    Member(Source{{56, 78}}, "inner", ty("Inner")),
+                                                });
 
     GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
@@ -192,8 +184,8 @@
         R"(56:78 error: the offset of a struct member of type '@stride(16) array<f32, 10>' in address space 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting '@align(16)' on this member
 12:34 note: see layout of struct:
 /*             align(4) size(164) */ struct Outer {
-/* offset(  0) align(4) size(  4) */   scalar : f32;
-/* offset(  4) align(4) size(160) */   inner : @stride(16) array<f32, 10>;
+/* offset(  0) align(4) size(  4) */   scalar : f32,
+/* offset(  4) align(4) size(160) */   inner : @stride(16) array<f32, 10>,
 /*                                */ };
 78:90 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -210,14 +202,12 @@
     // var<uniform> a : Outer;
     Alias("Inner", ty.array<f32, 10>(Vector{Stride(16)}));
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("scalar", ty.f32()),
-                  Member(Source{{34, 56}}, "inner", ty("Inner"), Vector{MemberAlign(16_i)}),
-              });
+    Structure("Outer", Vector{
+                           Member("scalar", ty.f32()),
+                           Member("inner", ty("Inner"), Vector{MemberAlign(16_i)}),
+                       });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -237,7 +227,7 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    Structure(Source{{12, 34}}, "Inner",
+    Structure(Ident(Source{{12, 34}}, "Inner"),
               Vector{
                   Member("scalar", ty.i32(), Vector{MemberAlign(1_i), MemberSize(5_a)}),
               });
@@ -255,15 +245,15 @@
     EXPECT_EQ(
         r()->error(),
         R"(78:90 error: 'uniform' storage requires that the number of bytes between the start of the previous member of type struct and the current member be a multiple of 16 bytes, but there are currently 8 bytes between 'inner' and 'scalar'. Consider setting '@align(16)' on this member
-34:56 note: see layout of struct:
+note: see layout of struct:
 /*            align(4) size(12) */ struct Outer {
-/* offset( 0) align(1) size( 5) */   inner : Inner;
-/* offset( 5) align(1) size( 3) */   // -- implicit field alignment padding --;
-/* offset( 8) align(4) size( 4) */   scalar : i32;
+/* offset( 0) align(1) size( 5) */   inner : Inner,
+/* offset( 5) align(1) size( 3) */   // -- implicit field alignment padding --
+/* offset( 8) align(4) size( 4) */   scalar : i32,
 /*                              */ };
 12:34 note: and layout of previous member struct:
 /*           align(1) size(5) */ struct Inner {
-/* offset(0) align(1) size(5) */   scalar : i32;
+/* offset(0) align(1) size(5) */   scalar : i32,
 /*                            */ };
 22:24 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -286,7 +276,7 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    Structure(Source{{12, 34}}, "Inner",
+    Structure(Ident(Source{{12, 34}}, "Inner"),
               Vector{
                   Member("a", ty.i32()),
                   Member("b", ty.i32()),
@@ -307,18 +297,18 @@
     EXPECT_EQ(
         r()->error(),
         R"(78:90 error: 'uniform' storage requires that the number of bytes between the start of the previous member of type struct and the current member be a multiple of 16 bytes, but there are currently 20 bytes between 'inner' and 'scalar'. Consider setting '@align(16)' on this member
-34:56 note: see layout of struct:
+note: see layout of struct:
 /*            align(4) size(24) */ struct Outer {
-/* offset( 0) align(4) size(20) */   inner : Inner;
-/* offset(20) align(4) size( 4) */   scalar : i32;
+/* offset( 0) align(4) size(20) */   inner : Inner,
+/* offset(20) align(4) size( 4) */   scalar : i32,
 /*                              */ };
 12:34 note: and layout of previous member struct:
 /*            align(4) size(20) */ struct Inner {
-/* offset( 0) align(4) size( 4) */   a : i32;
-/* offset( 4) align(4) size( 4) */   b : i32;
-/* offset( 8) align(4) size( 4) */   c : i32;
-/* offset(12) align(1) size( 5) */   scalar : i32;
-/* offset(17) align(1) size( 3) */   // -- implicit struct size padding --;
+/* offset( 0) align(4) size( 4) */   a : i32,
+/* offset( 4) align(4) size( 4) */   b : i32,
+/* offset( 8) align(4) size( 4) */   c : i32,
+/* offset(12) align(1) size( 5) */   scalar : i32,
+/* offset(17) align(1) size( 3) */   // -- implicit struct size padding --
 /*                              */ };
 22:24 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -337,19 +327,16 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    Structure(Source{{12, 34}}, "Inner",
-              Vector{
-                  Member("scalar", ty.i32(), Vector{MemberAlign(1_i), MemberSize(5_a)}),
-              });
+    Structure("Inner", Vector{
+                           Member("scalar", ty.i32(), Vector{MemberAlign(1_i), MemberSize(5_a)}),
+                       });
 
-    Structure(Source{{34, 56}}, "Outer",
-              Vector{
-                  Member(Source{{56, 78}}, "inner", ty("Inner")),
-                  Member(Source{{78, 90}}, "scalar", ty.i32(), Vector{MemberAlign(16_i)}),
-              });
+    Structure("Outer", Vector{
+                           Member("inner", ty("Inner")),
+                           Member("scalar", ty.i32(), Vector{MemberAlign(16_i)}),
+                       });
 
-    GlobalVar(Source{{22, 34}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -369,8 +356,8 @@
                                              Member("s", ty.f32()),
                                          });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("ScalarPackedAtEndOfVec3"), core::AddressSpace::kUniform,
-              Group(0_a), Binding(0_a));
+    GlobalVar("a", ty("ScalarPackedAtEndOfVec3"), core::AddressSpace::kUniform, Group(0_a),
+              Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -392,8 +379,8 @@
                                              Member("s", ty.f16()),
                                          });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("ScalarPackedAtEndOfVec3"), core::AddressSpace::kUniform,
-              Group(0_a), Binding(0_a));
+    GlobalVar("a", ty("ScalarPackedAtEndOfVec3"), core::AddressSpace::kUniform, Group(0_a),
+              Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -412,11 +399,10 @@
 
     Alias("Inner", ty.array<f32, 10>());
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("inner", ty(Source{{34, 56}}, "Inner")),
-                  Member("scalar", ty.i32()),
-              });
+    Structure(Ident(Source{{12, 34}}, "Outer"), Vector{
+                                                    Member("inner", ty(Source{{34, 56}}, "Inner")),
+                                                    Member("scalar", ty.i32()),
+                                                });
 
     GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
@@ -427,8 +413,8 @@
         R"(34:56 error: 'uniform' storage requires that array elements are aligned to 16 bytes, but array element of type 'f32' has a stride of 4 bytes. Consider using a vector or struct as the element type instead.
 12:34 note: see layout of struct:
 /*            align(4) size(44) */ struct Outer {
-/* offset( 0) align(4) size(40) */   inner : array<f32, 10>;
-/* offset(40) align(4) size( 4) */   scalar : i32;
+/* offset( 0) align(4) size(40) */   inner : array<f32, 10>,
+/* offset(40) align(4) size( 4) */   scalar : i32,
 /*                              */ };
 78:90 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -446,11 +432,10 @@
 
     Alias("Inner", ty.array<vec2<f32>, 10>());
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("inner", ty(Source{{34, 56}}, "Inner")),
-                  Member("scalar", ty.i32()),
-              });
+    Structure(Ident(Source{{12, 34}}, "Outer"), Vector{
+                                                    Member("inner", ty(Source{{34, 56}}, "Inner")),
+                                                    Member("scalar", ty.i32()),
+                                                });
 
     GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
@@ -461,9 +446,9 @@
         R"(34:56 error: 'uniform' storage requires that array elements are aligned to 16 bytes, but array element of type 'vec2<f32>' has a stride of 8 bytes. Consider using a vec4 instead.
 12:34 note: see layout of struct:
 /*            align(8) size(88) */ struct Outer {
-/* offset( 0) align(8) size(80) */   inner : array<vec2<f32>, 10>;
-/* offset(80) align(4) size( 4) */   scalar : i32;
-/* offset(84) align(1) size( 4) */   // -- implicit struct size padding --;
+/* offset( 0) align(8) size(80) */   inner : array<vec2<f32>, 10>,
+/* offset(80) align(4) size( 4) */   scalar : i32,
+/* offset(84) align(1) size( 4) */   // -- implicit struct size padding --
 /*                              */ };
 78:90 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -489,11 +474,10 @@
                                               });
     Alias("Inner", ty.array(ty.Of(array_elem), 10_u));
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("inner", ty(Source{{34, 56}}, "Inner")),
-                  Member("scalar", ty.i32()),
-              });
+    Structure(Ident(Source{{12, 34}}, "Outer"), Vector{
+                                                    Member("inner", ty(Source{{34, 56}}, "Inner")),
+                                                    Member("scalar", ty.i32()),
+                                                });
 
     GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
@@ -504,8 +488,8 @@
         R"(34:56 error: 'uniform' storage requires that array elements are aligned to 16 bytes, but array element of type 'ArrayElem' has a stride of 8 bytes. Consider using the '@size' attribute on the last struct member.
 12:34 note: see layout of struct:
 /*            align(4) size(84) */ struct Outer {
-/* offset( 0) align(4) size(80) */   inner : array<ArrayElem, 10>;
-/* offset(80) align(4) size( 4) */   scalar : i32;
+/* offset( 0) align(4) size(80) */   inner : array<ArrayElem, 10>,
+/* offset(80) align(4) size( 4) */   scalar : i32,
 /*                              */ };
 78:90 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -513,13 +497,12 @@
 TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
     // @group(0) @binding(0)
     // var<uniform> a : array<f32, 4u>;
-    GlobalVar(Source{{78, 90}}, "a", ty.array(Source{{34, 56}}, ty.f32(), 4_u),
-              core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
+    GlobalVar("a", ty.array(ty.f32(), 4_u), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(78:90 error: 'uniform' storage requires that array elements are aligned to 16 bytes, but array element of type 'f32' has a stride of 4 bytes. Consider using a vector or struct as the element type instead.)");
+        R"(error: 'uniform' storage requires that array elements are aligned to 16 bytes, but array element of type 'f32' has a stride of 4 bytes. Consider using a vector or struct as the element type instead.)");
 }
 
 TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_NestedArray) {
@@ -530,7 +513,7 @@
     // @group(0) @binding(0)
     // var<uniform> a : array<Outer, 4u>;
 
-    Structure(Source{{12, 34}}, "Outer",
+    Structure(Ident(Source{{12, 34}}, "Outer"),
               Vector{
                   Member("inner", ty.array(Source{{34, 56}}, ty.array<f32, 4>(), 4_u)),
               });
@@ -544,7 +527,7 @@
         R"(34:56 error: 'uniform' storage requires that array elements are aligned to 16 bytes, but array element of type 'f32' has a stride of 4 bytes. Consider using a vector or struct as the element type instead.
 12:34 note: see layout of struct:
 /*            align(4) size(64) */ struct Outer {
-/* offset( 0) align(4) size(64) */   inner : array<array<f32, 4>, 4>;
+/* offset( 0) align(4) size(64) */   inner : array<array<f32, 4>, 4>,
 /*                              */ };
 78:90 note: 'Outer' used in address space 'uniform' here)");
 }
@@ -562,14 +545,12 @@
 
     Alias("Inner", ty.array<f32, 10>(Vector{Stride(16)}));
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("inner", ty(Source{{34, 56}}, "Inner")),
-                  Member("scalar", ty.i32()),
-              });
+    Structure("Outer", Vector{
+                           Member("inner", ty("Inner")),
+                           Member("scalar", ty.i32()),
+                       });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -583,7 +564,7 @@
     // };
     // var<push_constant> a : S;
     Enable(wgsl::Extension::kChromiumExperimentalPushConstant);
-    Structure(Source{{12, 34}}, "S",
+    Structure(Ident(Source{{12, 34}}, "S"),
               Vector{Member("a", ty.f32(), Vector{MemberSize(5_a)}),
                      Member(Source{{34, 56}}, "b", ty.f32(), Vector{MemberAlign(1_i)})});
     GlobalVar(Source{{78, 90}}, "a", ty("S"), core::AddressSpace::kPushConstant);
@@ -594,9 +575,9 @@
         R"(34:56 error: the offset of a struct member of type 'f32' in address space 'push_constant' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting '@align(4)' on this member
 12:34 note: see layout of struct:
 /*           align(4) size(12) */ struct S {
-/* offset(0) align(4) size( 5) */   a : f32;
-/* offset(5) align(1) size( 4) */   b : f32;
-/* offset(9) align(1) size( 3) */   // -- implicit struct size padding --;
+/* offset(0) align(4) size( 5) */   a : f32,
+/* offset(5) align(1) size( 4) */   b : f32,
+/* offset(9) align(1) size( 3) */   // -- implicit struct size padding --
 /*                             */ };
 78:90 note: 'S' used in address space 'push_constant' here)");
 }
@@ -633,19 +614,16 @@
 
     Enable(wgsl::Extension::kChromiumInternalRelaxedUniformLayout);
 
-    Structure(Source{{12, 34}}, "Inner",
-              Vector{
-                  Member("scalar", ty.i32()),
-              });
+    Structure("Inner", Vector{
+                           Member("scalar", ty.i32()),
+                       });
 
-    Structure(Source{{34, 56}}, "Outer",
-              Vector{
-                  Member("scalar", ty.f32()),
-                  Member(Source{{56, 78}}, "inner", ty("Inner")),
-              });
+    Structure("Outer", Vector{
+                           Member("scalar", ty.f32()),
+                           Member("inner", ty("Inner")),
+                       });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -667,14 +645,12 @@
 
     Alias("Inner", ty.array<f32, 10>(Vector{Stride(16)}));
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("scalar", ty.f32()),
-                  Member(Source{{56, 78}}, "inner", ty("Inner")),
-              });
+    Structure("Outer", Vector{
+                           Member("scalar", ty.f32()),
+                           Member("inner", ty("Inner")),
+                       });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -696,19 +672,16 @@
 
     Enable(wgsl::Extension::kChromiumInternalRelaxedUniformLayout);
 
-    Structure(Source{{12, 34}}, "Inner",
-              Vector{
-                  Member("scalar", ty.i32(), Vector{MemberAlign(1_i), MemberSize(5_a)}),
-              });
+    Structure("Inner", Vector{
+                           Member("scalar", ty.i32(), Vector{MemberAlign(1_i), MemberSize(5_a)}),
+                       });
 
-    Structure(Source{{34, 56}}, "Outer",
-              Vector{
-                  Member(Source{{56, 78}}, "inner", ty("Inner")),
-                  Member(Source{{78, 90}}, "scalar", ty.i32()),
-              });
+    Structure("Outer", Vector{
+                           Member("inner", ty("Inner")),
+                           Member("scalar", ty.i32()),
+                       });
 
-    GlobalVar(Source{{22, 24}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -725,13 +698,11 @@
 
     Enable(wgsl::Extension::kChromiumInternalRelaxedUniformLayout);
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("arr", ty.array<f32, 10>()),
-              });
+    Structure("Outer", Vector{
+                           Member("arr", ty.array<f32, 10>()),
+                       });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -750,13 +721,11 @@
     Enable(wgsl::Extension::kF16);
     Enable(wgsl::Extension::kChromiumInternalRelaxedUniformLayout);
 
-    Structure(Source{{12, 34}}, "Outer",
-              Vector{
-                  Member("arr", ty.array<vec3<f16>, 10>()),
-              });
+    Structure("Outer", Vector{
+                           Member("arr", ty.array<vec3<f16>, 10>()),
+                       });
 
-    GlobalVar(Source{{78, 90}}, "a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a),
-              Binding(0_a));
+    GlobalVar("a", ty("Outer"), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/tint/lang/wgsl/resolver/address_space_validation_test.cc b/src/tint/lang/wgsl/resolver/address_space_validation_test.cc
index aacf005..185bbdb 100644
--- a/src/tint/lang/wgsl/resolver/address_space_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/address_space_validation_test.cc
@@ -560,7 +560,7 @@
         R"(12:34 error: 'uniform' storage requires that array elements are aligned to 16 bytes, but array element of type 'i32' has a stride of 4 bytes. Consider using a vector or struct as the element type instead.
 note: see layout of struct:
 /*           align(4) size(4) */ struct S {
-/* offset(0) align(4) size(4) */   m : array<i32>;
+/* offset(0) align(4) size(4) */   m : array<i32>,
 /*                            */ };
 90:12 note: 'S' used in address space 'uniform' here)");
 }
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index adc0134..a7a42ec 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -509,13 +509,14 @@
     }
 
     if (auto* str = store_ty->As<sem::Struct>()) {
+        auto& str_source = str->Declaration()->name->source;
         for (size_t i = 0; i < str->Members().Length(); ++i) {
             auto* const m = str->Members()[i];
             uint32_t required_align = required_alignment_of(m->Type());
 
             // Recurse into the member type.
             if (!AddressSpaceLayout(m->Type(), address_space, m->Declaration()->type->source)) {
-                AddNote(str->Declaration()->source) << "see layout of struct:\n" << str->Layout();
+                AddNote(str_source) << "see layout of struct:\n" << str->Layout();
                 note_usage();
                 return false;
             }
@@ -533,11 +534,12 @@
                     << style::Attribute("@align") << style::Code("(", required_align, ")")
                     << " on this member";
 
-                AddNote(str->Declaration()->source) << "see layout of struct:\n" << str->Layout();
+                AddNote(str_source) << "see layout of struct:\n" << str->Layout();
 
                 if (auto* member_str = m->Type()->As<sem::Struct>()) {
-                    AddNote(member_str->Declaration()->source) << "and layout of struct member:\n"
-                                                               << member_str->Layout();
+                    AddNote(member_str->Declaration()->name->source)
+                        << "and layout of struct member:\n"
+                        << member_str->Layout();
                 }
 
                 note_usage();
@@ -562,11 +564,10 @@
                         << style::Variable(member_name_of(m)) << ". Consider setting "
                         << style::Attribute("@align") << style::Code("(16)") << " on this member";
 
-                    AddNote(str->Declaration()->source) << "see layout of struct:\n"
-                                                        << str->Layout();
+                    AddNote(str_source) << "see layout of struct:\n" << str->Layout();
 
                     auto* prev_member_str = prev_member->Type()->As<sem::Struct>();
-                    AddNote(prev_member_str->Declaration()->source)
+                    AddNote(prev_member_str->Declaration()->name->source)
                         << "and layout of previous member struct:\n"
                         << prev_member_str->Layout();
                     note_usage();
@@ -2220,7 +2221,7 @@
 
 bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) const {
     if (str->Members().IsEmpty()) {
-        AddError(str->Declaration()->source) << "structures must have at least one member";
+        AddError(str->Declaration()->name->source) << "structures must have at least one member";
         return false;
     }
 
diff --git a/src/tint/utils/text/styled_text.cc b/src/tint/utils/text/styled_text.cc
index 76d33cf..e9290ae 100644
--- a/src/tint/utils/text/styled_text.cc
+++ b/src/tint/utils/text/styled_text.cc
@@ -72,16 +72,16 @@
 
 std::string StyledText::Plain() const {
     StringStream ss;
-    bool is_code = false;
+    bool is_code_no_quote = false;
     Walk([&](std::string_view text, TextStyle style) {
-        if (is_code != style.IsCode()) {
+        if (is_code_no_quote != (style.IsCode() && !style.IsCodeNoQuote())) {
             ss << "'";
         }
-        is_code = style.IsCode();
+        is_code_no_quote = style.IsCode() && !style.IsCodeNoQuote();
 
         ss << text;
     });
-    if (is_code) {
+    if (is_code_no_quote) {
         ss << "'";
     }
     return ss.str();
diff --git a/src/tint/utils/text/styled_text_theme.cc b/src/tint/utils/text/styled_text_theme.cc
index e243cd5..0785c47 100644
--- a/src/tint/utils/text/styled_text_theme.cc
+++ b/src/tint/utils/text/styled_text_theme.cc
@@ -128,6 +128,13 @@
         /* bold */ std::nullopt,
         /* underlined */ std::nullopt,
     },
+    /* kind_comment */
+    StyledTextTheme::Attributes{
+        /* foreground */ Color{106, 153, 85},
+        /* background */ std::nullopt,
+        /* bold */ std::nullopt,
+        /* underlined */ std::nullopt,
+    },
     /* kind_squiggle */
     StyledTextTheme::Attributes{
         /* foreground */ Color{0, 200, 255},
@@ -235,6 +242,13 @@
         /* bold */ std::nullopt,
         /* underlined */ std::nullopt,
     },
+    /* kind_comment */
+    StyledTextTheme::Attributes{
+        /* foreground */ Color{0, 128, 0},
+        /* background */ std::nullopt,
+        /* bold */ std::nullopt,
+        /* underlined */ std::nullopt,
+    },
     /* kind_squiggle */
     StyledTextTheme::Attributes{
         /* foreground */ Color{0, 200, 255},
@@ -294,6 +308,8 @@
                 apply(kind_literal);
             } else if (text_style.IsAttribute()) {
                 apply(kind_attribute);
+            } else if (text_style.IsComment()) {
+                apply(kind_comment);
             }
         }
         if (text_style.IsSquiggle()) {
diff --git a/src/tint/utils/text/styled_text_theme.h b/src/tint/utils/text/styled_text_theme.h
index c083dd1..bff0de7 100644
--- a/src/tint/utils/text/styled_text_theme.h
+++ b/src/tint/utils/text/styled_text_theme.h
@@ -103,6 +103,8 @@
     Attributes kind_literal;
     /// The theme's attributes for a attribute token. This is applied on top #kind_code.
     Attributes kind_attribute;
+    /// The theme's attributes for a comment token. This is applied on top #kind_code.
+    Attributes kind_comment;
 
     /// The theme's attributes for a squiggle-highlight.
     Attributes kind_squiggle;
diff --git a/src/tint/utils/text/text_style.h b/src/tint/utils/text/text_style.h
index a9ce9e1..c337a5a 100644
--- a/src/tint/utils/text/text_style.h
+++ b/src/tint/utils/text/text_style.h
@@ -53,68 +53,72 @@
 
     /// Bit patterns
 
-    static constexpr Bits kStyleMask /*          */ = 0b0000'0000'0000'0011;
-    static constexpr Bits kStyleUnderlined /*    */ = 0b0000'0000'0000'0001;
-    static constexpr Bits kStyleBold /*          */ = 0b0000'0000'0000'0010;
+    static constexpr Bits kStyleMask /*          */ = 0b00'000000'0000'00'11;
+    static constexpr Bits kStyleUnderlined /*    */ = 0b00'000000'0000'00'01;
+    static constexpr Bits kStyleBold /*          */ = 0b00'000000'0000'00'10;
 
-    static constexpr Bits kCompareMask /*        */ = 0b0000'0000'0000'1100;
-    static constexpr Bits kCompareMatch /*       */ = 0b0000'0000'0000'0100;
-    static constexpr Bits kCompareMismatch /*    */ = 0b0000'0000'0000'1000;
+    static constexpr Bits kCompareMask /*        */ = 0b00'000000'0000'11'00;
+    static constexpr Bits kCompareMatch /*       */ = 0b00'000000'0000'01'00;
+    static constexpr Bits kCompareMismatch /*    */ = 0b00'000000'0000'10'00;
 
-    static constexpr Bits kSeverityMask /*       */ = 0b0000'0000'1111'0000;
-    static constexpr Bits kSeverityDefault /*    */ = 0b0000'0000'0000'0000;
-    static constexpr Bits kSeveritySuccess /*    */ = 0b0000'0000'0001'0000;
-    static constexpr Bits kSeverityWarning /*    */ = 0b0000'0000'0010'0000;
-    static constexpr Bits kSeverityError /*      */ = 0b0000'0000'0011'0000;
-    static constexpr Bits kSeverityFatal /*      */ = 0b0000'0000'0100'0000;
+    static constexpr Bits kSeverityMask /*       */ = 0b00'000000'1111'00'00;
+    static constexpr Bits kSeverityDefault /*    */ = 0b00'000000'0000'00'00;
+    static constexpr Bits kSeveritySuccess /*    */ = 0b00'000000'0001'00'00;
+    static constexpr Bits kSeverityWarning /*    */ = 0b00'000000'0010'00'00;
+    static constexpr Bits kSeverityError /*      */ = 0b00'000000'0011'00'00;
+    static constexpr Bits kSeverityFatal /*      */ = 0b00'000000'0100'00'00;
 
-    static constexpr Bits kKindMask /*           */ = 0b0000'1111'0000'0000;
-    static constexpr Bits kKindCode /*           */ = 0b0000'0001'0000'0000;
-    static constexpr Bits kKindKeyword /*        */ = 0b0000'0011'0000'0000;
-    static constexpr Bits kKindVariable /*       */ = 0b0000'0101'0000'0000;
-    static constexpr Bits kKindType /*           */ = 0b0000'0111'0000'0000;
-    static constexpr Bits kKindFunction /*       */ = 0b0000'1001'0000'0000;
-    static constexpr Bits kKindEnum /*           */ = 0b0000'1011'0000'0000;
-    static constexpr Bits kKindLiteral /*        */ = 0b0000'1101'0000'0000;
-    static constexpr Bits kKindAttribute /*      */ = 0b0000'1111'0000'0000;
-    static constexpr Bits kKindSquiggle /*       */ = 0b0000'0010'0000'0000;
+    static constexpr Bits kKindMask /*           */ = 0b00'111111'0000'00'00;
+    static constexpr Bits kKindCode /*           */ = 0b00'000001'0000'00'00;
+    static constexpr Bits kKindCodeNoQuote /*    */ = 0b00'000011'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindKeyword /*        */ = 0b00'000101'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindVariable /*       */ = 0b00'001001'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindType /*           */ = 0b00'001101'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindFunction /*       */ = 0b00'010001'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindEnum /*           */ = 0b00'010101'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindLiteral /*        */ = 0b00'011001'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindAttribute /*      */ = 0b00'011101'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindComment /*        */ = 0b00'100001'0000'00'00;  // includes kKindCode
+    static constexpr Bits kKindSquiggle /*       */ = 0b00'000100'0000'00'00;
 
     /// Getters
 
-    bool IsBold() const { return (bits & kStyleBold) != 0; }
-    bool IsUnderlined() const { return (bits & kStyleUnderlined) != 0; }
+    constexpr bool IsBold() const { return (bits & kStyleBold) != 0; }
+    constexpr bool IsUnderlined() const { return (bits & kStyleUnderlined) == kStyleUnderlined; }
 
-    bool HasCompare() const { return (bits & kCompareMask) != 0; }
-    bool IsMatch() const { return (bits & kCompareMask) == kCompareMatch; }
-    bool IsMismatch() const { return (bits & kCompareMask) == kCompareMismatch; }
+    constexpr bool HasCompare() const { return (bits & kCompareMask) != 0; }
+    constexpr bool IsMatch() const { return (bits & kCompareMask) == kCompareMatch; }
+    constexpr bool IsMismatch() const { return (bits & kCompareMask) == kCompareMismatch; }
 
-    bool HasSeverity() const { return (bits & kSeverityMask) != 0; }
-    bool IsSuccess() const { return (bits & kSeverityMask) == kSeveritySuccess; }
-    bool IsWarning() const { return (bits & kSeverityMask) == kSeverityWarning; }
-    bool IsError() const { return (bits & kSeverityMask) == kSeverityError; }
-    bool IsFatal() const { return (bits & kSeverityMask) == kSeverityFatal; }
+    constexpr bool HasSeverity() const { return (bits & kSeverityMask) != 0; }
+    constexpr bool IsSuccess() const { return (bits & kSeverityMask) == kSeveritySuccess; }
+    constexpr bool IsWarning() const { return (bits & kSeverityMask) == kSeverityWarning; }
+    constexpr bool IsError() const { return (bits & kSeverityMask) == kSeverityError; }
+    constexpr bool IsFatal() const { return (bits & kSeverityMask) == kSeverityFatal; }
 
-    bool HasKind() const { return (bits & kKindMask) != 0; }
-    bool IsCode() const { return (bits & kKindCode) != 0; }
-    bool IsKeyword() const { return (bits & kKindMask) == kKindKeyword; }
-    bool IsVariable() const { return (bits & kKindMask) == kKindVariable; }
-    bool IsType() const { return (bits & kKindMask) == kKindType; }
-    bool IsFunction() const { return (bits & kKindMask) == kKindFunction; }
-    bool IsEnum() const { return (bits & kKindMask) == kKindEnum; }
-    bool IsLiteral() const { return (bits & kKindMask) == kKindLiteral; }
-    bool IsAttribute() const { return (bits & kKindMask) == kKindAttribute; }
-    bool IsSquiggle() const { return (bits & kKindMask) == kKindSquiggle; }
+    constexpr bool HasKind() const { return (bits & kKindMask) != 0; }
+    constexpr bool IsCode() const { return (bits & kKindCode) == kKindCode; }
+    constexpr bool IsCodeNoQuote() const { return (bits & kKindCodeNoQuote) == kKindCodeNoQuote; }
+    constexpr bool IsKeyword() const { return (bits & kKindMask) == kKindKeyword; }
+    constexpr bool IsVariable() const { return (bits & kKindMask) == kKindVariable; }
+    constexpr bool IsType() const { return (bits & kKindMask) == kKindType; }
+    constexpr bool IsFunction() const { return (bits & kKindMask) == kKindFunction; }
+    constexpr bool IsEnum() const { return (bits & kKindMask) == kKindEnum; }
+    constexpr bool IsLiteral() const { return (bits & kKindMask) == kKindLiteral; }
+    constexpr bool IsAttribute() const { return (bits & kKindMask) == kKindAttribute; }
+    constexpr bool IsComment() const { return (bits & kKindMask) == kKindComment; }
+    constexpr bool IsSquiggle() const { return (bits & kKindMask) == kKindSquiggle; }
 
     /// Equality operator
-    bool operator==(TextStyle other) const { return bits == other.bits; }
+    constexpr bool operator==(TextStyle other) const { return bits == other.bits; }
 
     /// Inequality operator
-    bool operator!=(TextStyle other) const { return bits != other.bits; }
+    constexpr bool operator!=(TextStyle other) const { return bits != other.bits; }
 
     /// @returns the combination of this TextStyle and @p other.
     /// If both this TextStyle and @p other have a compare style, severity style or kind style, then
     /// the style collision will resolve by using the style of @p other.
-    TextStyle operator+(TextStyle other) const {
+    constexpr TextStyle operator+(TextStyle other) const {
         Bits out = other.bits;
         out |= bits & kStyleMask;
         if (HasCompare() && !other.HasCompare()) {
@@ -184,6 +188,9 @@
 static constexpr TextStyle Fatal = TextStyle{TextStyle::kSeverityFatal};
 /// Code renders text with a 'code' style
 static constexpr TextStyle Code = TextStyle{TextStyle::kKindCode};
+/// Code renders text with a 'code' style, but does not use a single quote when printed as plain
+/// text.
+static constexpr TextStyle CodeNoQuote = TextStyle{TextStyle::kKindCodeNoQuote};
 /// Keyword renders text with a 'code' style that represents a 'keyword' token
 static constexpr TextStyle Keyword = TextStyle{TextStyle::kKindKeyword};
 /// Variable renders text with a 'code' style that represents a 'variable' token
@@ -198,6 +205,8 @@
 static constexpr TextStyle Literal = TextStyle{TextStyle::kKindLiteral};
 /// Attribute renders text with a 'code' style that represents an 'attribute' token
 static constexpr TextStyle Attribute = TextStyle{TextStyle::kKindAttribute};
+/// Comment renders text with a 'code' style that represents an 'comment' token
+static constexpr TextStyle Comment = TextStyle{TextStyle::kKindComment};
 /// Squiggle renders text with a squiggle-highlight style (`^^^^^`)
 static constexpr TextStyle Squiggle = TextStyle{TextStyle::kKindSquiggle};