Import Tint changes from Dawn

Changes:
  - c4cc6ec0381f0c4e62b0d5e2d3f72e267584e759 Remove the demangler. by dan sinclair <dsinclair@chromium.org>
  - 2200823afd79add9701ba456845763e32b4716a8 Allow building SPIRV_WRITER without READER by dan sinclair <dsinclair@chromium.org>
  - 541138b11b1c44608f0a0eb9bb27c742b01de8bb Cleanup SyntaxTree writer. by dan sinclair <dsinclair@chromium.org>
  - 27c772262048ecca2de30b6895d60ecc81acb991 Cleanup WGSL writer. by dan sinclair <dsinclair@chromium.org>
  - 8a435a2a4bfa5d2fc915440e928c756c443939ad Remove `error` from TextGenerator. by dan sinclair <dsinclair@chromium.org>
  - 585f395e66bcda4e82dd11e1c676418b692f4341 Simplify MultiplanarExternalTexture::ShouldRun. by dan sinclair <dsinclair@chromium.org>
  - 04d61c85cdf07c5709b1f65e791890786ca370dd Always run Multiplanar transform. by dan sinclair <dsinclair@chromium.org>
  - d146c8a7a2eda987b3140a3e7fa254710af53ad8 Enable multiplanar for SPIRV fuzzers. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: c4cc6ec0381f0c4e62b0d5e2d3f72e267584e759
Change-Id: I52e8f2987b9b20d48be36d3f3774da977c44eff4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/127000
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/include/tint/tint.h b/include/tint/tint.h
index 0bde567..847181b 100644
--- a/include/tint/tint.h
+++ b/include/tint/tint.h
@@ -21,7 +21,6 @@
 // TODO(tint:88): When implementing support for an install target, all of these
 //                headers will need to be moved to include/tint/.
 
-#include "src/tint/demangler.h"
 #include "src/tint/diagnostic/printer.h"
 #include "src/tint/inspector/inspector.h"
 #include "src/tint/reader/reader.h"
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index c42d1d1..1e084a2 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -307,17 +307,6 @@
   ]
 }
 
-libtint_source_set("libtint_demangler_src") {
-  sources = [
-    "demangler.cc",
-    "demangler.h",
-  ]
-  deps = [
-    ":libtint_base_src",
-    ":libtint_program_src",
-  ]
-}
-
 libtint_source_set("libtint_initializer_src") {
   sources = [ "tint.cc" ]
 }
@@ -1095,7 +1084,6 @@
     ":libtint_ast_src",
     ":libtint_base_src",
     ":libtint_constant_src",
-    ":libtint_demangler_src",
     ":libtint_initializer_src",
     ":libtint_inspector_src",
     ":libtint_program_src",
@@ -2019,11 +2007,6 @@
     ]
   }
 
-  tint_unittests_source_set("tint_unittests_demangler_src") {
-    sources = [ "demangler_test.cc" ]
-    deps = [ ":libtint_demangler_src" ]
-  }
-
   if (build_with_chromium) {
     tint_unittests_source_set("tint_unittests_fuzzer_src") {
       sources = [ "fuzzers/random_generator_test.cc" ]
@@ -2047,7 +2030,6 @@
       ":tint_unittests_cmd_src",
       ":tint_unittests_constant_src",
       ":tint_unittests_core_src",
-      ":tint_unittests_demangler_src",
       ":tint_unittests_diagnostic_src",
       ":tint_unittests_inspector_src",
       ":tint_unittests_resolver_src",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 87bdbbc..4dd1c88 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -245,8 +245,6 @@
   constant/node.h
   constant/value.cc
   constant/value.h
-  demangler.cc
-  demangler.h
   inspector/entry_point.cc
   inspector/entry_point.h
   inspector/inspector.cc
@@ -892,7 +890,6 @@
     constant/scalar_test.cc
     constant/splat_test.cc
     debug_test.cc
-    demangler_test.cc
     diagnostic/diagnostic_test.cc
     diagnostic/formatter_test.cc
     diagnostic/printer_test.cc
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index 45982f2..d61ae24 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -34,9 +34,9 @@
 
 #endif  // TINT_BUILD_SYNTAX_TREE_WRITER
 
-#if TINT_BUILD_SPV_READER
+#if TINT_BUILD_SPV_READER || TINT_BUILD_SPV_WRITER
 #include "spirv-tools/libspirv.hpp"
-#endif  // TINT_BUILD_SPV_READER
+#endif  // TINT_BUILD_SPV_READER || TINT_BUILD_SPV_WRITER
 
 #include "src/tint/ast/module.h"
 #include "src/tint/cmd/generate_external_texture_bindings.h"
@@ -83,7 +83,6 @@
     bool disable_workgroup_init = false;
     bool validate = false;
     bool print_hash = false;
-    bool demangle = false;
     bool dump_inspector_bindings = false;
     bool enable_robustness = false;
 
@@ -140,8 +139,6 @@
                                inserting a module-scope directive to suppress any uniformity
                                violations that may be produced.
   --disable-workgroup-init  -- Disable workgroup memory zero initialization.
-  --demangle                -- Preserve original source names. Demangle them.
-                               Affects AST dumping, and text-based output languages.
   --dump-inspector-bindings -- Dump reflection data about bindins to stdout.
   -h                        -- This help text
   --hlsl-root-constant-binding-point <group>,<binding>  -- Binding point for root constant.
@@ -349,8 +346,6 @@
 #endif
         } else if (arg == "--disable-workgroup-init") {
             opts->disable_workgroup_init = true;
-        } else if (arg == "--demangle") {
-            opts->demangle = true;
         } else if (arg == "--dump-inspector-bindings") {
             opts->dump_inspector_bindings = true;
         } else if (arg == "--validate") {
diff --git a/src/tint/demangler.cc b/src/tint/demangler.cc
deleted file mode 100644
index 146a31c..0000000
--- a/src/tint/demangler.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/demangler.h"
-
-#include "src/tint/program.h"
-#include "src/tint/utils/string_stream.h"
-
-namespace tint {
-namespace {
-
-constexpr char kSymbol[] = "$";
-constexpr size_t kSymbolLen = sizeof(kSymbol) - 1;
-
-}  // namespace
-
-Demangler::Demangler() = default;
-
-Demangler::~Demangler() = default;
-
-std::string Demangler::Demangle(const SymbolTable& symbols, const std::string& str) const {
-    utils::StringStream out;
-
-    size_t pos = 0;
-    for (;;) {
-        auto idx = str.find(kSymbol, pos);
-        if (idx == std::string::npos) {
-            out << str.substr(pos);
-            break;
-        }
-
-        out << str.substr(pos, idx - pos);
-
-        auto start_idx = idx + kSymbolLen;
-        auto end_idx = start_idx;
-        while (str[end_idx] >= '0' && str[end_idx] <= '9') {
-            end_idx++;
-        }
-        auto len = end_idx - start_idx;
-
-        auto id = str.substr(start_idx, len);
-        Symbol sym(static_cast<uint32_t>(std::stoi(id)), symbols.ProgramID());
-        out << symbols.NameFor(sym);
-
-        pos = end_idx;
-    }
-
-    return out.str();
-}
-
-}  // namespace tint
diff --git a/src/tint/demangler.h b/src/tint/demangler.h
deleted file mode 100644
index c187d48..0000000
--- a/src/tint/demangler.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_TINT_DEMANGLER_H_
-#define SRC_TINT_DEMANGLER_H_
-
-#include <string>
-
-namespace tint {
-
-class SymbolTable;
-
-/// Helper to demangle strings and replace symbols with original names
-class Demangler {
-  public:
-    /// Constructor
-    Demangler();
-    /// Destructor
-    ~Demangler();
-
-    /// Transforms given string and replaces any symbols with original names
-    /// @param symbols the symbol table
-    /// @param str the string to replace
-    /// @returns the string with any symbol replacements performed.
-    std::string Demangle(const SymbolTable& symbols, const std::string& str) const;
-};
-
-}  // namespace tint
-
-#endif  // SRC_TINT_DEMANGLER_H_
diff --git a/src/tint/demangler_test.cc b/src/tint/demangler_test.cc
deleted file mode 100644
index 8f7627a..0000000
--- a/src/tint/demangler_test.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/demangler.h"
-#include "src/tint/symbol_table.h"
-
-#include "gtest/gtest.h"
-
-namespace tint {
-namespace {
-
-using DemanglerTest = testing::Test;
-
-TEST_F(DemanglerTest, NoSymbols) {
-    SymbolTable t{ProgramID::New()};
-    t.Register("sym1");
-
-    Demangler d;
-    EXPECT_EQ("test str", d.Demangle(t, "test str"));
-}
-
-TEST_F(DemanglerTest, Symbol) {
-    SymbolTable t{ProgramID::New()};
-    t.Register("sym1");
-
-    Demangler d;
-    EXPECT_EQ("test sym1 str", d.Demangle(t, "test $1 str"));
-}
-
-TEST_F(DemanglerTest, MultipleSymbols) {
-    SymbolTable t{ProgramID::New()};
-    t.Register("sym1");
-    t.Register("sym2");
-
-    Demangler d;
-    EXPECT_EQ("test sym1 sym2 sym1 str", d.Demangle(t, "test $1 $2 $1 str"));
-}
-
-}  // namespace
-}  // namespace tint
diff --git a/src/tint/fuzzers/tint_common_fuzzer.cc b/src/tint/fuzzers/tint_common_fuzzer.cc
index 9813cfc..6be9b98 100644
--- a/src/tint/fuzzers/tint_common_fuzzer.cc
+++ b/src/tint/fuzzers/tint_common_fuzzer.cc
@@ -263,7 +263,8 @@
 
     // For the generates which use MultiPlanar, make sure the configuration options are provided so
     // that the transformer will execute.
-    if (output_ == OutputFormat::kMSL || output_ == OutputFormat::kHLSL) {
+    if (output_ == OutputFormat::kMSL || output_ == OutputFormat::kHLSL ||
+        output_ == OutputFormat::kSpv) {
         // Gather external texture binding information
         // Collect next valid binding number per group
         std::unordered_map<uint32_t, uint32_t> group_to_next_binding_number;
@@ -299,6 +300,10 @@
                 options_hlsl_.external_texture_options.bindings_map = new_bindings_map;
                 break;
             }
+            case OutputFormat::kSpv: {
+                options_spirv_.external_texture_options.bindings_map = new_bindings_map;
+                break;
+            }
             default:
                 break;
         }
diff --git a/src/tint/reader/spirv/parser_impl_test_helper.cc b/src/tint/reader/spirv/parser_impl_test_helper.cc
index 2a19c3d..4a57379 100644
--- a/src/tint/reader/spirv/parser_impl_test_helper.cc
+++ b/src/tint/reader/spirv/parser_impl_test_helper.cc
@@ -17,6 +17,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/generator_impl.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::reader::spirv::test {
 
 // Default to not dumping the SPIR-V assembly.
@@ -35,8 +37,10 @@
 
 std::string ToString(const Program& program) {
     writer::wgsl::GeneratorImpl writer(&program);
-    if (!writer.Generate()) {
-        return "WGSL writer error: " + writer.error();
+    writer.Generate();
+
+    if (!writer.Diagnostics().empty()) {
+        return "WGSL writer error: " + writer.Diagnostics().str();
     }
     return writer.result();
 }
@@ -44,9 +48,10 @@
 std::string ToString(const Program& program, utils::VectorRef<const ast::Statement*> stmts) {
     writer::wgsl::GeneratorImpl writer(&program);
     for (const auto* stmt : stmts) {
-        if (!writer.EmitStatement(stmt)) {
-            return "WGSL writer error: " + writer.error();
-        }
+        writer.EmitStatement(stmt);
+    }
+    if (!writer.Diagnostics().empty()) {
+        return "WGSL writer error: " + writer.Diagnostics().str();
     }
     return writer.result();
 }
@@ -57,14 +62,16 @@
         node,
         [&](const ast::Expression* expr) {
             utils::StringStream out;
-            if (!writer.EmitExpression(out, expr)) {
-                return "WGSL writer error: " + writer.error();
+            writer.EmitExpression(out, expr);
+            if (!writer.Diagnostics().empty()) {
+                return "WGSL writer error: " + writer.Diagnostics().str();
             }
             return out.str();
         },
         [&](const ast::Statement* stmt) {
-            if (!writer.EmitStatement(stmt)) {
-                return "WGSL writer error: " + writer.error();
+            writer.EmitStatement(stmt);
+            if (!writer.Diagnostics().empty()) {
+                return "WGSL writer error: " + writer.Diagnostics().str();
             }
             return writer.result();
         },
diff --git a/src/tint/reader/spirv/parser_impl_test_helper.h b/src/tint/reader/spirv/parser_impl_test_helper.h
index ea0e37e..c5d7939 100644
--- a/src/tint/reader/spirv/parser_impl_test_helper.h
+++ b/src/tint/reader/spirv/parser_impl_test_helper.h
@@ -34,7 +34,6 @@
 TINT_END_DISABLE_WARNING(NEWLINE_EOF);
 
 #include "gtest/gtest.h"
-#include "src/tint/demangler.h"
 #include "src/tint/reader/spirv/fail_stream.h"
 #include "src/tint/reader/spirv/function.h"
 #include "src/tint/reader/spirv/namer.h"
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index 310bc46..dcf1e1c 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -33,14 +33,8 @@
 namespace {
 
 bool ShouldRun(const Program* program) {
-    for (auto* node : program->ASTNodes().Objects()) {
-        if (auto* expr = node->As<ast::Expression>()) {
-            if (Is<type::ExternalTexture>(program->TypeOf(expr))) {
-                return true;
-            }
-        }
-    }
-    return false;
+    auto ext = program->Types().Find<type::ExternalTexture>();
+    return ext != nullptr;
 }
 
 /// This struct stores symbols for new bindings created as a result of transforming a
diff --git a/src/tint/writer/glsl/generator.cc b/src/tint/writer/glsl/generator.cc
index bbe6380..41d2cdf 100644
--- a/src/tint/writer/glsl/generator.cc
+++ b/src/tint/writer/glsl/generator.cc
@@ -46,7 +46,7 @@
     // Generate the GLSL code.
     auto impl = std::make_unique<GeneratorImpl>(&sanitized_result.program, options.version);
     result.success = impl->Generate();
-    result.error = impl->error();
+    result.error = impl->Diagnostics().str();
     result.glsl = impl->result();
 
     // Collect the list of entry points in the sanitized program.
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 311005b..5ae7650 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -180,12 +180,10 @@
         manager.Add<transform::Robustness>();
     }
 
-    if (!options.external_texture_options.bindings_map.empty()) {
-        // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
-        data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
-            options.external_texture_options.bindings_map);
-        manager.Add<transform::MultiplanarExternalTexture>();
-    }
+    // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
+    data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
+        options.external_texture_options.bindings_map);
+    manager.Add<transform::MultiplanarExternalTexture>();
 
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
diff --git a/src/tint/writer/glsl/generator_impl_array_accessor_test.cc b/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
index 453af22..793902b 100644
--- a/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
@@ -31,7 +31,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "ary[5]");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_assign_test.cc b/src/tint/writer/glsl/generator_impl_assign_test.cc
index d53d39f..63b8ce8 100644
--- a/src/tint/writer/glsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/glsl/generator_impl_assign_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(assign)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(assign)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  lhs = rhs;\n");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_binary_test.cc b/src/tint/writer/glsl/generator_impl_binary_test.cc
index 1c03530..29e7136 100644
--- a/src/tint/writer/glsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/glsl/generator_impl_binary_test.cc
@@ -59,7 +59,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 TEST_P(GlslBinaryTest, Emit_f16) {
@@ -87,7 +87,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 TEST_P(GlslBinaryTest, Emit_u32) {
@@ -106,7 +106,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 TEST_P(GlslBinaryTest, Emit_i32) {
@@ -130,7 +130,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -165,7 +165,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(a * 1.0f)");
 }
 
@@ -183,7 +183,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(a * 1.0hf)");
 }
 
@@ -199,7 +199,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(1.0f * a)");
 }
 
@@ -217,7 +217,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(1.0hf * a)");
 }
 
@@ -232,7 +232,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(mat * 1.0f)");
 }
 
@@ -249,7 +249,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(mat * 1.0hf)");
 }
 
@@ -264,7 +264,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(1.0f * mat)");
 }
 
@@ -281,7 +281,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(1.0hf * mat)");
 }
 
@@ -296,7 +296,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(mat * vec3(1.0f))");
 }
 
@@ -313,7 +313,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(mat * f16vec3(1.0hf))");
 }
 
@@ -328,7 +328,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(vec3(1.0f) * mat)");
 }
 
@@ -345,7 +345,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(f16vec3(1.0hf) * mat)");
 }
 
@@ -359,7 +359,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(lhs * rhs)");
 }
 
@@ -375,7 +375,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(lhs * rhs)");
 }
 
@@ -389,7 +389,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -405,7 +405,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -419,7 +419,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -435,7 +435,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -449,7 +449,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -465,7 +465,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -479,7 +479,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -495,7 +495,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
 }
 
@@ -513,7 +513,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec3 tint_float_modulo(vec3 lhs, vec3 rhs) {
@@ -557,7 +557,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -596,7 +596,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(tint_tmp)");
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (tint_tmp) {
@@ -621,7 +621,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(tint_tmp)");
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = a;
 if (tint_tmp_1) {
@@ -648,7 +648,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(tint_tmp)");
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (!tint_tmp) {
@@ -679,7 +679,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (tint_tmp) {
   tint_tmp = b;
@@ -715,7 +715,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = a;
 if (tint_tmp_1) {
   tint_tmp_1 = b;
@@ -746,7 +746,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = b;
 if (!tint_tmp_1) {
   tint_tmp_1 = c;
@@ -778,7 +778,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(decl)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(decl)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = b;
 if (tint_tmp_1) {
   tint_tmp_1 = c;
@@ -820,7 +820,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (tint_tmp) {
   tint_tmp = b;
diff --git a/src/tint/writer/glsl/generator_impl_bitcast_test.cc b/src/tint/writer/glsl/generator_impl_bitcast_test.cc
index b6e4f92..fc75caf 100644
--- a/src/tint/writer/glsl/generator_impl_bitcast_test.cc
+++ b/src/tint/writer/glsl/generator_impl_bitcast_test.cc
@@ -31,7 +31,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "intBitsToFloat(a)");
 }
 
@@ -43,7 +43,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "int(a)");
 }
 
@@ -55,7 +55,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "uint(a)");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_block_test.cc b/src/tint/writer/glsl/generator_impl_block_test.cc
index bf377fb..d9aec9f 100644
--- a/src/tint/writer/glsl/generator_impl_block_test.cc
+++ b/src/tint/writer/glsl/generator_impl_block_test.cc
@@ -27,7 +27,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     return;
   }
diff --git a/src/tint/writer/glsl/generator_impl_break_test.cc b/src/tint/writer/glsl/generator_impl_break_test.cc
index fa0e514..f3143eb 100644
--- a/src/tint/writer/glsl/generator_impl_break_test.cc
+++ b/src/tint/writer/glsl/generator_impl_break_test.cc
@@ -27,7 +27,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  break;\n");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index 27cd717..835db7c 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -351,7 +351,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "dot(param1, param2)");
 }
 
@@ -364,7 +364,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(true ? b : a)");
 }
 
@@ -377,7 +377,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_select(a, b, bvec2(true, false))");
 }
 
@@ -394,7 +394,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "((a) * (b) + (c))");
 }
 
@@ -412,7 +412,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "((a) * (b) + (c))");
 }
 
@@ -422,7 +422,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct modf_result_f32 {
@@ -458,7 +458,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -493,7 +493,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct modf_result_vec3_f32 {
@@ -529,7 +529,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -563,7 +563,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct modf_result_f32 {
@@ -591,7 +591,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -618,7 +618,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct modf_result_vec3_f32 {
@@ -646,7 +646,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -674,7 +674,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct frexp_result_f32 {
@@ -710,7 +710,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -745,7 +745,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct frexp_result_vec3_f32 {
@@ -781,7 +781,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -815,7 +815,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct frexp_result_f32 {
@@ -843,7 +843,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -870,7 +870,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct frexp_result_vec3_f32 {
@@ -898,7 +898,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -927,7 +927,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 float tint_degrees(float param_0) {
@@ -955,7 +955,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec3 tint_degrees(vec3 param_0) {
@@ -985,7 +985,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -1016,7 +1016,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -1045,7 +1045,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 float tint_radians(float param_0) {
@@ -1073,7 +1073,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec3 tint_radians(vec3 param_0) {
@@ -1103,7 +1103,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -1134,7 +1134,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
 
@@ -1165,7 +1165,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uvec3 tint_extract_bits(uvec3 v, uint offset, uint count) {
@@ -1199,7 +1199,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uvec3 tint_insert_bits(uvec3 v, uvec3 n, uint offset, uint count) {
@@ -1230,7 +1230,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec4 p1 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
@@ -1248,7 +1248,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec4 p1 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
@@ -1266,7 +1266,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec2 p1 = vec2(0.0f, 0.0f);
@@ -1284,7 +1284,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec2 p1 = vec2(0.0f, 0.0f);
@@ -1302,7 +1302,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec2 p1 = vec2(0.0f, 0.0f);
@@ -1320,7 +1320,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uint p1 = 0u;
@@ -1338,7 +1338,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uint p1 = 0u;
@@ -1356,7 +1356,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uint p1 = 0u;
@@ -1374,7 +1374,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uint p1 = 0u;
@@ -1392,7 +1392,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uint p1 = 0u;
@@ -1416,7 +1416,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -1439,7 +1439,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -1456,7 +1456,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 int tint_int_dot(ivec3 a, ivec3 b) {
@@ -1482,7 +1482,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 uint tint_int_dot(uvec3 a, uvec3 b) {
@@ -1508,7 +1508,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 float tint_quantizeToF16(float param_0) {
@@ -1535,7 +1535,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec2 tint_quantizeToF16(vec2 param_0) {
@@ -1562,7 +1562,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec3 tint_quantizeToF16(vec3 param_0) {
@@ -1591,7 +1591,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 vec4 tint_quantizeToF16(vec4 param_0) {
diff --git a/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc b/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc
index 040adfe..acd2faa 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc
@@ -289,7 +289,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto expected = expected_texture_overload(param.overload);
 
diff --git a/src/tint/writer/glsl/generator_impl_call_test.cc b/src/tint/writer/glsl/generator_impl_call_test.cc
index 69a78f4..a1da7ac 100644
--- a/src/tint/writer/glsl/generator_impl_call_test.cc
+++ b/src/tint/writer/glsl/generator_impl_call_test.cc
@@ -32,7 +32,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_func()");
 }
 
@@ -52,7 +52,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_func(param1, param2)");
 }
 
@@ -72,7 +72,7 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-    ASSERT_TRUE(gen.EmitStatement(call)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(call)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  my_func(param1, param2);\n");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_case_test.cc b/src/tint/writer/glsl/generator_impl_case_test.cc
index f12defd..39d5081 100644
--- a/src/tint/writer/glsl/generator_impl_case_test.cc
+++ b/src/tint/writer/glsl/generator_impl_case_test.cc
@@ -30,7 +30,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5: {
     break;
   }
@@ -45,7 +45,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5: {
     break;
   }
@@ -67,7 +67,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5:
   case 6: {
     break;
@@ -83,7 +83,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  default: {
     break;
   }
diff --git a/src/tint/writer/glsl/generator_impl_cast_test.cc b/src/tint/writer/glsl/generator_impl_cast_test.cc
index 41ee6f0..9b57e4b 100644
--- a/src/tint/writer/glsl/generator_impl_cast_test.cc
+++ b/src/tint/writer/glsl/generator_impl_cast_test.cc
@@ -29,7 +29,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "1.0f");
 }
 
@@ -40,7 +40,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "vec3(1.0f, 2.0f, 3.0f)");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_constructor_test.cc b/src/tint/writer/glsl/generator_impl_constructor_test.cc
index 02d148c..35bd2fb 100644
--- a/src/tint/writer/glsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_constructor_test.cc
@@ -29,7 +29,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("false"));
 }
 
@@ -38,7 +38,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-12345"));
 }
 
@@ -47,7 +47,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("56779u"));
 }
 
@@ -57,7 +57,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
 }
 
@@ -69,7 +69,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("32752.0hf"));
 }
 
@@ -78,7 +78,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-0.00001200000042445026f"));
 }
 
@@ -89,7 +89,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-0.0011997222900390625hf"));
 }
 
@@ -98,7 +98,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("true"));
 }
 
@@ -107,7 +107,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-12345"));
 }
 
@@ -116,7 +116,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("12345u"));
 }
 
@@ -125,7 +125,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("vec3(1.0f, 2.0f, 3.0f)"));
 }
 
@@ -136,7 +136,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("f16vec3(1.0hf, 2.0hf, 3.0hf)"));
 }
 
@@ -145,7 +145,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("vec3(0.0f)"));
 }
 
@@ -156,7 +156,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("f16vec3(0.0hf)"));
 }
 
@@ -165,7 +165,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("vec3(2.0f)"));
 }
 
@@ -176,7 +176,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("f16vec3(2.0hf)"));
 }
 
@@ -187,7 +187,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
   vec3 tint_symbol = vec3(v);)"));
 }
@@ -201,7 +201,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(float16_t v = 2.0hf;
   f16vec3 tint_symbol = f16vec3(v);)"));
 }
@@ -211,7 +211,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("bvec3(true)"));
 }
 
@@ -220,7 +220,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("ivec3(2)"));
 }
 
@@ -229,7 +229,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("uvec3(2u)"));
 }
 
@@ -238,7 +238,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(3.0f, 4.0f, 5.0f))"));
 }
@@ -250,7 +250,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("f16mat2x3(f16vec3(1.0hf, 2.0hf, 3.0hf), f16vec3(3.0hf, 4.0hf, 5.0hf))"));
@@ -277,7 +277,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("mat4(vec4(2.0f, 3.0f, 4.0f, 8.0f), vec4(0.0f), "
                                         "vec4(7.0f), vec4(42.0f, 21.0f, 6.0f, -5.0f))"));
@@ -306,7 +306,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("f16mat4(f16vec4(2.0hf, 3.0hf, 4.0hf, 8.0hf), f16vec4(0.0hf), "
@@ -318,7 +318,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("mat2x3 tint_symbol = mat2x3(vec3(0.0f), vec3(0.0f))"));
 }
@@ -330,7 +330,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("f16mat2x3 tint_symbol = f16mat2x3(f16vec3(0.0hf), f16vec3(0.0hf))"));
@@ -349,7 +349,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("mat4 m_2 = mat4(m_1);"));
 }
@@ -369,7 +369,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("f16mat4 m_2 = f16mat4(m_1);"));
 }
@@ -380,7 +380,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("vec3[3](vec3(1.0f, 2.0f, 3.0f), "
                                         "vec3(4.0f, 5.0f, 6.0f), "
                                         "vec3(7.0f, 8.0f, 9.0f))"));
@@ -391,7 +391,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("vec3[3](vec3(0.0f), vec3(0.0f), vec3(0.0f))"));
 }
 
@@ -406,7 +406,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("S(1, 2.0f, ivec3(3, 4, 5))"));
 }
 
@@ -421,7 +421,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("S(0"));
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_continue_test.cc b/src/tint/writer/glsl/generator_impl_continue_test.cc
index 331ce79..3ea8db6 100644
--- a/src/tint/writer/glsl/generator_impl_continue_test.cc
+++ b/src/tint/writer/glsl/generator_impl_continue_test.cc
@@ -27,7 +27,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     if (false) {
       break;
diff --git a/src/tint/writer/glsl/generator_impl_discard_test.cc b/src/tint/writer/glsl/generator_impl_discard_test.cc
index d63607d..a66ac1c 100644
--- a/src/tint/writer/glsl/generator_impl_discard_test.cc
+++ b/src/tint/writer/glsl/generator_impl_discard_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  discard;\n");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index bc085b9..ec72ef7 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -37,7 +37,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  #version 310 es
 
   void my_func() {
@@ -57,7 +57,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(  void tint_symbol() {
     return;
   })"));
@@ -78,7 +78,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  #version 310 es
 
   void my_func(float a, int b) {
@@ -96,7 +96,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -115,7 +115,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(float f(inout float foo) {
   return foo;
 }
@@ -143,7 +143,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -184,7 +184,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -237,7 +237,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -316,7 +316,7 @@
 
   GeneratorImpl& gen = SanitizeAndBuild();
 
-  ASSERT_TRUE(gen.Generate()) << gen.error();
+  ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
   EXPECT_EQ(gen.result(), R"(struct VertexOutput {
   float4 pos;
 };
@@ -376,7 +376,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -417,7 +417,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -458,7 +458,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -505,7 +505,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(#version 310 es
 precision highp float;
@@ -551,7 +551,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -596,7 +596,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -643,7 +643,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -689,7 +689,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(#version 310 es
 precision highp float;
@@ -726,7 +726,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision highp float;
 
@@ -752,7 +752,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -771,7 +771,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 layout(local_size_x = 2, local_size_y = 4, local_size_z = 6) in;
@@ -793,7 +793,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 layout(local_size_x = 2, local_size_y = 3, local_size_z = 4) in;
@@ -816,9 +816,9 @@
 
     GeneratorImpl& gen = Build();
 
-    EXPECT_FALSE(gen.Generate()) << gen.error();
+    EXPECT_FALSE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(
-        gen.error(),
+        gen.Diagnostics().str(),
         R"(error: override-expressions should have been removed with the SubstituteOverride transform)");
 }
 
@@ -830,7 +830,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 void my_func(float a[5]) {
@@ -848,7 +848,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 float[5] my_func() {
@@ -912,7 +912,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct Data {
diff --git a/src/tint/writer/glsl/generator_impl_identifier_test.cc b/src/tint/writer/glsl/generator_impl_identifier_test.cc
index 64b9fb3..3560295 100644
--- a/src/tint/writer/glsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/glsl/generator_impl_identifier_test.cc
@@ -29,7 +29,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "foo");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_if_test.cc b/src/tint/writer/glsl/generator_impl_if_test.cc
index 0713ab7..b474d2c 100644
--- a/src/tint/writer/glsl/generator_impl_if_test.cc
+++ b/src/tint/writer/glsl/generator_impl_if_test.cc
@@ -30,7 +30,7 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   }
@@ -53,7 +53,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
@@ -78,7 +78,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
@@ -106,7 +106,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
diff --git a/src/tint/writer/glsl/generator_impl_import_test.cc b/src/tint/writer/glsl/generator_impl_import_test.cc
index bf546ac..c7e3a97 100644
--- a/src/tint/writer/glsl/generator_impl_import_test.cc
+++ b/src/tint/writer/glsl/generator_impl_import_test.cc
@@ -41,7 +41,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.glsl_name) + "(1.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_Import,
@@ -80,7 +80,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.glsl_name) + "(1)");
 }
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_Import,
@@ -97,7 +97,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(
         out.str(),
         std::string(param.glsl_name) +
@@ -140,7 +140,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.glsl_name) + "(1.0f, 2.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_Import,
@@ -162,7 +162,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(),
               std::string(param.glsl_name) + "(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f))");
 }
@@ -187,7 +187,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.glsl_name) + "(1, 2)");
 }
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_Import,
@@ -205,7 +205,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.glsl_name) + "(1.0f, 2.0f, 3.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_Import,
@@ -225,7 +225,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(),
               std::string(param.glsl_name) +
                   R"((vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f)))");
@@ -246,7 +246,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.glsl_name) + "(1, 2, 3)");
 }
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_Import,
@@ -262,7 +262,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string("determinant(var)"));
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_loop_test.cc b/src/tint/writer/glsl/generator_impl_loop_test.cc
index 2ee83b8..3d23e1e 100644
--- a/src/tint/writer/glsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/glsl/generator_impl_loop_test.cc
@@ -34,7 +34,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
   }
@@ -55,7 +55,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
     {
@@ -79,7 +79,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
     {
@@ -116,7 +116,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     while (true) {
       break;
@@ -155,7 +155,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     float lhs = 2.5f;
     float other = 0.0f;
@@ -180,7 +180,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(; ; ) {
       return;
@@ -202,7 +202,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(int i = 0; ; ) {
       return;
@@ -226,7 +226,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     bool tint_tmp = t;
     if (tint_tmp) {
@@ -254,7 +254,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(; true; ) {
       a_statement();
@@ -279,7 +279,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     while (true) {
       bool tint_tmp = t;
@@ -307,7 +307,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(; ; i = (i + 1)) {
       return;
@@ -333,7 +333,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     while (true) {
       return;
@@ -359,7 +359,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(int i = 0; true; i = (i + 1)) {
       return;
@@ -387,7 +387,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     bool tint_tmp = t;
     if (tint_tmp) {
@@ -423,7 +423,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while(true) {
     return;
   }
@@ -442,7 +442,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while(true) {
     continue;
   }
@@ -466,7 +466,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     bool tint_tmp = t;
     if (tint_tmp) {
diff --git a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
index 6d0d58e..cfb36ea 100644
--- a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
@@ -119,7 +119,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct Data {
@@ -170,7 +170,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
@@ -222,7 +222,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
@@ -278,7 +278,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -331,7 +331,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -379,7 +379,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -427,7 +427,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -474,7 +474,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -527,7 +527,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -588,7 +588,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -650,7 +650,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -711,7 +711,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -771,7 +771,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -832,7 +832,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(#version 310 es
 precision highp float;
@@ -870,7 +870,7 @@
     WrapInFunction(var, expr);
 
     GeneratorImpl& gen = SanitizeAndBuild();
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("my_vec.xyz"));
 }
 
@@ -880,7 +880,7 @@
     WrapInFunction(var, expr);
 
     GeneratorImpl& gen = SanitizeAndBuild();
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("my_vec.gbr"));
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_module_constant_test.cc b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
index dfbcecf..adcfa50 100644
--- a/src/tint/writer/glsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
@@ -28,7 +28,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitProgramConstVariable(var)) << gen.error();
+    ASSERT_TRUE(gen.EmitProgramConstVariable(var)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "const float pos[3] = float[3](1.0f, 2.0f, 3.0f);\n");
 }
 
@@ -41,7 +41,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -61,7 +61,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -81,7 +81,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -101,7 +101,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -121,7 +121,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -143,7 +143,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
@@ -164,7 +164,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -184,7 +184,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -204,7 +204,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -226,7 +226,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
@@ -247,7 +247,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -267,7 +267,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -289,7 +289,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
@@ -310,7 +310,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -333,7 +333,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
diff --git a/src/tint/writer/glsl/generator_impl_return_test.cc b/src/tint/writer/glsl/generator_impl_return_test.cc
index 28bbf51..ce082d8 100644
--- a/src/tint/writer/glsl/generator_impl_return_test.cc
+++ b/src/tint/writer/glsl/generator_impl_return_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  return;\n");
 }
 
@@ -41,7 +41,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  return 123;\n");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
index 5ed2be7..79a3ea3 100644
--- a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
@@ -39,7 +39,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#version 310 es
@@ -79,7 +79,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#version 310 es
@@ -123,7 +123,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#version 310 es
@@ -160,7 +160,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#version 310 es
@@ -202,7 +202,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#version 310 es
@@ -248,7 +248,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#version 310 es
@@ -297,7 +297,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#version 310 es
diff --git a/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc b/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
index 7513e94..4251cb3 100644
--- a/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
@@ -48,7 +48,7 @@
 
     // TODO(crbug.com/tint/1421) offsets do not currently work on GLSL ES.
     // They will likely require manual padding.
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct Nephews {
@@ -71,7 +71,7 @@
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 440
 
 struct Nephews {
diff --git a/src/tint/writer/glsl/generator_impl_switch_test.cc b/src/tint/writer/glsl/generator_impl_switch_test.cc
index ce978dc..34184cf 100644
--- a/src/tint/writer/glsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/glsl/generator_impl_switch_test.cc
@@ -38,7 +38,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5: {
       break;
@@ -65,7 +65,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5:
     default: {
diff --git a/src/tint/writer/glsl/generator_impl_test.cc b/src/tint/writer/glsl/generator_impl_test.cc
index 9a24471..e5ce52e 100644
--- a/src/tint/writer/glsl/generator_impl_test.cc
+++ b/src/tint/writer/glsl/generator_impl_test.cc
@@ -33,7 +33,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 void my_func() {
@@ -47,7 +47,7 @@
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 440
 
 void my_func() {
@@ -70,7 +70,7 @@
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kES, 3, 1));
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_OES_sample_variables : require
 
@@ -95,7 +95,7 @@
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 440
 
 int my_func() {
diff --git a/src/tint/writer/glsl/generator_impl_type_test.cc b/src/tint/writer/glsl/generator_impl_type_test.cc
index 2535efd..3da9cd7 100644
--- a/src/tint/writer/glsl/generator_impl_type_test.cc
+++ b/src/tint/writer/glsl/generator_impl_type_test.cc
@@ -42,7 +42,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, "ary"))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool ary[4]");
 }
 
@@ -55,7 +55,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, "ary"))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool ary[5][4]");
 }
 
@@ -68,7 +68,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, "ary"))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool ary[6][5][4]");
 }
 
@@ -81,7 +81,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool[4]");
 }
 
@@ -93,7 +93,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, bool_, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool");
 }
 
@@ -105,7 +105,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, f32, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float");
 }
 
@@ -119,7 +119,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, f16, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float16_t");
 }
 
@@ -131,7 +131,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, i32, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "int");
 }
 
@@ -145,7 +145,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, mat2x3, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "mat2x3");
 }
 
@@ -161,7 +161,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, mat2x3, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "f16mat2x3");
 }
 
@@ -176,7 +176,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
     EXPECT_EQ(buf.String(), R"(struct S {
   int a;
   float b;
@@ -198,7 +198,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, sem_s, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "S");
 }
 
@@ -211,7 +211,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(struct S {
   int tint_symbol;
   float tint_symbol_1;
@@ -230,7 +230,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
     EXPECT_EQ(buf.String(), R"(struct S {
   int a;
   float b;
@@ -247,7 +247,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, u32, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "uint");
 }
 
@@ -260,7 +260,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, vec3, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "vec3");
 }
 
@@ -275,7 +275,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, vec3, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "f16vec3");
 }
 
@@ -287,7 +287,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, void_, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "void");
 }
 
@@ -299,7 +299,7 @@
     utils::StringStream out;
     ASSERT_FALSE(gen.EmitType(out, sampler, builtin::AddressSpace::kUndefined,
                               builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
 }
 
 TEST_F(GlslGeneratorImplTest_Type, EmitSamplerComparison) {
@@ -310,7 +310,7 @@
     utils::StringStream out;
     ASSERT_FALSE(gen.EmitType(out, sampler, builtin::AddressSpace::kUndefined,
                               builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
 }
 
 struct GlslDepthTextureData {
@@ -341,7 +341,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(params.result));
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -369,7 +369,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("sampler2DMS tex;"));
 }
 
@@ -415,7 +415,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(params.result));
 }
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_Type,
@@ -521,7 +521,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, s, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "highp sampler2DMS");
 }
 
@@ -553,7 +553,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(params.result));
 }
 INSTANTIATE_TEST_SUITE_P(
diff --git a/src/tint/writer/glsl/generator_impl_unary_op_test.cc b/src/tint/writer/glsl/generator_impl_unary_op_test.cc
index 27e9d2c..f8548ff 100644
--- a/src/tint/writer/glsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/glsl/generator_impl_unary_op_test.cc
@@ -28,7 +28,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "expr");
 }
 
@@ -40,7 +40,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "~(expr)");
 }
 
@@ -53,7 +53,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "expr");
 }
 
@@ -65,7 +65,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "!(expr)");
 }
 
@@ -77,7 +77,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "-(expr)");
 }
 
@@ -88,7 +88,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(-2147483647 - 1)");
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc b/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
index 2ace33c..9274d7a 100644
--- a/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
@@ -30,7 +30,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
 struct Simple {
@@ -50,7 +50,7 @@
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#version 440
 
 struct Simple {
diff --git a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
index 5691312..d5dbb15 100644
--- a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
@@ -34,7 +34,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
@@ -47,7 +47,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
@@ -60,7 +60,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
 }
 
@@ -74,7 +74,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -95,7 +95,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -116,7 +116,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -137,7 +137,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -158,7 +158,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -181,7 +181,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
@@ -203,7 +203,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -224,7 +224,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -245,7 +245,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -268,7 +268,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
@@ -290,7 +290,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -311,7 +311,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -334,7 +334,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 #extension GL_AMD_gpu_shader_half_float : require
@@ -356,7 +356,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -377,7 +377,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -398,7 +398,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -420,7 +420,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -449,7 +449,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
@@ -469,7 +469,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(),
                 HasSubstr("  float a[5] = float[5](0.0f, 0.0f, 0.0f, 0.0f, 0.0f);\n"));
 }
@@ -483,7 +483,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("  float a = 0.0f;\n"));
 }
 
@@ -495,7 +495,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(vec3 a = vec3(0.0f);
 )");
 }
@@ -510,7 +510,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(f16vec3 a = f16vec3(0.0hf);
 )");
 }
@@ -523,7 +523,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(mat2x3 a = mat2x3(vec3(0.0f), vec3(0.0f));
 )");
@@ -539,7 +539,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(f16mat2x3 a = f16mat2x3(f16vec3(0.0hf), f16vec3(0.0hf));
 )");
diff --git a/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc b/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
index 676025c..d2529f4 100644
--- a/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
+++ b/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
@@ -36,7 +36,7 @@
          });
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("shared float wg;\n"));
 }
 
@@ -52,7 +52,7 @@
          });
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("shared float wg;\n"));
 }
 
diff --git a/src/tint/writer/hlsl/generator.cc b/src/tint/writer/hlsl/generator.cc
index 788c5e8..cc3bc91 100644
--- a/src/tint/writer/hlsl/generator.cc
+++ b/src/tint/writer/hlsl/generator.cc
@@ -45,7 +45,7 @@
     // Generate the HLSL code.
     auto impl = std::make_unique<GeneratorImpl>(&sanitized_result.program);
     result.success = impl->Generate();
-    result.error = impl->error();
+    result.error = impl->Diagnostics().str();
     result.hlsl = impl->result();
 
     // Collect the list of entry points in the sanitized program.
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 7f7f2e0..0dc6586 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -195,12 +195,10 @@
         manager.Add<transform::Robustness>();
     }
 
-    if (!options.external_texture_options.bindings_map.empty()) {
-        // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
-        data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
-            options.external_texture_options.bindings_map);
-        manager.Add<transform::MultiplanarExternalTexture>();
-    }
+    // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
+    data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
+        options.external_texture_options.bindings_map);
+    manager.Add<transform::MultiplanarExternalTexture>();
 
     // BindingRemapper must come after MultiplanarExternalTexture
     manager.Add<transform::BindingRemapper>();
diff --git a/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
index df68694..e283401 100644
--- a/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
@@ -30,7 +30,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "ary[5]");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_binary_test.cc b/src/tint/writer/hlsl/generator_impl_binary_test.cc
index 7221e1a..61a4ee1 100644
--- a/src/tint/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_binary_test.cc
@@ -66,7 +66,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 TEST_P(HlslBinaryTest, Emit_f16) {
@@ -98,7 +98,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 TEST_P(HlslBinaryTest, Emit_u32) {
@@ -121,7 +121,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 TEST_P(HlslBinaryTest, Emit_i32) {
@@ -149,7 +149,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -186,7 +186,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(1.0f).xxx");
 }
 
@@ -203,7 +203,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(float16_t(1.0h)).xxx");
 }
 
@@ -218,7 +218,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(1.0f).xxx");
 }
 
@@ -235,7 +235,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(float16_t(1.0h)).xxx");
 }
 
@@ -250,7 +250,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(mat * 1.0f)");
 }
 
@@ -267,7 +267,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(mat * float16_t(1.0h))");
 }
 
@@ -282,7 +282,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(1.0f * mat)");
 }
 
@@ -299,7 +299,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(float16_t(1.0h) * mat)");
 }
 
@@ -314,7 +314,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "mul((1.0f).xxx, mat)");
 }
 
@@ -331,7 +331,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "mul((float16_t(1.0h)).xxx, mat)");
 }
 
@@ -346,7 +346,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "mul(mat, (1.0f).xxx)");
 }
 
@@ -363,7 +363,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "mul(mat, (float16_t(1.0h)).xxx)");
 }
 
@@ -377,7 +377,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "mul(rhs, lhs)");
 }
 
@@ -393,7 +393,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "mul(rhs, lhs)");
 }
 
@@ -407,7 +407,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(tint_tmp)");
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (tint_tmp) {
@@ -432,7 +432,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(tint_tmp)");
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = a;
 if (tint_tmp_1) {
@@ -459,7 +459,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(tint_tmp)");
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (!tint_tmp) {
@@ -490,7 +490,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (tint_tmp) {
   tint_tmp = b;
@@ -526,7 +526,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = a;
 if (tint_tmp_1) {
   tint_tmp_1 = b;
@@ -557,7 +557,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = b;
 if (!tint_tmp_1) {
   tint_tmp_1 = c;
@@ -589,7 +589,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(decl)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(decl)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp_1 = b;
 if (tint_tmp_1) {
   tint_tmp_1 = c;
@@ -631,7 +631,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(bool tint_tmp = a;
 if (tint_tmp) {
   tint_tmp = b;
diff --git a/src/tint/writer/hlsl/generator_impl_bitcast_test.cc b/src/tint/writer/hlsl/generator_impl_bitcast_test.cc
index 3061fbe..22d0fb8 100644
--- a/src/tint/writer/hlsl/generator_impl_bitcast_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_bitcast_test.cc
@@ -30,7 +30,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "asfloat(a)");
 }
 
@@ -42,7 +42,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "asint(a)");
 }
 
@@ -54,7 +54,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "asuint(a)");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_block_test.cc b/src/tint/writer/hlsl/generator_impl_block_test.cc
index c3687da..0ac0e77 100644
--- a/src/tint/writer/hlsl/generator_impl_block_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_block_test.cc
@@ -27,7 +27,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     return;
   }
diff --git a/src/tint/writer/hlsl/generator_impl_break_test.cc b/src/tint/writer/hlsl/generator_impl_break_test.cc
index 4e4ecf1..cad81b4 100644
--- a/src/tint/writer/hlsl/generator_impl_break_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_break_test.cc
@@ -27,7 +27,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  break;\n");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index 75fd463..92406a6 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -350,7 +350,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "dot(param1, param2)");
 }
 
@@ -363,7 +363,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(true ? b : a)");
 }
 
@@ -376,7 +376,7 @@
 
     gen.increment_indent();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(bool2(true, false) ? b : a)");
 }
 
@@ -386,7 +386,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_f32 {
   float fract;
   float whole;
@@ -414,7 +414,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_f16 {
   float16_t fract;
   float16_t whole;
@@ -440,7 +440,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f32 {
   float3 fract;
   float3 whole;
@@ -468,7 +468,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f16 {
   vector<float16_t, 3> fract;
   vector<float16_t, 3> whole;
@@ -493,7 +493,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_f32 {
   float fract;
   float whole;
@@ -513,7 +513,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_f16 {
   float16_t fract;
   float16_t whole;
@@ -531,7 +531,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f32 {
   float3 fract;
   float3 whole;
@@ -551,7 +551,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f16 {
   vector<float16_t, 3> fract;
   vector<float16_t, 3> whole;
@@ -575,7 +575,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f32 {
   float3 fract;
   float3 whole;
@@ -596,7 +596,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_f32 {
   float fract;
   int exp;
@@ -625,7 +625,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_f16 {
   float16_t fract;
   int exp;
@@ -652,7 +652,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f32 {
   float3 fract;
   int3 exp;
@@ -681,7 +681,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f16 {
   vector<float16_t, 3> fract;
   int3 exp;
@@ -707,7 +707,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_f32 {
   float fract;
   int exp;
@@ -727,7 +727,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_f16 {
   float16_t fract;
   int exp;
@@ -745,7 +745,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f32 {
   float3 fract;
   int3 exp;
@@ -765,7 +765,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f16 {
   vector<float16_t, 3> fract;
   int3 exp;
@@ -789,7 +789,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f32 {
   float3 fract;
   int3 exp;
@@ -811,7 +811,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float tint_degrees(float param_0) {
   return param_0 * 57.29577951308232286465;
 }
@@ -832,7 +832,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float3 tint_degrees(float3 param_0) {
   return param_0 * 57.29577951308232286465;
 }
@@ -855,7 +855,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float16_t tint_degrees(float16_t param_0) {
   return param_0 * 57.29577951308232286465;
 }
@@ -878,7 +878,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(vector<float16_t, 3> tint_degrees(vector<float16_t, 3> param_0) {
   return param_0 * 57.29577951308232286465;
 }
@@ -899,7 +899,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float tint_radians(float param_0) {
   return param_0 * 0.01745329251994329547;
 }
@@ -920,7 +920,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float3 tint_radians(float3 param_0) {
   return param_0 * 0.01745329251994329547;
 }
@@ -943,7 +943,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float16_t tint_radians(float16_t param_0) {
   return param_0 * 0.01745329251994329547;
 }
@@ -966,7 +966,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(vector<float16_t, 3> tint_radians(vector<float16_t, 3> param_0) {
   return param_0 * 0.01745329251994329547;
 }
@@ -987,7 +987,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   int val = 0;
@@ -1004,7 +1004,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   int3 val = int3(0, 0, 0);
@@ -1021,7 +1021,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   float val = 0.0f;
@@ -1038,7 +1038,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   float3 val = float3(0.0f, 0.0f, 0.0f);
@@ -1057,7 +1057,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   float16_t val = float16_t(0.0h);
@@ -1076,7 +1076,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   vector<float16_t, 3> val = vector<float16_t, 3>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
@@ -1093,7 +1093,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float tint_trunc(float param_0) {
   return param_0 < 0 ? ceil(param_0) : floor(param_0);
 }
@@ -1114,7 +1114,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float3 tint_trunc(float3 param_0) {
   return param_0 < 0 ? ceil(param_0) : floor(param_0);
 }
@@ -1137,7 +1137,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float16_t tint_trunc(float16_t param_0) {
   return param_0 < 0 ? ceil(param_0) : floor(param_0);
 }
@@ -1160,7 +1160,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(vector<float16_t, 3> tint_trunc(vector<float16_t, 3> param_0) {
   return param_0 < 0 ? ceil(param_0) : floor(param_0);
 }
@@ -1180,7 +1180,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(uint tint_pack4x8snorm(float4 param_0) {
   int4 i = int4(round(clamp(param_0, -1.0, 1.0) * 127.0)) & 0xff;
   return asuint(i.x | i.y << 8 | i.z << 16 | i.w << 24);
@@ -1202,7 +1202,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(uint tint_pack4x8unorm(float4 param_0) {
   uint4 i = uint4(round(clamp(param_0, 0.0, 1.0) * 255.0));
   return (i.x | i.y << 8 | i.z << 16 | i.w << 24);
@@ -1224,7 +1224,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(uint tint_pack2x16snorm(float2 param_0) {
   int2 i = int2(round(clamp(param_0, -1.0, 1.0) * 32767.0)) & 0xffff;
   return asuint(i.x | i.y << 16);
@@ -1246,7 +1246,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(uint tint_pack2x16unorm(float2 param_0) {
   uint2 i = uint2(round(clamp(param_0, 0.0, 1.0) * 65535.0));
   return (i.x | i.y << 16);
@@ -1268,7 +1268,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(uint tint_pack2x16float(float2 param_0) {
   uint2 i = f32tof16(param_0);
   return i.x | (i.y << 16);
@@ -1290,7 +1290,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float4 tint_unpack4x8snorm(uint param_0) {
   int j = int(param_0);
   int4 i = int4(j << 24, j << 16, j << 8, j) >> 24;
@@ -1313,7 +1313,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float4 tint_unpack4x8unorm(uint param_0) {
   uint j = param_0;
   uint4 i = uint4(j & 0xff, (j >> 8) & 0xff, (j >> 16) & 0xff, j >> 24);
@@ -1336,7 +1336,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float2 tint_unpack2x16snorm(uint param_0) {
   int j = int(param_0);
   int2 i = int2(j << 16, j) >> 16;
@@ -1359,7 +1359,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float2 tint_unpack2x16unorm(uint param_0) {
   uint j = param_0;
   uint2 i = uint2(j & 0xffff, j >> 16);
@@ -1382,7 +1382,7 @@
     WrapInFunction(Decl(Var("r", call)));
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float2 tint_unpack2x16float(uint param_0) {
   uint i = param_0;
   return f16tof32(uint2(i & 0xffff, i >> 16));
@@ -1410,7 +1410,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void main() {
   DeviceMemoryBarrierWithGroupSync();
@@ -1431,7 +1431,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void main() {
   GroupMemoryBarrierWithGroupSync();
@@ -1450,7 +1450,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(int tint_dot4I8Packed(uint param_0, uint param_1) {
   int accumulator = 0;
   return dot4add_i8packed(param_0, param_1, accumulator);
@@ -1476,7 +1476,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(uint tint_dot4U8Packed(uint param_0, uint param_1) {
   uint accumulator = 0u;
   return dot4add_u8packed(param_0, param_1, accumulator);
@@ -1499,7 +1499,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   int val1 = 0;
@@ -1516,7 +1516,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void test_function() {
   int val1 = 0;
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_texture_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_texture_test.cc
index 4d7165b..174c66c 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_texture_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_texture_test.cc
@@ -381,7 +381,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto expected = expected_texture_overload(param.overload);
 
diff --git a/src/tint/writer/hlsl/generator_impl_call_test.cc b/src/tint/writer/hlsl/generator_impl_call_test.cc
index 50909bb..80bacb6 100644
--- a/src/tint/writer/hlsl/generator_impl_call_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_call_test.cc
@@ -32,7 +32,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_func()");
 }
 
@@ -52,7 +52,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_func(param1, param2)");
 }
 
@@ -72,7 +72,7 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-    ASSERT_TRUE(gen.EmitStatement(call)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(call)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  my_func(param1, param2);\n");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_case_test.cc b/src/tint/writer/hlsl/generator_impl_case_test.cc
index 2352bbc..523de31 100644
--- a/src/tint/writer/hlsl/generator_impl_case_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_case_test.cc
@@ -30,7 +30,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5: {
     break;
   }
@@ -45,7 +45,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5: {
     break;
   }
@@ -63,7 +63,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5:
   case 6: {
     break;
@@ -79,7 +79,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s, 0u)) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s, 0u)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  default: {
     break;
   }
diff --git a/src/tint/writer/hlsl/generator_impl_cast_test.cc b/src/tint/writer/hlsl/generator_impl_cast_test.cc
index 7bb77da..73bc170 100644
--- a/src/tint/writer/hlsl/generator_impl_cast_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_cast_test.cc
@@ -29,7 +29,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "1.0f");
 }
 
@@ -40,7 +40,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float3(1.0f, 2.0f, 3.0f)");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_const_assert_test.cc b/src/tint/writer/hlsl/generator_impl_const_assert_test.cc
index eac90c6..52548db 100644
--- a/src/tint/writer/hlsl/generator_impl_const_assert_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_const_assert_test.cc
@@ -26,7 +26,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     // const asserts are not emitted
     EXPECT_EQ(gen.result(), "");
 }
@@ -36,7 +36,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     // const asserts are not emitted
     EXPECT_EQ(gen.result(), R"(void f() {
 }
diff --git a/src/tint/writer/hlsl/generator_impl_constructor_test.cc b/src/tint/writer/hlsl/generator_impl_constructor_test.cc
index d285cd6..f9c41ea 100644
--- a/src/tint/writer/hlsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_constructor_test.cc
@@ -29,7 +29,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("false"));
 }
 
@@ -38,7 +38,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-12345"));
 }
 
@@ -47,7 +47,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("56779u"));
 }
 
@@ -57,7 +57,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
 }
 
@@ -69,7 +69,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("float16_t(32752.0h)"));
 }
 
@@ -78,7 +78,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-0.00001200000042445026f"));
 }
 
@@ -89,7 +89,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("float16_t(-0.0011997222900390625h)"));
 }
 
@@ -98,7 +98,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("true"));
 }
 
@@ -107,7 +107,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-12345"));
 }
 
@@ -116,7 +116,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("12345u"));
 }
 
@@ -125,7 +125,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("float3(1.0f, 2.0f, 3.0f)"));
 }
 
@@ -136,7 +136,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(
         gen.result(),
         HasSubstr("vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h))"));
@@ -147,7 +147,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("0.0f).xxx"));
 }
 
@@ -158,7 +158,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("(float16_t(0.0h)).xxx"));
 }
 
@@ -167,7 +167,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("2.0f).xxx"));
 }
 
@@ -178,7 +178,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("(float16_t(2.0h)).xxx"));
 }
 
@@ -189,7 +189,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
   const float3 tint_symbol = float3((v).xxx);)"));
 }
@@ -203,7 +203,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(float16_t v = float16_t(2.0h);
   const vector<float16_t, 3> tint_symbol = vector<float16_t, 3>((v).xxx);)"));
 }
@@ -213,7 +213,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("(true).xxx"));
 }
 
@@ -224,7 +224,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(bool v = true;
   const bool3 tint_symbol = bool3((v).xxx);)"));
 }
@@ -234,7 +234,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("2).xxx"));
 }
 
@@ -243,7 +243,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("2u).xxx"));
 }
 
@@ -252,7 +252,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))"));
@@ -265,7 +265,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("matrix<float16_t, 2, 3>(vector<float16_t, 3>(float16_t(1.0h), "
@@ -294,7 +294,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("float4x4(float4(2.0f, 3.0f, 4.0f, 8.0f), (0.0f).xxxx, "
                                         "(7.0f).xxxx, float4(42.0f, 21.0f, 6.0f, -5.0f))"));
@@ -323,7 +323,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("matrix<float16_t, 4, 4>("
                                         "vector<float16_t, 4>(float16_t(2.0h), float16_t(3.0h), "
@@ -338,7 +338,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("float2x3 tint_symbol = float2x3((0.0f).xxx, (0.0f).xxx)"));
 }
@@ -350,7 +350,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("matrix<float16_t, 2, 3>((float16_t(0.0h)).xxx, (float16_t(0.0h)).xxx)"));
@@ -369,7 +369,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("float4x4 m_2 = float4x4(m_1);"));
 }
@@ -389,7 +389,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("matrix<float16_t, 4, 4> m_2 = matrix<float16_t, 4, 4>(m_1);"));
@@ -401,7 +401,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(
         gen.result(),
         HasSubstr(
@@ -413,7 +413,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("(float3[3])0"));
 }
 
@@ -428,7 +428,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("{1, 2.0f, int3(3, 4, 5)}"));
 }
 
@@ -443,7 +443,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("(S)0"));
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_continue_test.cc b/src/tint/writer/hlsl/generator_impl_continue_test.cc
index 6ba80c0..936d515 100644
--- a/src/tint/writer/hlsl/generator_impl_continue_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_continue_test.cc
@@ -28,7 +28,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     if (false) {
       break;
diff --git a/src/tint/writer/hlsl/generator_impl_discard_test.cc b/src/tint/writer/hlsl/generator_impl_discard_test.cc
index d5b86ac..9a7d1a5 100644
--- a/src/tint/writer/hlsl/generator_impl_discard_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_discard_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  discard;\n");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_function_test.cc b/src/tint/writer/hlsl/generator_impl_function_test.cc
index eacec2b..17df895 100644
--- a/src/tint/writer/hlsl/generator_impl_function_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_function_test.cc
@@ -37,7 +37,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  void my_func() {
     return;
   }
@@ -54,7 +54,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(  void tint_symbol() {
     return;
   })"));
@@ -75,7 +75,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  void my_func(float a, int b) {
     return;
   }
@@ -90,7 +90,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(void main() {
   return;
 }
@@ -106,7 +106,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(float f(inout float foo) {
   return foo;
 }
@@ -131,7 +131,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct tint_symbol_1 {
   float foo : TEXCOORD0;
 };
@@ -171,7 +171,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct tint_symbol_1 {
   float4 coord : SV_Position;
 };
@@ -230,7 +230,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct Interface {
   float4 pos;
   float col1;
@@ -315,7 +315,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct VertexOutput {
   float4 pos;
 };
@@ -384,7 +384,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(cbuffer cbuffer_ubo : register(b0, space1) {
   uint4 ubo[1];
 };
@@ -418,7 +418,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(cbuffer cbuffer_uniforms : register(b0, space1) {
   uint4 uniforms[1];
 };
@@ -452,7 +452,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(RWByteAddressBuffer coord : register(u0, space1);
 
@@ -485,7 +485,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(ByteAddressBuffer coord : register(t0, space1);
 
@@ -516,7 +516,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(RWByteAddressBuffer coord : register(u0, space1);
 
@@ -547,7 +547,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(RWByteAddressBuffer coord : register(u0, space1);
 
@@ -584,7 +584,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(cbuffer cbuffer_coord : register(b0, space1) {
   uint4 coord[1];
 };
@@ -627,7 +627,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(RWByteAddressBuffer coord : register(u0, space1);
 
@@ -650,7 +650,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(void tint_symbol() {
   return;
 }
@@ -666,7 +666,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
 void main() {
   return;
@@ -683,7 +683,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(2, 4, 6)]
 void main() {
   return;
@@ -703,7 +703,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"([numthreads(2, 3, 4)]
 void main() {
   return;
@@ -724,9 +724,9 @@
 
     GeneratorImpl& gen = Build();
 
-    EXPECT_FALSE(gen.Generate()) << gen.error();
+    EXPECT_FALSE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(
-        gen.error(),
+        gen.Diagnostics().str(),
         R"(error: override-expressions should have been removed with the SubstituteOverride transform)");
 }
 
@@ -742,7 +742,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(void my_func(float a[5]) {
   return;
 }
@@ -757,7 +757,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(typedef float my_func_ret[5];
 my_func_ret my_func() {
   return (float[5])0;
@@ -775,7 +775,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(void my_func(int a) {
   if ((a == 0)) {
     discard;
@@ -795,7 +795,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(int my_func(int a) {
   if (true) {
     if ((a == 0)) {
@@ -863,7 +863,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(RWByteAddressBuffer data : register(u0);
 
 [numthreads(1, 1, 1)]
diff --git a/src/tint/writer/hlsl/generator_impl_identifier_test.cc b/src/tint/writer/hlsl/generator_impl_identifier_test.cc
index db09c45..ca7c582 100644
--- a/src/tint/writer/hlsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_identifier_test.cc
@@ -29,7 +29,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "foo");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_if_test.cc b/src/tint/writer/hlsl/generator_impl_if_test.cc
index 9feb41e..764c40e 100644
--- a/src/tint/writer/hlsl/generator_impl_if_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_if_test.cc
@@ -30,7 +30,7 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   }
@@ -53,7 +53,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
@@ -78,7 +78,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
@@ -106,7 +106,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
diff --git a/src/tint/writer/hlsl/generator_impl_import_test.cc b/src/tint/writer/hlsl/generator_impl_import_test.cc
index ed41b32..67af40d 100644
--- a/src/tint/writer/hlsl/generator_impl_import_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_import_test.cc
@@ -41,7 +41,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -78,7 +78,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -95,7 +95,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(
         out.str(),
         std::string(param.hlsl_name) +
@@ -136,7 +136,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1.0f, 2.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -158,7 +158,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.hlsl_name) +
                              "(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))");
 }
@@ -183,7 +183,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1, 2)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -201,7 +201,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1.0f, 2.0f, 3.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -222,7 +222,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(
         out.str(),
         std::string(param.hlsl_name) +
@@ -245,7 +245,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1, 2, 3)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -261,7 +261,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string("determinant(var)"));
 }
 
@@ -274,7 +274,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string("f16tof32(f32tof16(v))"));
 }
 
@@ -287,7 +287,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string("f16tof32(f32tof16(v))"));
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_loop_test.cc b/src/tint/writer/hlsl/generator_impl_loop_test.cc
index 98b7f79..d8ec84b 100644
--- a/src/tint/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_loop_test.cc
@@ -34,7 +34,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
   }
@@ -55,7 +55,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
     {
@@ -79,7 +79,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
     {
@@ -116,7 +116,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     while (true) {
       break;
@@ -156,7 +156,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     float lhs = 2.5f;
     float other = 0.0f;
@@ -180,7 +180,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(; ; ) {
       return;
@@ -201,7 +201,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(int i = 0; ; ) {
       return;
@@ -225,7 +225,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     bool tint_tmp = t;
     if (tint_tmp) {
@@ -251,7 +251,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(; true; ) {
       return;
@@ -276,7 +276,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     while (true) {
       bool tint_tmp = t;
@@ -303,7 +303,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(; ; i = (i + 1)) {
       return;
@@ -329,7 +329,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     while (true) {
       return;
@@ -355,7 +355,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     for(int i = 0; true; i = (i + 1)) {
       return;
@@ -383,7 +383,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     bool tint_tmp = t;
     if (tint_tmp) {
@@ -419,7 +419,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while(true) {
     return;
   }
@@ -438,7 +438,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while(true) {
     continue;
   }
@@ -462,7 +462,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     bool tint_tmp = t;
     if (tint_tmp) {
diff --git a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
index ca08714..389b8f2 100644
--- a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -129,7 +129,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(struct Data {
   float mem;
 };
@@ -178,7 +178,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
@@ -321,7 +321,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
@@ -452,7 +452,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
@@ -728,7 +728,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
@@ -1003,7 +1003,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(p.expected));
 }
 
@@ -1138,7 +1138,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1174,7 +1174,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1207,7 +1207,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1238,7 +1238,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[5];
@@ -1273,7 +1273,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[3];
@@ -1307,7 +1307,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1339,7 +1339,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[6];
@@ -1382,7 +1382,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(struct Inner {
   int v;
@@ -1432,7 +1432,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(struct Inner {
   int v;
@@ -1479,7 +1479,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1517,7 +1517,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[6];
@@ -1553,7 +1553,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1592,7 +1592,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1631,7 +1631,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[8];
@@ -1674,7 +1674,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1715,7 +1715,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[8];
@@ -1759,7 +1759,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1801,7 +1801,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[8];
@@ -1844,7 +1844,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1885,7 +1885,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(cbuffer cbuffer_data : register(b1, space1) {
   uint4 data[8];
@@ -1927,7 +1927,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1968,7 +1968,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     auto* expected =
         R"(RWByteAddressBuffer data : register(u0, space1);
 
@@ -1986,7 +1986,7 @@
     WrapInFunction(var, expr);
 
     GeneratorImpl& gen = SanitizeAndBuild();
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("my_vec.xyz"));
 }
 
@@ -1996,7 +1996,7 @@
     WrapInFunction(var, expr);
 
     GeneratorImpl& gen = SanitizeAndBuild();
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("my_vec.gbr"));
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_module_constant_test.cc b/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
index 5a9327d..e7805a7 100644
--- a/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
@@ -28,7 +28,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const int l = 1;
@@ -42,7 +42,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float l = 1.0f;
@@ -56,7 +56,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const int l = 1;
@@ -70,7 +70,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const uint l = 1u;
@@ -84,7 +84,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float l = 1.0f;
@@ -100,7 +100,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float16_t l = float16_t(1.0h);
@@ -114,7 +114,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const int3 l = int3(1, 2, 3);
@@ -128,7 +128,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float3 l = float3(1.0f, 2.0f, 3.0f);
@@ -142,7 +142,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float3 l = float3(1.0f, 2.0f, 3.0f);
@@ -158,7 +158,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const vector<float16_t, 3> l = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
@@ -172,7 +172,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
@@ -186,7 +186,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
@@ -202,7 +202,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const matrix<float16_t, 2, 3> l = matrix<float16_t, 2, 3>(vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h)), vector<float16_t, 3>(float16_t(4.0h), float16_t(5.0h), float16_t(6.0h)));
@@ -216,7 +216,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float l[3] = {1.0f, 2.0f, 3.0f};
@@ -233,7 +233,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
diff --git a/src/tint/writer/hlsl/generator_impl_return_test.cc b/src/tint/writer/hlsl/generator_impl_return_test.cc
index 91b4159..7ef52c6 100644
--- a/src/tint/writer/hlsl/generator_impl_return_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_return_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  return;\n");
 }
 
@@ -41,7 +41,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  return 123;\n");
 }
 
diff --git a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
index e349b5c..cc8c253 100644
--- a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
@@ -39,7 +39,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
@@ -73,7 +73,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
@@ -110,7 +110,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
@@ -149,7 +149,7 @@
     options.array_length_from_uniform.bindpoint_to_size_index.emplace(sem::BindingPoint{2, 2}, 7u);
     GeneratorImpl& gen = SanitizeAndBuild(options);
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(cbuffer cbuffer_tint_symbol_1 : register(b4, space3) {
@@ -183,7 +183,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(void main() {
@@ -218,7 +218,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(struct S {
@@ -257,7 +257,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(void main() {
@@ -299,7 +299,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(void main() {
diff --git a/src/tint/writer/hlsl/generator_impl_switch_test.cc b/src/tint/writer/hlsl/generator_impl_switch_test.cc
index 810a99b..427835f 100644
--- a/src/tint/writer/hlsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_switch_test.cc
@@ -33,7 +33,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5: {
       break;
@@ -56,7 +56,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5:
     default: {
@@ -87,7 +87,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  do {
     a = 42;
   } while (false);
@@ -125,7 +125,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  bar();
   do {
     a = 42;
diff --git a/src/tint/writer/hlsl/generator_impl_test.cc b/src/tint/writer/hlsl/generator_impl_test.cc
index 0c39a31..572f71a 100644
--- a/src/tint/writer/hlsl/generator_impl_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_test.cc
@@ -34,7 +34,8 @@
     GeneratorImpl& gen = Build();
 
     ASSERT_FALSE(gen.Generate());
-    EXPECT_EQ(gen.error(), R"(12:34 error: HLSL backend does not support extension 'undefined')");
+    EXPECT_EQ(gen.Diagnostics().str(),
+              R"(12:34 error: HLSL backend does not support extension 'undefined')");
 }
 
 TEST_F(HlslGeneratorImplTest, Generate) {
@@ -42,7 +43,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(void my_func() {
 }
 )");
diff --git a/src/tint/writer/hlsl/generator_impl_type_test.cc b/src/tint/writer/hlsl/generator_impl_type_test.cc
index c842b4a..d93d8e3 100644
--- a/src/tint/writer/hlsl/generator_impl_type_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_type_test.cc
@@ -42,7 +42,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, "ary"))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool ary[4]");
 }
 
@@ -55,7 +55,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, "ary"))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool ary[5][4]");
 }
 
@@ -68,7 +68,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, "ary"))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool ary[6][5][4]");
 }
 
@@ -81,7 +81,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool[4]");
 }
 
@@ -93,7 +93,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, bool_, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool");
 }
 
@@ -105,7 +105,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, f16, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float16_t");
 }
 
@@ -117,7 +117,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, f32, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float");
 }
 
@@ -129,7 +129,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, i32, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "int");
 }
 
@@ -143,7 +143,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, mat2x3, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "matrix<float16_t, 2, 3>");
 }
 
@@ -157,7 +157,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, mat2x3, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float2x3");
 }
 
@@ -172,7 +172,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
     EXPECT_EQ(buf.String(), R"(struct S {
   int a;
   float b;
@@ -190,7 +190,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "RWByteAddressBuffer g : register(u0);\n");
 }
 
@@ -207,7 +207,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, sem_s, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "S");
 }
 
@@ -220,7 +220,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(struct S {
   int tint_symbol;
   float tint_symbol_1;
@@ -239,7 +239,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
     EXPECT_EQ(buf.String(), R"(struct S {
   int a;
   float b;
@@ -255,7 +255,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, u32, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "uint");
 }
 
@@ -268,7 +268,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, vec3, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float3");
 }
 
@@ -280,7 +280,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, void_, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "void");
 }
 
@@ -292,7 +292,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, sampler, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "SamplerState");
 }
 
@@ -304,7 +304,7 @@
     utils::StringStream out;
     ASSERT_TRUE(gen.EmitType(out, sampler, builtin::AddressSpace::kUndefined,
                              builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "SamplerComparisonState");
 }
 
@@ -336,7 +336,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(params.result));
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -367,7 +367,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("Texture2DMS<float4> tex : register(t1, space2);"));
 }
 
@@ -413,7 +413,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(params.result));
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Type,
@@ -519,7 +519,7 @@
     utils::StringStream out;
     ASSERT_TRUE(
         gen.EmitType(out, s, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite, ""))
-        << gen.error();
+        << gen.Diagnostics();
     EXPECT_EQ(out.str(), "Texture2DMS<float4>");
 }
 
@@ -556,7 +556,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(params.result));
 }
 INSTANTIATE_TEST_SUITE_P(
diff --git a/src/tint/writer/hlsl/generator_impl_unary_op_test.cc b/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
index 1ac6851..6d4a624 100644
--- a/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
@@ -28,7 +28,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "expr");
 }
 
@@ -40,7 +40,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "~(expr)");
 }
 
@@ -53,7 +53,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "expr");
 }
 
@@ -65,7 +65,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "!(expr)");
 }
 
@@ -77,7 +77,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "-(expr)");
 }
 }  // namespace
diff --git a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index f8420ae..f50ab58 100644
--- a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -34,7 +34,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
@@ -47,7 +47,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  const float a = 0.0f;\n");
 }
 
@@ -60,7 +60,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
 }
 
@@ -74,7 +74,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const int l = 1;
@@ -92,7 +92,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float l = 1.0f;
@@ -110,7 +110,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const int l = 1;
@@ -128,7 +128,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const uint l = 1u;
@@ -146,7 +146,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float l = 1.0f;
@@ -166,7 +166,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float16_t l = float16_t(1.0h);
@@ -184,7 +184,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const int3 l = int3(1, 2, 3);
@@ -202,7 +202,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float3 l = float3(1.0f, 2.0f, 3.0f);
@@ -220,7 +220,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float3 l = float3(1.0f, 2.0f, 3.0f);
@@ -240,7 +240,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const vector<float16_t, 3> l = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
@@ -258,7 +258,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
@@ -276,7 +276,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
@@ -296,7 +296,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const matrix<float16_t, 2, 3> l = matrix<float16_t, 2, 3>(vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h)), vector<float16_t, 3>(float16_t(4.0h), float16_t(5.0h), float16_t(6.0h)));
@@ -314,7 +314,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const float l[3] = {1.0f, 2.0f, 3.0f};
@@ -335,7 +335,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(void f() {
   const bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
@@ -352,7 +352,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("  float a[5] = (float[5])0;\n"));
 }
 
@@ -365,7 +365,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("  static float a = 0.0f;\n"));
 }
 
@@ -377,7 +377,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float3 a = (0.0f).xxx;
 )");
 }
@@ -392,7 +392,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(vector<float16_t, 3> a = (float16_t(0.0h)).xxx;
 )");
 }
@@ -405,7 +405,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(float2x3 a = float2x3((0.0f).xxx, (0.0f).xxx);
 )");
@@ -421,7 +421,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(
         gen.result(),
         R"(matrix<float16_t, 2, 3> a = matrix<float16_t, 2, 3>((float16_t(0.0h)).xxx, (float16_t(0.0h)).xxx);
diff --git a/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc b/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
index 3895aba..910bc9a 100644
--- a/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
@@ -36,7 +36,7 @@
          });
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("groupshared float wg;\n"));
 }
 
@@ -52,7 +52,7 @@
          });
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("groupshared float wg;\n"));
 }
 
diff --git a/src/tint/writer/msl/generator.cc b/src/tint/writer/msl/generator.cc
index 5cd9ac8..f811a88 100644
--- a/src/tint/writer/msl/generator.cc
+++ b/src/tint/writer/msl/generator.cc
@@ -50,7 +50,7 @@
     // Generate the MSL code.
     auto impl = std::make_unique<GeneratorImpl>(&sanitized_result.program);
     result.success = impl->Generate();
-    result.error = impl->error();
+    result.error = impl->Diagnostics().str();
     result.msl = impl->result();
     result.has_invariant_attribute = impl->HasInvariant();
     result.workgroup_allocations = impl->DynamicWorkgroupAllocations();
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index f4bf13e..ed459e6 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -211,12 +211,10 @@
         manager.Add<transform::BuiltinPolyfill>();
     }
 
-    if (!options.external_texture_options.bindings_map.empty()) {
-        // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
-        data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
-            options.external_texture_options.bindings_map);
-        manager.Add<transform::MultiplanarExternalTexture>();
-    }
+    // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
+    data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
+        options.external_texture_options.bindings_map);
+    manager.Add<transform::MultiplanarExternalTexture>();
 
     // BindingRemapper must come after MultiplanarExternalTexture
     manager.Add<transform::BindingRemapper>();
diff --git a/src/tint/writer/msl/generator_impl_array_accessor_test.cc b/src/tint/writer/msl/generator_impl_array_accessor_test.cc
index f721416..b194492 100644
--- a/src/tint/writer/msl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/msl/generator_impl_array_accessor_test.cc
@@ -30,7 +30,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "ary[5]");
 }
 
@@ -44,7 +44,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(*(p))[5]");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_assign_test.cc b/src/tint/writer/msl/generator_impl_assign_test.cc
index 6423aae..d73efc8 100644
--- a/src/tint/writer/msl/generator_impl_assign_test.cc
+++ b/src/tint/writer/msl/generator_impl_assign_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(assign)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(assign)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  lhs = rhs;\n");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_binary_test.cc b/src/tint/writer/msl/generator_impl_binary_test.cc
index 0297d8a..70bdf7d 100644
--- a/src/tint/writer/msl/generator_impl_binary_test.cc
+++ b/src/tint/writer/msl/generator_impl_binary_test.cc
@@ -48,7 +48,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -92,7 +92,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 using Op = ast::BinaryOp;
@@ -126,7 +126,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr2)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr2)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 using Op = ast::BinaryOp;
@@ -153,7 +153,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "fmod(left, right)");
 }
 
@@ -168,7 +168,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "fmod(left, right)");
 }
 
@@ -181,7 +181,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "fmod(left, right)");
 }
 
@@ -196,7 +196,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "fmod(left, right)");
 }
 
@@ -209,7 +209,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool(left & right)");
 }
 
@@ -222,7 +222,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool(left | right)");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_bitcast_test.cc b/src/tint/writer/msl/generator_impl_bitcast_test.cc
index a4aa870..b48d694 100644
--- a/src/tint/writer/msl/generator_impl_bitcast_test.cc
+++ b/src/tint/writer/msl/generator_impl_bitcast_test.cc
@@ -30,7 +30,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "as_type<float>(a)");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_block_test.cc b/src/tint/writer/msl/generator_impl_block_test.cc
index 5d4862c..3733326 100644
--- a/src/tint/writer/msl/generator_impl_block_test.cc
+++ b/src/tint/writer/msl/generator_impl_block_test.cc
@@ -27,7 +27,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     return;
   }
@@ -42,7 +42,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitBlock(b)) << gen.error();
+    ASSERT_TRUE(gen.EmitBlock(b)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     return;
   }
diff --git a/src/tint/writer/msl/generator_impl_break_test.cc b/src/tint/writer/msl/generator_impl_break_test.cc
index d9d1103..85eea25 100644
--- a/src/tint/writer/msl/generator_impl_break_test.cc
+++ b/src/tint/writer/msl/generator_impl_break_test.cc
@@ -27,7 +27,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  break;\n");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_builtin_test.cc b/src/tint/writer/msl/generator_impl_builtin_test.cc
index 9868d5e..5a38ba2 100644
--- a/src/tint/writer/msl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_test.cc
@@ -384,7 +384,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "dot(param1, param2)");
 }
 
@@ -395,7 +395,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "threadgroup_barrier(mem_flags::mem_device)");
 }
 
@@ -406,7 +406,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "threadgroup_barrier(mem_flags::mem_threadgroup)");
 }
 
@@ -416,7 +416,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -448,7 +448,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -478,7 +478,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -510,7 +510,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -539,7 +539,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -563,7 +563,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -585,7 +585,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -609,7 +609,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -632,7 +632,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -664,7 +664,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -694,7 +694,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -726,7 +726,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -755,7 +755,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -779,7 +779,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -801,7 +801,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -825,7 +825,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -849,7 +849,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -874,7 +874,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -901,7 +901,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -928,7 +928,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -953,7 +953,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -978,7 +978,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -1005,7 +1005,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -1032,7 +1032,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -1058,7 +1058,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "as_type<uint>(half2(p1))");
 }
 
@@ -1070,7 +1070,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float2(as_type<half2>(p1))");
 }
 
@@ -1080,7 +1080,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -1114,7 +1114,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
diff --git a/src/tint/writer/msl/generator_impl_builtin_texture_test.cc b/src/tint/writer/msl/generator_impl_builtin_texture_test.cc
index 730fe99..f6b9635 100644
--- a/src/tint/writer/msl/generator_impl_builtin_texture_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_texture_test.cc
@@ -287,7 +287,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
 
     auto expected = expected_texture_overload(param.overload);
     EXPECT_EQ(expected, out.str());
diff --git a/src/tint/writer/msl/generator_impl_call_test.cc b/src/tint/writer/msl/generator_impl_call_test.cc
index 95fbe91..17b97bc 100644
--- a/src/tint/writer/msl/generator_impl_call_test.cc
+++ b/src/tint/writer/msl/generator_impl_call_test.cc
@@ -32,7 +32,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_func()");
 }
 
@@ -55,7 +55,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_func(param1, param2)");
 }
 
@@ -76,7 +76,7 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  my_func(param1, param2);\n");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_case_test.cc b/src/tint/writer/msl/generator_impl_case_test.cc
index c218000..7306755 100644
--- a/src/tint/writer/msl/generator_impl_case_test.cc
+++ b/src/tint/writer/msl/generator_impl_case_test.cc
@@ -30,7 +30,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5: {
     break;
   }
@@ -45,7 +45,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5: {
     break;
   }
@@ -67,7 +67,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  case 5:
   case 6: {
     break;
@@ -83,7 +83,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  default: {
     break;
   }
diff --git a/src/tint/writer/msl/generator_impl_cast_test.cc b/src/tint/writer/msl/generator_impl_cast_test.cc
index 51fc0ab..78d6c58 100644
--- a/src/tint/writer/msl/generator_impl_cast_test.cc
+++ b/src/tint/writer/msl/generator_impl_cast_test.cc
@@ -29,7 +29,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "1.0f");
 }
 
@@ -40,7 +40,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float3(1.0f, 2.0f, 3.0f)");
 }
 
@@ -51,7 +51,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "2147483648u");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_const_assert_test.cc b/src/tint/writer/msl/generator_impl_const_assert_test.cc
index a9850eb..13153c1 100644
--- a/src/tint/writer/msl/generator_impl_const_assert_test.cc
+++ b/src/tint/writer/msl/generator_impl_const_assert_test.cc
@@ -26,7 +26,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     // const asserts are not emitted
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -39,7 +39,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     // const asserts are not emitted
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
diff --git a/src/tint/writer/msl/generator_impl_constructor_test.cc b/src/tint/writer/msl/generator_impl_constructor_test.cc
index ec5646c..b3a333e 100644
--- a/src/tint/writer/msl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/msl/generator_impl_constructor_test.cc
@@ -29,7 +29,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("false"));
 }
 
@@ -38,7 +38,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-12345"));
 }
 
@@ -47,7 +47,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("56779u"));
 }
 
@@ -57,7 +57,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
 }
 
@@ -69,7 +69,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("32752.0h"));
 }
 
@@ -78,7 +78,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-0.00001200000042445026f"));
 }
 
@@ -89,7 +89,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-0.0011997222900390625h"));
 }
 
@@ -98,7 +98,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("true"));
 }
 
@@ -107,7 +107,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("-12345"));
 }
 
@@ -116,7 +116,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("12345u"));
 }
 
@@ -125,7 +125,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("float3(1.0f, 2.0f, 3.0f)"));
 }
 
@@ -136,7 +136,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("half3(1.0h, 2.0h, 3.0h)"));
 }
 
@@ -145,7 +145,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("float3(0.0f)"));
 }
 
@@ -156,7 +156,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("half3(0.0h)"));
 }
 
@@ -165,7 +165,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("float3(2.0f)"));
 }
 
@@ -176,7 +176,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("half3(2.0h)"));
 }
 
@@ -187,7 +187,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
   float3 const tint_symbol = float3(v);)"));
 }
@@ -201,7 +201,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(half v = 2.0h;
   half3 const tint_symbol = half3(v);)"));
 }
@@ -211,7 +211,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("bool3(true)"));
 }
 
@@ -220,7 +220,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("int3(2)"));
 }
 
@@ -229,7 +229,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("uint3(2u)"));
 }
 
@@ -238,7 +238,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))"));
@@ -251,7 +251,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("half2x3(half3(1.0h, 2.0h, 3.0h), half3(3.0h, 4.0h, 5.0h))"));
@@ -278,7 +278,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("float4x4(float4(2.0f, 3.0f, 4.0f, 8.0f), float4(0.0f), "
                                         "float4(7.0f), float4(42.0f, 21.0f, 6.0f, -5.0f))"));
@@ -307,7 +307,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("half4x4(half4(2.0h, 3.0h, 4.0h, 8.0h), half4(0.0h), "
                                         "half4(7.0h), half4(42.0h, 21.0h, 6.0h, -5.0h))"));
@@ -318,7 +318,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("float2x3 const tint_symbol = float2x3(float3(0.0f), float3(0.0f))"));
@@ -331,7 +331,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(),
                 HasSubstr("half2x3 const tint_symbol = half2x3(half3(0.0h), half3(0.0h))"));
@@ -350,7 +350,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("float4x4 m_2 = float4x4(m_1);"));
 }
@@ -370,7 +370,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_THAT(gen.result(), HasSubstr("half4x4 m_2 = half4x4(m_1);"));
 }
@@ -381,7 +381,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("{float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), "
                                         "float3(7.0f, 8.0f, 9.0f)}"));
 }
@@ -397,7 +397,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("{.a=1, .b=2.0f, .c=int3(3, 4, 5)}"));
 }
 
@@ -412,7 +412,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("{}"));
     EXPECT_THAT(gen.result(), testing::Not(HasSubstr("{{}}")));
 }
diff --git a/src/tint/writer/msl/generator_impl_continue_test.cc b/src/tint/writer/msl/generator_impl_continue_test.cc
index 649aae6..09b85d7 100644
--- a/src/tint/writer/msl/generator_impl_continue_test.cc
+++ b/src/tint/writer/msl/generator_impl_continue_test.cc
@@ -28,7 +28,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     if (false) {
       break;
diff --git a/src/tint/writer/msl/generator_impl_discard_test.cc b/src/tint/writer/msl/generator_impl_discard_test.cc
index 4bfa8a7..311401c 100644
--- a/src/tint/writer/msl/generator_impl_discard_test.cc
+++ b/src/tint/writer/msl/generator_impl_discard_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  discard_fragment();\n");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_function_test.cc b/src/tint/writer/msl/generator_impl_function_test.cc
index 3eb809b..b828574 100644
--- a/src/tint/writer/msl/generator_impl_function_test.cc
+++ b/src/tint/writer/msl/generator_impl_function_test.cc
@@ -33,7 +33,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
@@ -59,7 +59,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
@@ -76,7 +76,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -105,7 +105,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -150,7 +150,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -206,7 +206,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -289,7 +289,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -357,7 +357,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -396,7 +396,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -440,7 +440,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -491,7 +491,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -543,7 +543,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -578,7 +578,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
@@ -612,7 +612,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
@@ -689,7 +689,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
diff --git a/src/tint/writer/msl/generator_impl_identifier_test.cc b/src/tint/writer/msl/generator_impl_identifier_test.cc
index a1e6260..0caf77d 100644
--- a/src/tint/writer/msl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/msl/generator_impl_identifier_test.cc
@@ -29,7 +29,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "foo");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_if_test.cc b/src/tint/writer/msl/generator_impl_if_test.cc
index 5138dce..acc5128 100644
--- a/src/tint/writer/msl/generator_impl_if_test.cc
+++ b/src/tint/writer/msl/generator_impl_if_test.cc
@@ -28,7 +28,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   }
@@ -45,7 +45,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
@@ -65,7 +65,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
@@ -85,7 +85,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
diff --git a/src/tint/writer/msl/generator_impl_import_test.cc b/src/tint/writer/msl/generator_impl_import_test.cc
index 841e7dc..d221665 100644
--- a/src/tint/writer/msl/generator_impl_import_test.cc
+++ b/src/tint/writer/msl/generator_impl_import_test.cc
@@ -83,7 +83,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), R"(abs(1))");
 }
 
@@ -94,7 +94,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), R"(fabs(2.0f))");
 }
 
@@ -108,7 +108,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1.0f, 2.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
@@ -126,7 +126,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), R"(fabs(2.0f - 3.0f))");
 }
 
@@ -140,7 +140,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.msl_name) +
                              R"((float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f)))");
 }
@@ -165,7 +165,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1, 2)");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
@@ -182,7 +182,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1.0f, 2.0f, 3.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
@@ -203,7 +203,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(
         out.str(),
         std::string(param.msl_name) +
@@ -226,7 +226,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1, 2, 3)");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
@@ -244,7 +244,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), std::string("determinant(var)"));
 }
 
@@ -257,7 +257,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float(half(v))");
 }
 
@@ -270,7 +270,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float3(half3(v))");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_loop_test.cc b/src/tint/writer/msl/generator_impl_loop_test.cc
index a0c56f7..7146433 100644
--- a/src/tint/writer/msl/generator_impl_loop_test.cc
+++ b/src/tint/writer/msl/generator_impl_loop_test.cc
@@ -34,7 +34,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
   }
@@ -55,7 +55,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
     {
@@ -79,7 +79,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     break;
     {
@@ -113,7 +113,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     while (true) {
       break;
@@ -153,7 +153,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     float lhs = 2.5f;
     float other = 0.0f;
@@ -178,7 +178,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  for(; ; ) {
     return;
   }
@@ -198,7 +198,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  for(int i = 0; ; ) {
     return;
   }
@@ -226,7 +226,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     {
       f(1);
@@ -252,7 +252,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  for(; true; ) {
     return;
   }
@@ -273,7 +273,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(  for(; ; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
     return;
@@ -302,7 +302,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while (true) {
     return;
     {
@@ -328,7 +328,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(  for(int i = 0; true; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
     a_statement();
@@ -358,7 +358,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  {
     {
       f(1);
@@ -388,7 +388,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while(true) {
     return;
   }
@@ -407,7 +407,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while(true) {
     continue;
   }
@@ -429,7 +429,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  while((t && false)) {
     return;
   }
diff --git a/src/tint/writer/msl/generator_impl_member_accessor_test.cc b/src/tint/writer/msl/generator_impl_member_accessor_test.cc
index 38a222c..5765342 100644
--- a/src/tint/writer/msl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/msl/generator_impl_member_accessor_test.cc
@@ -29,7 +29,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "str.mem");
 }
 
@@ -41,7 +41,7 @@
 
     GeneratorImpl& gen = Build();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_vec.xyz");
 }
 
@@ -53,7 +53,7 @@
 
     GeneratorImpl& gen = Build();
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "my_vec.gbr");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_module_constant_test.cc b/src/tint/writer/msl/generator_impl_module_constant_test.cc
index 8a326e2..4d977b0 100644
--- a/src/tint/writer/msl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/msl/generator_impl_module_constant_test.cc
@@ -28,7 +28,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -46,7 +46,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -64,7 +64,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -82,7 +82,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -100,7 +100,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -120,7 +120,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -138,7 +138,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -156,7 +156,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -174,7 +174,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -194,7 +194,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -212,7 +212,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -230,7 +230,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -250,7 +250,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -268,7 +268,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -302,7 +302,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
diff --git a/src/tint/writer/msl/generator_impl_return_test.cc b/src/tint/writer/msl/generator_impl_return_test.cc
index c9c3de7..91fe996 100644
--- a/src/tint/writer/msl/generator_impl_return_test.cc
+++ b/src/tint/writer/msl/generator_impl_return_test.cc
@@ -29,7 +29,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  return;\n");
 }
 
@@ -41,7 +41,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  return 123;\n");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_sanitizer_test.cc b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
index c75fe92..bc211b2 100644
--- a/src/tint/writer/msl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
@@ -44,7 +44,7 @@
     opts.array_length_from_uniform.bindpoint_to_size_index.emplace(sem::BindingPoint{2, 1}, 1);
     GeneratorImpl& gen = SanitizeAndBuild(opts);
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#include <metal_stdlib>
@@ -101,7 +101,7 @@
     opts.array_length_from_uniform.bindpoint_to_size_index.emplace(sem::BindingPoint{2, 1}, 1);
     GeneratorImpl& gen = SanitizeAndBuild(opts);
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#include <metal_stdlib>
@@ -162,7 +162,7 @@
     opts.array_length_from_uniform.bindpoint_to_size_index.emplace(sem::BindingPoint{2, 1}, 1);
     GeneratorImpl& gen = SanitizeAndBuild(opts);
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#include <metal_stdlib>
@@ -222,7 +222,7 @@
     options.array_length_from_uniform.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 2}, 2u);
     GeneratorImpl& gen = SanitizeAndBuild(options);
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     auto got = gen.result();
     auto* expect = R"(#include <metal_stdlib>
@@ -281,7 +281,7 @@
     GeneratorImpl& gen = SanitizeAndBuild(options);
 
     ASSERT_FALSE(gen.Generate());
-    EXPECT_THAT(gen.error(), HasSubstr("Unable to translate builtin: arrayLength"));
+    EXPECT_THAT(gen.Diagnostics().str(), HasSubstr("Unable to translate builtin: arrayLength"));
 }
 
 }  // namespace
diff --git a/src/tint/writer/msl/generator_impl_switch_test.cc b/src/tint/writer/msl/generator_impl_switch_test.cc
index 6b47d09..352db35 100644
--- a/src/tint/writer/msl/generator_impl_switch_test.cc
+++ b/src/tint/writer/msl/generator_impl_switch_test.cc
@@ -37,7 +37,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5: {
       break;
@@ -61,7 +61,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5:
     default: {
diff --git a/src/tint/writer/msl/generator_impl_test.cc b/src/tint/writer/msl/generator_impl_test.cc
index 43eb558..4772d46 100644
--- a/src/tint/writer/msl/generator_impl_test.cc
+++ b/src/tint/writer/msl/generator_impl_test.cc
@@ -37,7 +37,8 @@
     GeneratorImpl& gen = Build();
 
     ASSERT_FALSE(gen.Generate());
-    EXPECT_EQ(gen.error(), R"(12:34 error: MSL backend does not support extension 'undefined')");
+    EXPECT_EQ(gen.Diagnostics().str(),
+              R"(12:34 error: MSL backend does not support extension 'undefined')");
 }
 
 TEST_F(MslGeneratorImplTest, Generate) {
@@ -49,7 +50,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -111,7 +112,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_TRUE(gen.HasInvariant());
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -148,7 +149,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_FALSE(gen.HasInvariant());
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -174,7 +175,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -214,7 +215,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -275,7 +276,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
@@ -363,7 +364,7 @@
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index ce1c215..3208708 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -96,7 +96,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_array<bool, 4>");
 }
 
@@ -108,7 +108,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_array<tint_array<bool, 4>, 5>");
 }
 
@@ -121,7 +121,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_array<tint_array<tint_array<bool, 4>, 5>, 6>");
 }
 
@@ -132,7 +132,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_array<bool, 4>");
 }
 
@@ -143,7 +143,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "ary")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_array<bool, 1>");
 }
 
@@ -153,7 +153,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, bool_, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, bool_, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "bool");
 }
 
@@ -163,7 +163,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, f32, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, f32, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float");
 }
 
@@ -173,7 +173,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, f16, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, f16, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "half");
 }
 
@@ -183,7 +183,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, i32, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, i32, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "int");
 }
 
@@ -195,7 +195,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float2x3");
 }
 
@@ -207,7 +207,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "half2x3");
 }
 
@@ -219,7 +219,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, p, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, p, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "threadgroup float* ");
 }
 
@@ -232,7 +232,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(s), "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(s), "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "S");
 }
 
@@ -246,7 +246,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
     EXPECT_EQ(buf.String(), R"(struct S {
   int a;
   float b;
@@ -293,7 +293,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(type)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
 
     // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
@@ -402,7 +402,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(type)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
 
     // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
@@ -494,7 +494,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(type)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
 
     // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
@@ -578,7 +578,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(type)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
 
     // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
@@ -640,7 +640,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(type)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
     EXPECT_EQ(buf.String(), R"(struct S {
   /* 0x0000 */ int tint_pad_2;
   /* 0x0004 */ tint_array<int8_t, 124> tint_pad_10;
@@ -699,7 +699,7 @@
 
     TextGenerator::TextBuffer buf;
     auto* sem_s = program->TypeOf(type)->As<sem::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
+    ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.Diagnostics();
     EXPECT_EQ(buf.String(), R"(struct S {
   /* 0x0000 */ int a;
   /* 0x0004 */ float b;
@@ -713,7 +713,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, u32, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, u32, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "uint");
 }
 
@@ -724,7 +724,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, vec3, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, vec3, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "float3");
 }
 
@@ -734,7 +734,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, void_, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, void_, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "void");
 }
 
@@ -744,7 +744,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, sampler, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, sampler, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "sampler");
 }
 
@@ -754,7 +754,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, sampler, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, sampler, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "sampler");
 }
 
@@ -777,7 +777,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -798,7 +798,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "depth2d_ms<float, access::read>");
 }
 
@@ -822,7 +822,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, s, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, s, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -844,7 +844,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, ms, "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, ms, "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "texture2d_ms<uint, access::read>");
 }
 
@@ -868,7 +868,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "")) << gen.error();
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type), "")) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
diff --git a/src/tint/writer/msl/generator_impl_unary_op_test.cc b/src/tint/writer/msl/generator_impl_unary_op_test.cc
index d92a9cb..a19a3b4 100644
--- a/src/tint/writer/msl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/msl/generator_impl_unary_op_test.cc
@@ -28,7 +28,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "&(expr)");
 }
 
@@ -40,7 +40,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "~(expr)");
 }
 
@@ -53,7 +53,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "*(expr)");
 }
 
@@ -65,7 +65,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "!(expr)");
 }
 
@@ -77,7 +77,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "tint_unary_minus(expr)");
 }
 
@@ -88,7 +88,7 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), "(-2147483647 - 1)");
 }
 
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index f4b0578..3660be5 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -34,7 +34,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
@@ -47,7 +47,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  float const a = 0.0f;\n");
 }
 
@@ -60,7 +60,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
 }
 
@@ -70,7 +70,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -88,7 +88,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -106,7 +106,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -124,7 +124,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -142,7 +142,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -162,7 +162,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -180,7 +180,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -198,7 +198,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -216,7 +216,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -236,7 +236,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -254,7 +254,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -272,7 +272,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -292,7 +292,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -310,7 +310,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -344,7 +344,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
 
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
@@ -378,7 +378,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  tint_array<float, 5> a = {};\n");
 }
 
@@ -396,7 +396,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(  S a = {};
 )");
 }
@@ -410,7 +410,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  float2 a = 0.0f;\n");
 }
 
@@ -425,7 +425,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  half2 a = 0.0h;\n");
 }
 
@@ -439,7 +439,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  float3x2 a = float3x2(0.0f);\n");
 }
 
@@ -455,7 +455,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), "  half3x2 a = half3x2(0.0h);\n");
 }
 
@@ -467,7 +467,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(float3 a = float3(0.0f);
 )");
 }
@@ -482,7 +482,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(), R"(half3 a = half3(0.0h);
 )");
 }
@@ -495,7 +495,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(float2x3 a = float2x3(float3(0.0f), float3(0.0f));
 )");
@@ -511,7 +511,7 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
     EXPECT_EQ(gen.result(),
               R"(half2x3 a = half2x3(half3(0.0h), half3(0.0h));
 )");
@@ -526,7 +526,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr(R"(thread tint_private_vars_struct tint_private_vars = {};
     float const tint_symbol = tint_private_vars.a;
     return;
@@ -542,7 +542,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
     EXPECT_THAT(gen.result(), HasSubstr("threadgroup float tint_symbol_2;\n"));
 }
 
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index 970a3e5..80485c9 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -92,12 +92,10 @@
         options.binding_remapper_options.access_controls,
         options.binding_remapper_options.allow_collisions);
 
-    if (!options.external_texture_options.bindings_map.empty()) {
-        // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
-        data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
-            options.external_texture_options.bindings_map);
-        manager.Add<transform::MultiplanarExternalTexture>();
-    }
+    // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
+    data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(
+        options.external_texture_options.bindings_map);
+    manager.Add<transform::MultiplanarExternalTexture>();
 
     {  // Builtin polyfills
         // BuiltinPolyfill must come before DirectVariableAccess, due to the use of pointer
diff --git a/src/tint/writer/syntax_tree/generator.cc b/src/tint/writer/syntax_tree/generator.cc
index 6919d24..0f93ae5 100644
--- a/src/tint/writer/syntax_tree/generator.cc
+++ b/src/tint/writer/syntax_tree/generator.cc
@@ -26,8 +26,9 @@
 
     // Generate the AST dump.
     auto impl = std::make_unique<GeneratorImpl>(program);
-    result.success = impl->Generate();
-    result.error = impl->error();
+    impl->Generate();
+    result.success = impl->Diagnostics().empty();
+    result.error = impl->Diagnostics().str();
     result.ast = impl->result();
 
     return result;
diff --git a/src/tint/writer/syntax_tree/generator_impl.cc b/src/tint/writer/syntax_tree/generator_impl.cc
index 02e6ff7..4d5f9be 100644
--- a/src/tint/writer/syntax_tree/generator_impl.cc
+++ b/src/tint/writer/syntax_tree/generator_impl.cc
@@ -45,40 +45,31 @@
 
 GeneratorImpl::~GeneratorImpl() = default;
 
-bool GeneratorImpl::Generate() {
+void GeneratorImpl::Generate() {
     // Generate global declarations in the order they appear in the module.
     for (auto* decl : program_->AST().GlobalDeclarations()) {
-        if (!Switch(
-                decl,  //
-                [&](const ast::DiagnosticDirective* dd) {
-                    return EmitDiagnosticControl(dd->control);
-                },
-                [&](const ast::Enable* e) { return EmitEnable(e); },
-                [&](const ast::TypeDecl* td) { return EmitTypeDecl(td); },
-                [&](const ast::Function* func) { return EmitFunction(func); },
-                [&](const ast::Variable* var) { return EmitVariable(var); },
-                [&](const ast::ConstAssert* ca) { return EmitConstAssert(ca); },
-                [&](Default) {
-                    TINT_UNREACHABLE(Writer, diagnostics_);
-                    return false;
-                })) {
-            return false;
-        }
+        Switch(
+            decl,  //
+            [&](const ast::DiagnosticDirective* dd) { EmitDiagnosticControl(dd->control); },
+            [&](const ast::Enable* e) { EmitEnable(e); },
+            [&](const ast::TypeDecl* td) { EmitTypeDecl(td); },
+            [&](const ast::Function* func) { EmitFunction(func); },
+            [&](const ast::Variable* var) { EmitVariable(var); },
+            [&](const ast::ConstAssert* ca) { EmitConstAssert(ca); },
+            [&](Default) { TINT_UNREACHABLE(Writer, diagnostics_); });
+
         if (decl != program_->AST().GlobalDeclarations().Back()) {
             line();
         }
     }
-
-    return true;
 }
 
-bool GeneratorImpl::EmitDiagnosticControl(const ast::DiagnosticControl& diagnostic) {
+void GeneratorImpl::EmitDiagnosticControl(const ast::DiagnosticControl& diagnostic) {
     line() << "DiagnosticControl [severity: " << diagnostic.severity
            << ", rule: " << program_->Symbols().NameFor(diagnostic.rule_name->symbol) << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitEnable(const ast::Enable* enable) {
+void GeneratorImpl::EmitEnable(const ast::Enable* enable) {
     auto l = line();
     l << "Enable [";
     for (auto* ext : enable->extensions) {
@@ -88,13 +79,12 @@
         l << ext->name;
     }
     l << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitTypeDecl(const ast::TypeDecl* ty) {
-    return Switch(
-        ty,
-        [&](const ast::Alias* alias) {  //
+void GeneratorImpl::EmitTypeDecl(const ast::TypeDecl* ty) {
+    Switch(
+        ty,  //
+        [&](const ast::Alias* alias) {
             line() << "Alias [";
             {
                 ScopedIndent ai(this);
@@ -103,87 +93,53 @@
                 line() << "expr: ";
                 {
                     ScopedIndent ex(this);
-                    if (!EmitExpression(alias->type)) {
-                        return false;
-                    }
+                    EmitExpression(alias->type);
                 }
             }
             line() << "]";
-            return true;
         },
-        [&](const ast::Struct* str) {  //
-            return EmitStructType(str);
-        },
-        [&](Default) {  //
+        [&](const ast::Struct* str) { EmitStructType(str); },
+        [&](Default) {
             diagnostics_.add_error(diag::System::Writer,
                                    "unknown declared type: " + std::string(ty->TypeInfo().name));
-            return false;
         });
 }
 
-bool GeneratorImpl::EmitExpression(const ast::Expression* expr) {
-    return Switch(
-        expr,
-        [&](const ast::IndexAccessorExpression* a) {  //
-            return EmitIndexAccessor(a);
-        },
-        [&](const ast::BinaryExpression* b) {  //
-            return EmitBinary(b);
-        },
-        [&](const ast::BitcastExpression* b) {  //
-            return EmitBitcast(b);
-        },
-        [&](const ast::CallExpression* c) {  //
-            return EmitCall(c);
-        },
-        [&](const ast::IdentifierExpression* i) {  //
-            return EmitIdentifier(i);
-        },
-        [&](const ast::LiteralExpression* l) {  //
-            return EmitLiteral(l);
-        },
-        [&](const ast::MemberAccessorExpression* m) {  //
-            return EmitMemberAccessor(m);
-        },
-        [&](const ast::PhonyExpression*) {  //
-            line() << "[PhonyExpression]";
-            return true;
-        },
-        [&](const ast::UnaryOpExpression* u) {  //
-            return EmitUnaryOp(u);
-        },
-        [&](Default) {
-            diagnostics_.add_error(diag::System::Writer, "unknown expression type");
-            return false;
-        });
+void GeneratorImpl::EmitExpression(const ast::Expression* expr) {
+    Switch(
+        expr,  //
+        [&](const ast::IndexAccessorExpression* a) { EmitIndexAccessor(a); },
+        [&](const ast::BinaryExpression* b) { EmitBinary(b); },
+        [&](const ast::BitcastExpression* b) { EmitBitcast(b); },
+        [&](const ast::CallExpression* c) { EmitCall(c); },
+        [&](const ast::IdentifierExpression* i) { EmitIdentifier(i); },
+        [&](const ast::LiteralExpression* l) { EmitLiteral(l); },
+        [&](const ast::MemberAccessorExpression* m) { EmitMemberAccessor(m); },
+        [&](const ast::PhonyExpression*) { line() << "[PhonyExpression]"; },
+        [&](const ast::UnaryOpExpression* u) { EmitUnaryOp(u); },
+        [&](Default) { diagnostics_.add_error(diag::System::Writer, "unknown expression type"); });
 }
 
-bool GeneratorImpl::EmitIndexAccessor(const ast::IndexAccessorExpression* expr) {
+void GeneratorImpl::EmitIndexAccessor(const ast::IndexAccessorExpression* expr) {
     line() << "IndexAccessorExpression [";
     {
         ScopedIndent iae(this);
         line() << "object: ";
         {
             ScopedIndent obj(this);
-            if (!EmitExpression(expr->object)) {
-                return false;
-            }
+            EmitExpression(expr->object);
         }
 
         line() << "index: ";
         {
             ScopedIndent idx(this);
-            if (!EmitExpression(expr->index)) {
-                return false;
-            }
+            EmitExpression(expr->index);
         }
     }
     line() << "]";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitMemberAccessor(const ast::MemberAccessorExpression* expr) {
+void GeneratorImpl::EmitMemberAccessor(const ast::MemberAccessorExpression* expr) {
     line() << "MemberAccessorExpression [";
     {
         ScopedIndent mae(this);
@@ -191,40 +147,32 @@
         line() << "object: ";
         {
             ScopedIndent obj(this);
-            if (!EmitExpression(expr->object)) {
-                return false;
-            }
+            EmitExpression(expr->object);
         }
         line() << "member: " << program_->Symbols().NameFor(expr->member->symbol);
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitBitcast(const ast::BitcastExpression* expr) {
+void GeneratorImpl::EmitBitcast(const ast::BitcastExpression* expr) {
     line() << "BitcastExpression [";
     {
         ScopedIndent bc(this);
         {
             line() << "type: ";
             ScopedIndent ty(this);
-            if (!EmitExpression(expr->type)) {
-                return false;
-            }
+            EmitExpression(expr->type);
         }
         {
             line() << "expr: ";
             ScopedIndent exp(this);
-            if (!EmitExpression(expr->expr)) {
-                return false;
-            }
+            EmitExpression(expr->expr);
         }
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitCall(const ast::CallExpression* expr) {
+void GeneratorImpl::EmitCall(const ast::CallExpression* expr) {
     line() << "Call [";
     {
         ScopedIndent cl(this);
@@ -232,9 +180,7 @@
         line() << "target: [";
         {
             ScopedIndent tgt(this);
-            if (!EmitExpression(expr->target)) {
-                return false;
-            }
+            EmitExpression(expr->target);
         }
         line() << "]";
 
@@ -246,9 +192,7 @@
                     line() << "arg: [";
                     {
                         ScopedIndent arg_val(this);
-                        if (!EmitExpression(arg)) {
-                            return false;
-                        }
+                        EmitExpression(arg);
                     }
                     line() << "]";
                 }
@@ -257,21 +201,16 @@
         }
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitLiteral(const ast::LiteralExpression* lit) {
-    bool ret = false;
+void GeneratorImpl::EmitLiteral(const ast::LiteralExpression* lit) {
     line() << "LiteralExpression [";
     {
         ScopedIndent le(this);
-        ret = Switch(
-            lit,
-            [&](const ast::BoolLiteralExpression* l) {  //
-                line() << (l->value ? "true" : "false");
-                return true;
-            },
-            [&](const ast::FloatLiteralExpression* l) {  //
+        Switch(
+            lit,  //
+            [&](const ast::BoolLiteralExpression* l) { line() << (l->value ? "true" : "false"); },
+            [&](const ast::FloatLiteralExpression* l) {
                 // f16 literals are also emitted as float value with suffix "h".
                 // Note that all normal and subnormal f16 values are normal f32 values, and since
                 // NaN and Inf are not allowed to be spelled in literal, it should be fine to emit
@@ -281,33 +220,23 @@
                 } else {
                     line() << FloatToBitPreservingString(static_cast<float>(l->value)) << l->suffix;
                 }
-                return true;
             },
-            [&](const ast::IntLiteralExpression* l) {  //
-                line() << l->value << l->suffix;
-                return true;
-            },
-            [&](Default) {  //
-                diagnostics_.add_error(diag::System::Writer, "unknown literal type");
-                return false;
-            });
+            [&](const ast::IntLiteralExpression* l) { line() << l->value << l->suffix; },
+            [&](Default) { diagnostics_.add_error(diag::System::Writer, "unknown literal type"); });
     }
     line() << "]";
-    return ret;
 }
 
-bool GeneratorImpl::EmitIdentifier(const ast::IdentifierExpression* expr) {
-    bool ret = false;
+void GeneratorImpl::EmitIdentifier(const ast::IdentifierExpression* expr) {
     line() << "IdentifierExpression [";
     {
         ScopedIndent ie(this);
-        ret = EmitIdentifier(expr->identifier);
+        EmitIdentifier(expr->identifier);
     }
     line() << "]";
-    return ret;
 }
 
-bool GeneratorImpl::EmitIdentifier(const ast::Identifier* ident) {
+void GeneratorImpl::EmitIdentifier(const ast::Identifier* ident) {
     line() << "Identifier [";
     {
         ScopedIndent id(this);
@@ -329,9 +258,7 @@
                     {
                         ScopedIndent args(this);
                         for (auto* expr : tmpl_ident->arguments) {
-                            if (!EmitExpression(expr)) {
-                                return false;
-                            }
+                            EmitExpression(expr);
                         }
                     }
                     line() << "]";
@@ -343,10 +270,9 @@
         }
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitFunction(const ast::Function* func) {
+void GeneratorImpl::EmitFunction(const ast::Function* func) {
     line() << "Function [";
     {
         ScopedIndent funct(this);
@@ -355,9 +281,7 @@
             line() << "attrs: [";
             {
                 ScopedIndent attrs(this);
-                if (!EmitAttributes(func->attributes)) {
-                    return false;
-                }
+                EmitAttributes(func->attributes);
             }
             line() << "]";
         }
@@ -376,18 +300,14 @@
                             line() << "attrs: [";
                             {
                                 ScopedIndent attrs(this);
-                                if (!EmitAttributes(v->attributes)) {
-                                    return false;
-                                }
+                                EmitAttributes(v->attributes);
                             }
                             line() << "]";
                         }
                         line() << "type: [";
                         {
                             ScopedIndent ty(this);
-                            if (!EmitExpression(v->type)) {
-                                return false;
-                            }
+                            EmitExpression(v->type);
                         }
                         line() << "]";
                     }
@@ -406,9 +326,7 @@
                     line() << "attrs: [";
                     {
                         ScopedIndent attrs(this);
-                        if (!EmitAttributes(func->return_type_attributes)) {
-                            return false;
-                        }
+                        EmitAttributes(func->return_type_attributes);
                     }
                     line() << "]";
                 }
@@ -416,9 +334,7 @@
                 line() << "type: [";
                 {
                     ScopedIndent ty(this);
-                    if (!EmitExpression(func->return_type)) {
-                        return false;
-                    }
+                    EmitExpression(func->return_type);
                 }
                 line() << "]";
             } else {
@@ -430,26 +346,20 @@
         {
             ScopedIndent bdy(this);
             if (func->body) {
-                if (!EmitBlockHeader(func->body)) {
-                    return false;
-                }
-                if (!EmitStatementsWithIndent(func->body->statements)) {
-                    return false;
-                }
+                EmitBlockHeader(func->body);
+                EmitStatementsWithIndent(func->body->statements);
             }
         }
         line() << "]";
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitImageFormat(const builtin::TexelFormat fmt) {
+void GeneratorImpl::EmitImageFormat(const builtin::TexelFormat fmt) {
     line() << "builtin::TexelFormat [" << fmt << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitStructType(const ast::Struct* str) {
+void GeneratorImpl::EmitStructType(const ast::Struct* str) {
     line() << "Struct [";
     {
         ScopedIndent strct(this);
@@ -458,9 +368,7 @@
             line() << "attrs: [";
             {
                 ScopedIndent attrs(this);
-                if (!EmitAttributes(str->attributes)) {
-                    return false;
-                }
+                EmitAttributes(str->attributes);
             }
             line() << "]";
         }
@@ -477,9 +385,7 @@
                         line() << "attrs: [";
                         {
                             ScopedIndent attrs(this);
-                            if (!EmitAttributes(mem->attributes)) {
-                                return false;
-                            }
+                            EmitAttributes(mem->attributes);
                         }
                         line() << "]";
                     }
@@ -488,9 +394,7 @@
                     line() << "type: [";
                     {
                         ScopedIndent ty(this);
-                        if (!EmitExpression(mem->type)) {
-                            return false;
-                        }
+                        EmitExpression(mem->type);
                     }
                     line() << "]";
                 }
@@ -500,10 +404,9 @@
         line() << "]";
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitVariable(const ast::Variable* v) {
+void GeneratorImpl::EmitVariable(const ast::Variable* v) {
     line() << "Variable [";
     {
         ScopedIndent variable(this);
@@ -511,14 +414,12 @@
             line() << "attrs: [";
             {
                 ScopedIndent attr(this);
-                if (!EmitAttributes(v->attributes)) {
-                    return false;
-                }
+                EmitAttributes(v->attributes);
             }
             line() << "]";
         }
 
-        bool ok = Switch(
+        Switch(
             v,  //
             [&](const ast::Var* var) {
                 if (var->declared_address_space || var->declared_access) {
@@ -528,18 +429,14 @@
                         line() << "address_space: [";
                         {
                             ScopedIndent addr(this);
-                            if (!EmitExpression(var->declared_address_space)) {
-                                return false;
-                            }
+                            EmitExpression(var->declared_address_space);
                         }
                         line() << "]";
                         if (var->declared_access) {
                             line() << "access: [";
                             {
                                 ScopedIndent acs(this);
-                                if (!EmitExpression(var->declared_access)) {
-                                    return false;
-                                }
+                                EmitExpression(var->declared_access);
                             }
                             line() << "]";
                         }
@@ -548,27 +445,13 @@
                 } else {
                     line() << "Var []";
                 }
-                return true;
             },
-            [&](const ast::Let*) {
-                line() << "Let []";
-                return true;
-            },
-            [&](const ast::Override*) {
-                line() << "Override []";
-                return true;
-            },
-            [&](const ast::Const*) {
-                line() << "Const []";
-                return true;
-            },
+            [&](const ast::Let*) { line() << "Let []"; },
+            [&](const ast::Override*) { line() << "Override []"; },
+            [&](const ast::Const*) { line() << "Const []"; },
             [&](Default) {
                 TINT_ICE(Writer, diagnostics_) << "unhandled variable type " << v->TypeInfo().name;
-                return false;
             });
-        if (!ok) {
-            return false;
-        }
 
         line() << "name: " << program_->Symbols().NameFor(v->name->symbol);
 
@@ -576,9 +459,7 @@
             line() << "type: [";
             {
                 ScopedIndent vty(this);
-                if (!EmitExpression(ty)) {
-                    return false;
-                }
+                EmitExpression(ty);
             }
             line() << "]";
         }
@@ -587,21 +468,18 @@
             line() << "initializer: [";
             {
                 ScopedIndent init(this);
-                if (!EmitExpression(v->initializer)) {
-                    return false;
-                }
+                EmitExpression(v->initializer);
             }
             line() << "]";
         }
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
+void GeneratorImpl::EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
     for (auto* attr : attrs) {
-        bool ok = Switch(
-            attr,
+        Switch(
+            attr,  //
             [&](const ast::WorkgroupAttribute* workgroup) {
                 auto values = workgroup->Values();
                 line() << "WorkgroupAttribute [";
@@ -609,65 +487,49 @@
                     ScopedIndent wg(this);
                     for (size_t i = 0; i < 3; i++) {
                         if (values[i]) {
-                            if (!EmitExpression(values[i])) {
-                                return false;
-                            }
+                            EmitExpression(values[i]);
                         }
                     }
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::StageAttribute* stage) {
                 line() << "StageAttribute [" << stage->stage << "]";
-                return true;
             },
             [&](const ast::BindingAttribute* binding) {
                 line() << "BindingAttribute [";
                 {
                     ScopedIndent ba(this);
-                    if (!EmitExpression(binding->expr)) {
-                        return false;
-                    }
+                    EmitExpression(binding->expr);
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::GroupAttribute* group) {
                 line() << "GroupAttribute [";
                 {
                     ScopedIndent ga(this);
-                    if (!EmitExpression(group->expr)) {
-                        return false;
-                    }
+                    EmitExpression(group->expr);
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::LocationAttribute* location) {
                 line() << "LocationAttribute [";
                 {
                     ScopedIndent la(this);
-                    if (!EmitExpression(location->expr)) {
-                        return false;
-                    }
+                    EmitExpression(location->expr);
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::BuiltinAttribute* builtin) {
                 line() << "BuiltinAttribute [";
                 {
                     ScopedIndent ba(this);
-                    if (!EmitExpression(builtin->builtin)) {
-                        return false;
-                    }
+                    EmitExpression(builtin->builtin);
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::DiagnosticAttribute* diagnostic) {
-                return EmitDiagnosticControl(diagnostic->control);
+                EmitDiagnosticControl(diagnostic->control);
             },
             [&](const ast::InterpolateAttribute* interpolate) {
                 line() << "InterpolateAttribute [";
@@ -676,99 +538,68 @@
                     line() << "type: [";
                     {
                         ScopedIndent ty(this);
-                        if (!EmitExpression(interpolate->type)) {
-                            return false;
-                        }
+                        EmitExpression(interpolate->type);
                     }
                     line() << "]";
                     if (interpolate->sampling) {
                         line() << "sampling: [";
                         {
                             ScopedIndent sa(this);
-                            if (!EmitExpression(interpolate->sampling)) {
-                                return false;
-                            }
+                            EmitExpression(interpolate->sampling);
                         }
                         line() << "]";
                     }
                 }
                 line() << "]";
-                return true;
             },
-            [&](const ast::InvariantAttribute*) {
-                line() << "InvariantAttribute []";
-                return true;
-            },
+            [&](const ast::InvariantAttribute*) { line() << "InvariantAttribute []"; },
             [&](const ast::IdAttribute* override_deco) {
                 line() << "IdAttribute [";
                 {
                     ScopedIndent id(this);
-                    if (!EmitExpression(override_deco->expr)) {
-                        return false;
-                    }
+                    EmitExpression(override_deco->expr);
                 }
                 line() << "]";
-                return true;
             },
-            [&](const ast::MustUseAttribute*) {
-                line() << "MustUseAttribute []";
-                return true;
-            },
+            [&](const ast::MustUseAttribute*) { line() << "MustUseAttribute []"; },
             [&](const ast::StructMemberOffsetAttribute* offset) {
                 line() << "StructMemberOffsetAttribute [";
                 {
                     ScopedIndent smoa(this);
-                    if (!EmitExpression(offset->expr)) {
-                        return false;
-                    }
+                    EmitExpression(offset->expr);
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::StructMemberSizeAttribute* size) {
                 line() << "StructMemberSizeAttribute [";
                 {
                     ScopedIndent smsa(this);
-                    if (!EmitExpression(size->expr)) {
-                        return false;
-                    }
+                    EmitExpression(size->expr);
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::StructMemberAlignAttribute* align) {
                 line() << "StructMemberAlignAttribute [";
                 {
                     ScopedIndent smaa(this);
-                    if (!EmitExpression(align->expr)) {
-                        return false;
-                    }
+                    EmitExpression(align->expr);
                 }
                 line() << "]";
-                return true;
             },
             [&](const ast::StrideAttribute* stride) {
                 line() << "StrideAttribute [" << stride->stride << "]";
-                return true;
             },
             [&](const ast::InternalAttribute* internal) {
                 line() << "InternalAttribute [" << internal->InternalName() << "]";
-                return true;
             },
             [&](Default) {
                 TINT_ICE(Writer, diagnostics_)
                     << "Unsupported attribute '" << attr->TypeInfo().name << "'";
-                return false;
             });
-
-        if (!ok) {
-            return false;
-        }
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitBinary(const ast::BinaryExpression* expr) {
+void GeneratorImpl::EmitBinary(const ast::BinaryExpression* expr) {
     line() << "BinaryExpression [";
     {
         ScopedIndent be(this);
@@ -776,33 +607,26 @@
         {
             ScopedIndent lhs(this);
 
-            if (!EmitExpression(expr->lhs)) {
-                return false;
-            }
+            EmitExpression(expr->lhs);
         }
         line() << "]";
         line() << "op: [";
         {
             ScopedIndent op(this);
-            if (!EmitBinaryOp(expr->op)) {
-                return false;
-            }
+            EmitBinaryOp(expr->op);
         }
         line() << "]";
         line() << "rhs: [";
         {
             ScopedIndent rhs(this);
-            if (!EmitExpression(expr->rhs)) {
-                return false;
-            }
+            EmitExpression(expr->rhs);
         }
         line() << "]";
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitBinaryOp(const ast::BinaryOp op) {
+void GeneratorImpl::EmitBinaryOp(const ast::BinaryOp op) {
     switch (op) {
         case ast::BinaryOp::kAnd:
             line() << "&";
@@ -860,12 +684,11 @@
             break;
         case ast::BinaryOp::kNone:
             diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
-            return false;
+            break;
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitUnaryOp(const ast::UnaryOpExpression* expr) {
+void GeneratorImpl::EmitUnaryOp(const ast::UnaryOpExpression* expr) {
     line() << "UnaryOpExpression [";
     {
         ScopedIndent uoe(this);
@@ -894,137 +717,106 @@
         line() << "expr: [";
         {
             ScopedIndent ex(this);
-            if (!EmitExpression(expr->expr)) {
-                return false;
-            }
+            EmitExpression(expr->expr);
         }
         line() << "]";
     }
     line() << "]";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
-    {
-        if (!EmitBlockHeader(stmt)) {
-            return false;
-        }
-    }
-    if (!EmitStatementsWithIndent(stmt->statements)) {
-        return false;
-    }
-
-    return true;
+void GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
+    EmitBlockHeader(stmt);
+    EmitStatementsWithIndent(stmt->statements);
 }
 
-bool GeneratorImpl::EmitBlockHeader(const ast::BlockStatement* stmt) {
+void GeneratorImpl::EmitBlockHeader(const ast::BlockStatement* stmt) {
     if (!stmt->attributes.IsEmpty()) {
         line() << "attrs: [";
         {
             ScopedIndent attrs(this);
-            if (!EmitAttributes(stmt->attributes)) {
-                return false;
-            }
+            EmitAttributes(stmt->attributes);
         }
         line() << "]";
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
-    return Switch(
+void GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
+    Switch(
         stmt,  //
-        [&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
-        [&](const ast::BlockStatement* b) { return EmitBlock(b); },
-        [&](const ast::BreakStatement* b) { return EmitBreak(b); },
-        [&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
-        [&](const ast::CallStatement* c) { return EmitCall(c->expr); },
-        [&](const ast::CompoundAssignmentStatement* c) { return EmitCompoundAssign(c); },
-        [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
-        [&](const ast::DiscardStatement* d) { return EmitDiscard(d); },
-        [&](const ast::IfStatement* i) { return EmitIf(i); },
-        [&](const ast::IncrementDecrementStatement* l) { return EmitIncrementDecrement(l); },
-        [&](const ast::LoopStatement* l) { return EmitLoop(l); },
-        [&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
-        [&](const ast::WhileStatement* l) { return EmitWhile(l); },
-        [&](const ast::ReturnStatement* r) { return EmitReturn(r); },
-        [&](const ast::ConstAssert* c) { return EmitConstAssert(c); },
-        [&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
-        [&](const ast::VariableDeclStatement* v) { return EmitVariable(v->variable); },
+        [&](const ast::AssignmentStatement* a) { EmitAssign(a); },
+        [&](const ast::BlockStatement* b) { EmitBlock(b); },
+        [&](const ast::BreakStatement* b) { EmitBreak(b); },
+        [&](const ast::BreakIfStatement* b) { EmitBreakIf(b); },
+        [&](const ast::CallStatement* c) { EmitCall(c->expr); },
+        [&](const ast::CompoundAssignmentStatement* c) { EmitCompoundAssign(c); },
+        [&](const ast::ContinueStatement* c) { EmitContinue(c); },
+        [&](const ast::DiscardStatement* d) { EmitDiscard(d); },
+        [&](const ast::IfStatement* i) { EmitIf(i); },
+        [&](const ast::IncrementDecrementStatement* l) { EmitIncrementDecrement(l); },
+        [&](const ast::LoopStatement* l) { EmitLoop(l); },
+        [&](const ast::ForLoopStatement* l) { EmitForLoop(l); },
+        [&](const ast::WhileStatement* l) { EmitWhile(l); },
+        [&](const ast::ReturnStatement* r) { EmitReturn(r); },
+        [&](const ast::ConstAssert* c) { EmitConstAssert(c); },
+        [&](const ast::SwitchStatement* s) { EmitSwitch(s); },
+        [&](const ast::VariableDeclStatement* v) { EmitVariable(v->variable); },
         [&](Default) {
             diagnostics_.add_error(diag::System::Writer,
                                    "unknown statement type: " + std::string(stmt->TypeInfo().name));
-            return false;
         });
 }
 
-bool GeneratorImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
+void GeneratorImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
     for (auto* s : stmts) {
-        if (!EmitStatement(s)) {
-            return false;
-        }
+        EmitStatement(s);
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts) {
+void GeneratorImpl::EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts) {
     ScopedIndent si(this);
-    return EmitStatements(stmts);
+    EmitStatements(stmts);
 }
 
-bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) {
+void GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) {
     line() << "AssignmentStatement [";
     {
         ScopedIndent as(this);
         line() << "lhs: [";
         {
             ScopedIndent lhs(this);
-            if (!EmitExpression(stmt->lhs)) {
-                return false;
-            }
+            EmitExpression(stmt->lhs);
         }
         line() << "]";
         line() << "rhs: [";
         {
             ScopedIndent rhs(this);
-            if (!EmitExpression(stmt->rhs)) {
-                return false;
-            }
-            line() << "]";
+            EmitExpression(stmt->rhs);
         }
+        line() << "]";
     }
     line() << "]";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
+void GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
     line() << "BreakStatement []";
-    return true;
 }
 
-bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
+void GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
     line() << "BreakIfStatement [";
     {
         ScopedIndent bis(this);
-        if (!EmitExpression(b->condition)) {
-            return false;
-        }
+        EmitExpression(b->condition);
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
+void GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
     line() << "CaseStatement [";
     {
         ScopedIndent cs(this);
         if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
             line() << "selector: default";
-            if (!EmitBlockHeader(stmt->body)) {
-                return false;
-            }
+            EmitBlockHeader(stmt->body);
         } else {
             line() << "selectors: [";
             {
@@ -1032,65 +824,52 @@
                 for (auto* sel : stmt->selectors) {
                     if (sel->IsDefault()) {
                         line() << "default []";
-                    } else if (!EmitExpression(sel->expr)) {
-                        return false;
+                    } else {
+                        EmitExpression(sel->expr);
                     }
                 }
             }
             line() << "]";
-            if (!EmitBlockHeader(stmt->body)) {
-                return false;
-            }
+            EmitBlockHeader(stmt->body);
         }
-        if (!EmitStatementsWithIndent(stmt->body->statements)) {
-            return false;
-        }
+        EmitStatementsWithIndent(stmt->body->statements);
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt) {
+void GeneratorImpl::EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt) {
     line() << "CompoundAssignmentStatement [";
     {
         ScopedIndent cas(this);
         line() << "lhs: [";
         {
             ScopedIndent lhs(this);
-            if (!EmitExpression(stmt->lhs)) {
-                return false;
-            }
+            EmitExpression(stmt->lhs);
         }
         line() << "]";
 
         line() << "op: [";
         {
             ScopedIndent op(this);
-            if (!EmitBinaryOp(stmt->op)) {
-                return false;
-            }
+            EmitBinaryOp(stmt->op);
         }
         line() << "]";
         line() << "rhs: [";
         {
             ScopedIndent rhs(this);
 
-            if (!EmitExpression(stmt->rhs)) {
-                return false;
-            }
+            EmitExpression(stmt->rhs);
         }
         line() << "]";
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitContinue(const ast::ContinueStatement*) {
+void GeneratorImpl::EmitContinue(const ast::ContinueStatement*) {
     line() << "ContinueStatement []";
-    return true;
 }
 
-bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
+void GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
     {
         line() << "IfStatement [";
         {
@@ -1098,20 +877,14 @@
             line() << "condition: [";
             {
                 ScopedIndent cond(this);
-                if (!EmitExpression(stmt->condition)) {
-                    return false;
-                }
+                EmitExpression(stmt->condition);
             }
             line() << "]";
-            if (!EmitBlockHeader(stmt->body)) {
-                return false;
-            }
+            EmitBlockHeader(stmt->body);
         }
         line() << "] ";
     }
-    if (!EmitStatementsWithIndent(stmt->body->statements)) {
-        return false;
-    }
+    EmitStatementsWithIndent(stmt->body->statements);
 
     const ast::Statement* e = stmt->else_statement;
     while (e) {
@@ -1121,19 +894,13 @@
                 {
                     ScopedIndent ifs(this);
                     line() << "condition: [";
-                    if (!EmitExpression(elseif->condition)) {
-                        return false;
-                    }
+                    EmitExpression(elseif->condition);
                 }
                 line() << "]";
-                if (!EmitBlockHeader(elseif->body)) {
-                    return false;
-                }
+                EmitBlockHeader(elseif->body);
             }
             line() << "]";
-            if (!EmitStatementsWithIndent(elseif->body->statements)) {
-                return false;
-            }
+            EmitStatementsWithIndent(elseif->body->statements);
             e = elseif->else_statement;
         } else {
             auto* body = e->As<ast::BlockStatement>();
@@ -1141,80 +908,61 @@
                 line() << "Else [";
                 {
                     ScopedIndent els(this);
-                    if (!EmitBlockHeader(body)) {
-                        return false;
-                    }
+                    EmitBlockHeader(body);
                 }
                 line() << "]";
             }
-            if (!EmitStatementsWithIndent(body->statements)) {
-                return false;
-            }
+            EmitStatementsWithIndent(body->statements);
             break;
         }
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) {
+void GeneratorImpl::EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) {
     line() << "IncrementDecrementStatement [";
     {
         ScopedIndent ids(this);
         line() << "expr: [";
-        if (!EmitExpression(stmt->lhs)) {
-            return false;
-        }
+        EmitExpression(stmt->lhs);
         line() << "]";
         line() << "dir: " << (stmt->increment ? "++" : "--");
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) {
+void GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) {
     line() << "DiscardStatement []";
-    return true;
 }
 
-bool GeneratorImpl::EmitLoop(const ast::LoopStatement* stmt) {
+void GeneratorImpl::EmitLoop(const ast::LoopStatement* stmt) {
     line() << "LoopStatement [";
     {
         ScopedIndent ls(this);
-        if (!EmitStatements(stmt->body->statements)) {
-            return false;
-        }
+        EmitStatements(stmt->body->statements);
 
         if (stmt->continuing && !stmt->continuing->Empty()) {
             line() << "Continuing [";
             {
                 ScopedIndent cont(this);
-                if (!EmitStatementsWithIndent(stmt->continuing->statements)) {
-                    return false;
-                }
+                EmitStatementsWithIndent(stmt->continuing->statements);
             }
             line() << "]";
         }
     }
     line() << "]";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
+void GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
     TextBuffer init_buf;
     if (auto* init = stmt->initializer) {
         TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
-        if (!EmitStatement(init)) {
-            return false;
-        }
+        EmitStatement(init);
     }
 
     TextBuffer cont_buf;
     if (auto* cont = stmt->continuing) {
         TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
-        if (!EmitStatement(cont)) {
-            return false;
-        }
+        EmitStatement(cont);
     }
 
     line() << "ForLoopStatement [";
@@ -1244,9 +992,7 @@
         {
             ScopedIndent con(this);
             if (auto* cond = stmt->condition) {
-                if (!EmitExpression(cond)) {
-                    return false;
-                }
+                EmitExpression(cond);
             }
         }
 
@@ -1269,90 +1015,62 @@
                     break;
             }
         }
-        if (!EmitBlockHeader(stmt->body)) {
-            return false;
-        }
-
-        if (!EmitStatementsWithIndent(stmt->body->statements)) {
-            return false;
-        }
+        EmitBlockHeader(stmt->body);
+        EmitStatementsWithIndent(stmt->body->statements);
     }
     line() << "]";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
+void GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
     line() << "WhileStatement [";
     {
         ScopedIndent ws(this);
-        {
-            auto* cond = stmt->condition;
-            if (!EmitExpression(cond)) {
-                return false;
-            }
-        }
-        if (!EmitBlockHeader(stmt->body)) {
-            return false;
-        }
-        if (!EmitStatementsWithIndent(stmt->body->statements)) {
-            return false;
-        }
+        EmitExpression(stmt->condition);
+        EmitBlockHeader(stmt->body);
+        EmitStatementsWithIndent(stmt->body->statements);
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
+void GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
     line() << "ReturnStatement [";
     {
         ScopedIndent ret(this);
         if (stmt->value) {
-            if (!EmitExpression(stmt->value)) {
-                return false;
-            }
+            EmitExpression(stmt->value);
         }
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitConstAssert(const ast::ConstAssert* stmt) {
+void GeneratorImpl::EmitConstAssert(const ast::ConstAssert* stmt) {
     line() << "ConstAssert [";
     {
         ScopedIndent ca(this);
-        if (!EmitExpression(stmt->condition)) {
-            return false;
-        }
+        EmitExpression(stmt->condition);
     }
     line() << "]";
-    return true;
 }
 
-bool GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
+void GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
     line() << "SwitchStatement [";
     {
         ScopedIndent ss(this);
         line() << "condition: [";
         {
             ScopedIndent cond(this);
-            if (!EmitExpression(stmt->condition)) {
-                return false;
-            }
+            EmitExpression(stmt->condition);
         }
         line() << "]";
 
         {
             ScopedIndent si(this);
             for (auto* s : stmt->body) {
-                if (!EmitCase(s)) {
-                    return false;
-                }
+                EmitCase(s);
             }
         }
     }
     line() << "]";
-    return true;
 }
 
 }  // namespace tint::writer::syntax_tree
diff --git a/src/tint/writer/syntax_tree/generator_impl.h b/src/tint/writer/syntax_tree/generator_impl.h
index 8b9a187..dc2cf01 100644
--- a/src/tint/writer/syntax_tree/generator_impl.h
+++ b/src/tint/writer/syntax_tree/generator_impl.h
@@ -49,165 +49,125 @@
     ~GeneratorImpl();
 
     /// Generates the result data
-    /// @returns true on successful generation; false otherwise
-    bool Generate();
+    void Generate();
 
     /// Handles generating a diagnostic control
     /// @param diagnostic the diagnostic control node
-    /// @returns true if the diagnostic control was emitted
-    bool EmitDiagnosticControl(const ast::DiagnosticControl& diagnostic);
+    void EmitDiagnosticControl(const ast::DiagnosticControl& diagnostic);
     /// Handles generating an enable directive
     /// @param enable the enable node
-    /// @returns true if the enable directive was emitted
-    bool EmitEnable(const ast::Enable* enable);
+    void EmitEnable(const ast::Enable* enable);
     /// Handles generating a declared type
     /// @param ty the declared type to generate
-    /// @returns true if the declared type was emitted
-    bool EmitTypeDecl(const ast::TypeDecl* ty);
+    void EmitTypeDecl(const ast::TypeDecl* ty);
     /// Handles an index accessor expression
     /// @param expr the expression to emit
-    /// @returns true if the index accessor was emitted
-    bool EmitIndexAccessor(const ast::IndexAccessorExpression* expr);
+    void EmitIndexAccessor(const ast::IndexAccessorExpression* expr);
     /// Handles an assignment statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitAssign(const ast::AssignmentStatement* stmt);
+    void EmitAssign(const ast::AssignmentStatement* stmt);
     /// Handles generating a binary expression
     /// @param expr the binary expression
-    /// @returns true if the expression was emitted, false otherwise
-    bool EmitBinary(const ast::BinaryExpression* expr);
+    void EmitBinary(const ast::BinaryExpression* expr);
     /// Handles generating a binary operator
     /// @param op the binary operator
-    /// @returns true if the operator was emitted, false otherwise
-    bool EmitBinaryOp(const ast::BinaryOp op);
+    void EmitBinaryOp(const ast::BinaryOp op);
     /// Handles generating a bitcast expression
     /// @param expr the bitcast expression
-    /// @returns true if the bitcast was emitted
-    bool EmitBitcast(const ast::BitcastExpression* expr);
+    void EmitBitcast(const ast::BitcastExpression* expr);
     /// Handles a block statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBlock(const ast::BlockStatement* stmt);
+    void EmitBlock(const ast::BlockStatement* stmt);
     /// Handles emitting the start of a block statement (including attributes)
     /// @param stmt the block statement to emit the header for
-    /// @returns true if the statement was emitted successfully
-    bool EmitBlockHeader(const ast::BlockStatement* stmt);
+    void EmitBlockHeader(const ast::BlockStatement* stmt);
     /// Handles a break statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreak(const ast::BreakStatement* stmt);
+    void EmitBreak(const ast::BreakStatement* stmt);
     /// Handles a break-if statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreakIf(const ast::BreakIfStatement* stmt);
+    void EmitBreakIf(const ast::BreakIfStatement* stmt);
     /// Handles generating a call expression
     /// @param expr the call expression
-    /// @returns true if the call expression is emitted
-    bool EmitCall(const ast::CallExpression* expr);
+    void EmitCall(const ast::CallExpression* expr);
     /// Handles a case statement
     /// @param stmt the statement
-    /// @returns true if the statment was emitted successfully
-    bool EmitCase(const ast::CaseStatement* stmt);
+    void EmitCase(const ast::CaseStatement* stmt);
     /// Handles a compound assignment statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt);
+    void EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt);
     /// Handles generating a literal expression
     /// @param expr the literal expression expression
-    /// @returns true if the literal expression is emitted
-    bool EmitLiteral(const ast::LiteralExpression* expr);
+    void EmitLiteral(const ast::LiteralExpression* expr);
     /// Handles a continue statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitContinue(const ast::ContinueStatement* stmt);
+    void EmitContinue(const ast::ContinueStatement* stmt);
     /// Handles generate an Expression
     /// @param expr the expression
-    /// @returns true if the expression was emitted
-    bool EmitExpression(const ast::Expression* expr);
+    void EmitExpression(const ast::Expression* expr);
     /// Handles generating a function
     /// @param func the function to generate
-    /// @returns true if the function was emitted
-    bool EmitFunction(const ast::Function* func);
+    void EmitFunction(const ast::Function* func);
     /// Handles generating an identifier expression
     /// @param expr the identifier expression
-    /// @returns true if the identifier was emitted
-    bool EmitIdentifier(const ast::IdentifierExpression* expr);
+    void EmitIdentifier(const ast::IdentifierExpression* expr);
     /// Handles generating an identifier
     /// @param ident the identifier
-    /// @returns true if the identifier was emitted
-    bool EmitIdentifier(const ast::Identifier* ident);
+    void EmitIdentifier(const ast::Identifier* ident);
     /// Handles an if statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitIf(const ast::IfStatement* stmt);
+    void EmitIf(const ast::IfStatement* stmt);
     /// Handles an increment/decrement statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt);
+    void EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt);
     /// Handles generating a discard statement
     /// @param stmt the discard statement
-    /// @returns true if the statement was successfully emitted
-    bool EmitDiscard(const ast::DiscardStatement* stmt);
+    void EmitDiscard(const ast::DiscardStatement* stmt);
     /// Handles a loop statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emtited
-    bool EmitLoop(const ast::LoopStatement* stmt);
+    void EmitLoop(const ast::LoopStatement* stmt);
     /// Handles a for-loop statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emtited
-    bool EmitForLoop(const ast::ForLoopStatement* stmt);
+    void EmitForLoop(const ast::ForLoopStatement* stmt);
     /// Handles a while statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emtited
-    bool EmitWhile(const ast::WhileStatement* stmt);
+    void EmitWhile(const ast::WhileStatement* stmt);
     /// Handles a member accessor expression
     /// @param expr the member accessor expression
-    /// @returns true if the member accessor was emitted
-    bool EmitMemberAccessor(const ast::MemberAccessorExpression* expr);
+    void EmitMemberAccessor(const ast::MemberAccessorExpression* expr);
     /// Handles return statements
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitReturn(const ast::ReturnStatement* stmt);
+    void EmitReturn(const ast::ReturnStatement* stmt);
     /// Handles const assertion statements
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitConstAssert(const ast::ConstAssert* stmt);
+    void EmitConstAssert(const ast::ConstAssert* stmt);
     /// Handles statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitStatement(const ast::Statement* stmt);
+    void EmitStatement(const ast::Statement* stmt);
     /// Handles a statement list
     /// @param stmts the statements to emit
-    /// @returns true if the statements were emitted
-    bool EmitStatements(utils::VectorRef<const ast::Statement*> stmts);
+    void EmitStatements(utils::VectorRef<const ast::Statement*> stmts);
     /// Handles a statement list with an increased indentation
     /// @param stmts the statements to emit
-    /// @returns true if the statements were emitted
-    bool EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts);
+    void EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts);
     /// Handles generating a switch statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitSwitch(const ast::SwitchStatement* stmt);
+    void EmitSwitch(const ast::SwitchStatement* stmt);
     /// Handles generating a struct declaration
     /// @param str the struct
-    /// @returns true if the struct is emitted
-    bool EmitStructType(const ast::Struct* str);
+    void EmitStructType(const ast::Struct* str);
     /// Handles emitting an image format
     /// @param fmt the format to generate
-    /// @returns true if the format is emitted
-    bool EmitImageFormat(const builtin::TexelFormat fmt);
+    void EmitImageFormat(const builtin::TexelFormat fmt);
     /// Handles a unary op expression
     /// @param expr the expression to emit
-    /// @returns true if the expression was emitted
-    bool EmitUnaryOp(const ast::UnaryOpExpression* expr);
+    void EmitUnaryOp(const ast::UnaryOpExpression* expr);
     /// Handles generating a variable
     /// @param var the variable to generate
-    /// @returns true if the variable was emitted
-    bool EmitVariable(const ast::Variable* var);
+    void EmitVariable(const ast::Variable* var);
     /// Handles generating a attribute list
     /// @param attrs the attribute list
-    /// @returns true if the attributes were emitted
-    bool EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs);
+    void EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs);
 };
 
 }  // namespace tint::writer::syntax_tree
diff --git a/src/tint/writer/text_generator.h b/src/tint/writer/text_generator.h
index 2bd725f..34b93d4 100644
--- a/src/tint/writer/text_generator.h
+++ b/src/tint/writer/text_generator.h
@@ -102,9 +102,6 @@
     /// @returns the list of diagnostics raised by the generator.
     const diag::List& Diagnostics() const { return diagnostics_; }
 
-    /// @returns the error
-    std::string error() const { return diagnostics_.str(); }
-
     /// @return a new, unique identifier with the given prefix.
     /// @param prefix optional prefix to apply to the generated identifier. If
     /// empty "tint_symbol" will be used.
diff --git a/src/tint/writer/wgsl/generator.cc b/src/tint/writer/wgsl/generator.cc
index 623f3a6..f1f3f68 100644
--- a/src/tint/writer/wgsl/generator.cc
+++ b/src/tint/writer/wgsl/generator.cc
@@ -26,8 +26,9 @@
 
     // Generate the WGSL code.
     auto impl = std::make_unique<GeneratorImpl>(program);
-    result.success = impl->Generate();
-    result.error = impl->error();
+    impl->Generate();
+    result.success = impl->Diagnostics().empty();
+    result.error = impl->Diagnostics().str();
     result.wgsl = impl->result();
 
     return result;
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index e1bf9fc..744bbd2 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -46,20 +46,16 @@
 
 GeneratorImpl::~GeneratorImpl() = default;
 
-bool GeneratorImpl::Generate() {
+void GeneratorImpl::Generate() {
     // Generate directives before any other global declarations.
     bool has_directives = false;
     for (auto enable : program_->AST().Enables()) {
-        if (!EmitEnable(enable)) {
-            return false;
-        }
+        EmitEnable(enable);
         has_directives = true;
     }
     for (auto diagnostic : program_->AST().DiagnosticDirectives()) {
         auto out = line();
-        if (!EmitDiagnosticControl(out, diagnostic->control)) {
-            return false;
-        }
+        EmitDiagnosticControl(out, diagnostic->control);
         out << ";";
         has_directives = true;
     }
@@ -71,34 +67,26 @@
         if (decl->IsAnyOf<ast::DiagnosticDirective, ast::Enable>()) {
             continue;
         }
-        if (!Switch(
-                decl,  //
-                [&](const ast::TypeDecl* td) { return EmitTypeDecl(td); },
-                [&](const ast::Function* func) { return EmitFunction(func); },
-                [&](const ast::Variable* var) { return EmitVariable(line(), var); },
-                [&](const ast::ConstAssert* ca) { return EmitConstAssert(ca); },
-                [&](Default) {
-                    TINT_UNREACHABLE(Writer, diagnostics_);
-                    return false;
-                })) {
-            return false;
-        }
+        Switch(
+            decl,  //
+            [&](const ast::TypeDecl* td) { return EmitTypeDecl(td); },
+            [&](const ast::Function* func) { return EmitFunction(func); },
+            [&](const ast::Variable* var) { return EmitVariable(line(), var); },
+            [&](const ast::ConstAssert* ca) { return EmitConstAssert(ca); },
+            [&](Default) { TINT_UNREACHABLE(Writer, diagnostics_); });
         if (decl != program_->AST().GlobalDeclarations().Back()) {
             line();
         }
     }
-
-    return true;
 }
 
-bool GeneratorImpl::EmitDiagnosticControl(utils::StringStream& out,
+void GeneratorImpl::EmitDiagnosticControl(utils::StringStream& out,
                                           const ast::DiagnosticControl& diagnostic) {
     out << "diagnostic(" << diagnostic.severity << ", "
         << program_->Symbols().NameFor(diagnostic.rule_name->symbol) << ")";
-    return true;
 }
 
-bool GeneratorImpl::EmitEnable(const ast::Enable* enable) {
+void GeneratorImpl::EmitEnable(const ast::Enable* enable) {
     auto out = line();
     out << "enable ";
     for (auto* ext : enable->extensions) {
@@ -108,69 +96,40 @@
         out << ext->name;
     }
     out << ";";
-    return true;
 }
 
-bool GeneratorImpl::EmitTypeDecl(const ast::TypeDecl* ty) {
-    return Switch(
-        ty,
-        [&](const ast::Alias* alias) {  //
+void GeneratorImpl::EmitTypeDecl(const ast::TypeDecl* ty) {
+    Switch(
+        ty,  //
+        [&](const ast::Alias* alias) {
             auto out = line();
             out << "alias " << program_->Symbols().NameFor(alias->name->symbol) << " = ";
-            if (!EmitExpression(out, alias->type)) {
-                return false;
-            }
+            EmitExpression(out, alias->type);
             out << ";";
-            return true;
         },
-        [&](const ast::Struct* str) {  //
-            return EmitStructType(str);
-        },
-        [&](Default) {  //
+        [&](const ast::Struct* str) { EmitStructType(str); },
+        [&](Default) {
             diagnostics_.add_error(diag::System::Writer,
                                    "unknown declared type: " + std::string(ty->TypeInfo().name));
-            return false;
         });
 }
 
-bool GeneratorImpl::EmitExpression(utils::StringStream& out, const ast::Expression* expr) {
-    return Switch(
-        expr,
-        [&](const ast::IndexAccessorExpression* a) {  //
-            return EmitIndexAccessor(out, a);
-        },
-        [&](const ast::BinaryExpression* b) {  //
-            return EmitBinary(out, b);
-        },
-        [&](const ast::BitcastExpression* b) {  //
-            return EmitBitcast(out, b);
-        },
-        [&](const ast::CallExpression* c) {  //
-            return EmitCall(out, c);
-        },
-        [&](const ast::IdentifierExpression* i) {  //
-            return EmitIdentifier(out, i);
-        },
-        [&](const ast::LiteralExpression* l) {  //
-            return EmitLiteral(out, l);
-        },
-        [&](const ast::MemberAccessorExpression* m) {  //
-            return EmitMemberAccessor(out, m);
-        },
-        [&](const ast::PhonyExpression*) {  //
-            out << "_";
-            return true;
-        },
-        [&](const ast::UnaryOpExpression* u) {  //
-            return EmitUnaryOp(out, u);
-        },
-        [&](Default) {
-            diagnostics_.add_error(diag::System::Writer, "unknown expression type");
-            return false;
-        });
+void GeneratorImpl::EmitExpression(utils::StringStream& out, const ast::Expression* expr) {
+    Switch(
+        expr,  //
+        [&](const ast::IndexAccessorExpression* a) { EmitIndexAccessor(out, a); },
+        [&](const ast::BinaryExpression* b) { EmitBinary(out, b); },
+        [&](const ast::BitcastExpression* b) { EmitBitcast(out, b); },
+        [&](const ast::CallExpression* c) { EmitCall(out, c); },
+        [&](const ast::IdentifierExpression* i) { EmitIdentifier(out, i); },
+        [&](const ast::LiteralExpression* l) { EmitLiteral(out, l); },
+        [&](const ast::MemberAccessorExpression* m) { EmitMemberAccessor(out, m); },
+        [&](const ast::PhonyExpression*) { out << "_"; },
+        [&](const ast::UnaryOpExpression* u) { EmitUnaryOp(out, u); },
+        [&](Default) { diagnostics_.add_error(diag::System::Writer, "unknown expression type"); });
 }
 
-bool GeneratorImpl::EmitIndexAccessor(utils::StringStream& out,
+void GeneratorImpl::EmitIndexAccessor(utils::StringStream& out,
                                       const ast::IndexAccessorExpression* expr) {
     bool paren_lhs =
         !expr->object
@@ -178,23 +137,17 @@
     if (paren_lhs) {
         out << "(";
     }
-    if (!EmitExpression(out, expr->object)) {
-        return false;
-    }
+    EmitExpression(out, expr->object);
     if (paren_lhs) {
         out << ")";
     }
     out << "[";
 
-    if (!EmitExpression(out, expr->index)) {
-        return false;
-    }
+    EmitExpression(out, expr->index);
     out << "]";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitMemberAccessor(utils::StringStream& out,
+void GeneratorImpl::EmitMemberAccessor(utils::StringStream& out,
                                        const ast::MemberAccessorExpression* expr) {
     bool paren_lhs =
         !expr->object
@@ -202,36 +155,25 @@
     if (paren_lhs) {
         out << "(";
     }
-    if (!EmitExpression(out, expr->object)) {
-        return false;
-    }
+    EmitExpression(out, expr->object);
     if (paren_lhs) {
         out << ")";
     }
 
     out << "." << program_->Symbols().NameFor(expr->member->symbol);
-    return true;
 }
 
-bool GeneratorImpl::EmitBitcast(utils::StringStream& out, const ast::BitcastExpression* expr) {
+void GeneratorImpl::EmitBitcast(utils::StringStream& out, const ast::BitcastExpression* expr) {
     out << "bitcast<";
-    if (!EmitExpression(out, expr->type)) {
-        return false;
-    }
+    EmitExpression(out, expr->type);
 
     out << ">(";
-    if (!EmitExpression(out, expr->expr)) {
-        return false;
-    }
-
+    EmitExpression(out, expr->expr);
     out << ")";
-    return true;
 }
 
-bool GeneratorImpl::EmitCall(utils::StringStream& out, const ast::CallExpression* expr) {
-    if (!EmitExpression(out, expr->target)) {
-        return false;
-    }
+void GeneratorImpl::EmitCall(utils::StringStream& out, const ast::CallExpression* expr) {
+    EmitExpression(out, expr->target);
     out << "(";
 
     bool first = true;
@@ -242,24 +184,16 @@
         }
         first = false;
 
-        if (!EmitExpression(out, arg)) {
-            return false;
-        }
+        EmitExpression(out, arg);
     }
-
     out << ")";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitLiteral(utils::StringStream& out, const ast::LiteralExpression* lit) {
-    return Switch(
-        lit,
-        [&](const ast::BoolLiteralExpression* l) {  //
-            out << (l->value ? "true" : "false");
-            return true;
-        },
-        [&](const ast::FloatLiteralExpression* l) {  //
+void GeneratorImpl::EmitLiteral(utils::StringStream& out, const ast::LiteralExpression* lit) {
+    Switch(
+        lit,  //
+        [&](const ast::BoolLiteralExpression* l) { out << (l->value ? "true" : "false"); },
+        [&](const ast::FloatLiteralExpression* l) {
             // f16 literals are also emitted as float value with suffix "h".
             // Note that all normal and subnormal f16 values are normal f32 values, and since NaN
             // and Inf are not allowed to be spelled in literal, it should be fine to emit f16
@@ -269,24 +203,17 @@
             } else {
                 out << FloatToBitPreservingString(static_cast<float>(l->value)) << l->suffix;
             }
-            return true;
         },
-        [&](const ast::IntLiteralExpression* l) {  //
-            out << l->value << l->suffix;
-            return true;
-        },
-        [&](Default) {  //
-            diagnostics_.add_error(diag::System::Writer, "unknown literal type");
-            return false;
-        });
+        [&](const ast::IntLiteralExpression* l) { out << l->value << l->suffix; },
+        [&](Default) { diagnostics_.add_error(diag::System::Writer, "unknown literal type"); });
 }
 
-bool GeneratorImpl::EmitIdentifier(utils::StringStream& out,
+void GeneratorImpl::EmitIdentifier(utils::StringStream& out,
                                    const ast::IdentifierExpression* expr) {
-    return EmitIdentifier(out, expr->identifier);
+    EmitIdentifier(out, expr->identifier);
 }
 
-bool GeneratorImpl::EmitIdentifier(utils::StringStream& out, const ast::Identifier* ident) {
+void GeneratorImpl::EmitIdentifier(utils::StringStream& out, const ast::Identifier* ident) {
     if (auto* tmpl_ident = ident->As<ast::TemplatedIdentifier>()) {
         if (!tmpl_ident->attributes.IsEmpty()) {
             EmitAttributes(out, tmpl_ident->attributes);
@@ -298,21 +225,16 @@
             if (expr != tmpl_ident->arguments.Front()) {
                 out << ", ";
             }
-            if (!EmitExpression(out, expr)) {
-                return false;
-            }
+            EmitExpression(out, expr);
         }
     } else {
         out << program_->Symbols().NameFor(ident->symbol);
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitFunction(const ast::Function* func) {
+void GeneratorImpl::EmitFunction(const ast::Function* func) {
     if (func->attributes.Length()) {
-        if (!EmitAttributes(line(), func->attributes)) {
-            return false;
-        }
+        EmitAttributes(line(), func->attributes);
     }
     {
         auto out = line();
@@ -326,17 +248,13 @@
             first = false;
 
             if (!v->attributes.IsEmpty()) {
-                if (!EmitAttributes(out, v->attributes)) {
-                    return false;
-                }
+                EmitAttributes(out, v->attributes);
                 out << " ";
             }
 
             out << program_->Symbols().NameFor(v->name->symbol) << " : ";
 
-            if (!EmitExpression(out, v->type)) {
-                return false;
-            }
+            EmitExpression(out, v->type);
         }
 
         out << ")";
@@ -345,51 +263,39 @@
             out << " -> ";
 
             if (!func->return_type_attributes.IsEmpty()) {
-                if (!EmitAttributes(out, func->return_type_attributes)) {
-                    return false;
-                }
+                EmitAttributes(out, func->return_type_attributes);
                 out << " ";
             }
 
-            if (!EmitExpression(out, func->return_type)) {
-                return false;
-            }
+            EmitExpression(out, func->return_type);
         }
 
         if (func->body) {
             out << " ";
-            if (!EmitBlockHeader(out, func->body)) {
-                return false;
-            }
+            EmitBlockHeader(out, func->body);
         }
     }
 
     if (func->body) {
-        if (!EmitStatementsWithIndent(func->body->statements)) {
-            return false;
-        }
+        EmitStatementsWithIndent(func->body->statements);
         line() << "}";
     }
-
-    return true;
 }
 
-bool GeneratorImpl::EmitImageFormat(utils::StringStream& out, const builtin::TexelFormat fmt) {
+void GeneratorImpl::EmitImageFormat(utils::StringStream& out, const builtin::TexelFormat fmt) {
     switch (fmt) {
         case builtin::TexelFormat::kUndefined:
             diagnostics_.add_error(diag::System::Writer, "unknown image format");
-            return false;
+            break;
         default:
             out << fmt;
+            break;
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitStructType(const ast::Struct* str) {
+void GeneratorImpl::EmitStructType(const ast::Struct* str) {
     if (str->attributes.Length()) {
-        if (!EmitAttributes(line(), str->attributes)) {
-            return false;
-        }
+        EmitAttributes(line(), str->attributes);
     }
     line() << "struct " << program_->Symbols().NameFor(str->name->symbol) << " {";
 
@@ -424,9 +330,7 @@
             if (attr->Is<ast::StructMemberOffsetAttribute>()) {
                 auto l = line();
                 l << "/* ";
-                if (!EmitAttributes(l, utils::Vector{attr})) {
-                    return false;
-                }
+                EmitAttributes(l, utils::Vector{attr});
                 l << " */";
             } else {
                 attributes_sanitized.Push(attr);
@@ -434,92 +338,60 @@
         }
 
         if (!attributes_sanitized.IsEmpty()) {
-            if (!EmitAttributes(line(), attributes_sanitized)) {
-                return false;
-            }
+            EmitAttributes(line(), attributes_sanitized);
         }
 
         auto out = line();
         out << program_->Symbols().NameFor(mem->name->symbol) << " : ";
-        if (!EmitExpression(out, mem->type)) {
-            return false;
-        }
+        EmitExpression(out, mem->type);
         out << ",";
     }
     decrement_indent();
 
     line() << "}";
-    return true;
 }
 
-bool GeneratorImpl::EmitVariable(utils::StringStream& out, const ast::Variable* v) {
+void GeneratorImpl::EmitVariable(utils::StringStream& out, const ast::Variable* v) {
     if (!v->attributes.IsEmpty()) {
-        if (!EmitAttributes(out, v->attributes)) {
-            return false;
-        }
+        EmitAttributes(out, v->attributes);
         out << " ";
     }
 
-    bool ok = Switch(
+    Switch(
         v,  //
         [&](const ast::Var* var) {
             out << "var";
             if (var->declared_address_space || var->declared_access) {
                 out << "<";
                 TINT_DEFER(out << ">");
-                if (!EmitExpression(out, var->declared_address_space)) {
-                    return false;
-                }
+                EmitExpression(out, var->declared_address_space);
                 if (var->declared_access) {
                     out << ", ";
-                    if (!EmitExpression(out, var->declared_access)) {
-                        return false;
-                    }
+                    EmitExpression(out, var->declared_access);
                 }
             }
-            return true;
         },
-        [&](const ast::Let*) {
-            out << "let";
-            return true;
-        },
-        [&](const ast::Override*) {
-            out << "override";
-            return true;
-        },
-        [&](const ast::Const*) {
-            out << "const";
-            return true;
-        },
+        [&](const ast::Let*) { out << "let"; }, [&](const ast::Override*) { out << "override"; },
+        [&](const ast::Const*) { out << "const"; },
         [&](Default) {
             TINT_ICE(Writer, diagnostics_) << "unhandled variable type " << v->TypeInfo().name;
-            return false;
         });
-    if (!ok) {
-        return false;
-    }
 
     out << " " << program_->Symbols().NameFor(v->name->symbol);
 
     if (auto ty = v->type) {
         out << " : ";
-        if (!EmitExpression(out, ty)) {
-            return false;
-        }
+        EmitExpression(out, ty);
     }
 
     if (v->initializer != nullptr) {
         out << " = ";
-        if (!EmitExpression(out, v->initializer)) {
-            return false;
-        }
+        EmitExpression(out, v->initializer);
     }
     out << ";";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitAttributes(utils::StringStream& out,
+void GeneratorImpl::EmitAttributes(utils::StringStream& out,
                                    utils::VectorRef<const ast::Attribute*> attrs) {
     bool first = true;
     for (auto* attr : attrs) {
@@ -528,8 +400,8 @@
         }
         first = false;
         out << "@";
-        bool ok = Switch(
-            attr,
+        Switch(
+            attr,  //
             [&](const ast::WorkgroupAttribute* workgroup) {
                 auto values = workgroup->Values();
                 out << "workgroup_size(";
@@ -538,150 +410,90 @@
                         if (i > 0) {
                             out << ", ";
                         }
-                        if (!EmitExpression(out, values[i])) {
-                            return false;
-                        }
+                        EmitExpression(out, values[i]);
                     }
                 }
                 out << ")";
-                return true;
             },
-            [&](const ast::StageAttribute* stage) {
-                out << stage->stage;
-                return true;
-            },
+            [&](const ast::StageAttribute* stage) { out << stage->stage; },
             [&](const ast::BindingAttribute* binding) {
                 out << "binding(";
-                if (!EmitExpression(out, binding->expr)) {
-                    return false;
-                }
+                EmitExpression(out, binding->expr);
                 out << ")";
-                return true;
             },
             [&](const ast::GroupAttribute* group) {
                 out << "group(";
-                if (!EmitExpression(out, group->expr)) {
-                    return false;
-                }
+                EmitExpression(out, group->expr);
                 out << ")";
-                return true;
             },
             [&](const ast::LocationAttribute* location) {
                 out << "location(";
-                if (!EmitExpression(out, location->expr)) {
-                    return false;
-                }
+                EmitExpression(out, location->expr);
                 out << ")";
-                return true;
             },
             [&](const ast::BuiltinAttribute* builtin) {
                 out << "builtin(";
-                if (!EmitExpression(out, builtin->builtin)) {
-                    return false;
-                }
+                EmitExpression(out, builtin->builtin);
                 out << ")";
-                return true;
             },
             [&](const ast::DiagnosticAttribute* diagnostic) {
-                return EmitDiagnosticControl(out, diagnostic->control);
+                EmitDiagnosticControl(out, diagnostic->control);
             },
             [&](const ast::InterpolateAttribute* interpolate) {
                 out << "interpolate(";
-                if (!EmitExpression(out, interpolate->type)) {
-                    return false;
-                }
+                EmitExpression(out, interpolate->type);
                 if (interpolate->sampling) {
                     out << ", ";
-                    if (!EmitExpression(out, interpolate->sampling)) {
-                        return false;
-                    }
+                    EmitExpression(out, interpolate->sampling);
                 }
                 out << ")";
-                return true;
             },
-            [&](const ast::InvariantAttribute*) {
-                out << "invariant";
-                return true;
-            },
+            [&](const ast::InvariantAttribute*) { out << "invariant"; },
             [&](const ast::IdAttribute* override_deco) {
                 out << "id(";
-                if (!EmitExpression(out, override_deco->expr)) {
-                    return false;
-                }
+                EmitExpression(out, override_deco->expr);
                 out << ")";
-                return true;
             },
-            [&](const ast::MustUseAttribute*) {
-                out << "must_use";
-                return true;
-            },
+            [&](const ast::MustUseAttribute*) { out << "must_use"; },
             [&](const ast::StructMemberOffsetAttribute* offset) {
                 out << "offset(";
-                if (!EmitExpression(out, offset->expr)) {
-                    return false;
-                }
+                EmitExpression(out, offset->expr);
                 out << ")";
-                return true;
             },
             [&](const ast::StructMemberSizeAttribute* size) {
                 out << "size(";
-                if (!EmitExpression(out, size->expr)) {
-                    return false;
-                }
+                EmitExpression(out, size->expr);
                 out << ")";
-                return true;
             },
             [&](const ast::StructMemberAlignAttribute* align) {
                 out << "align(";
-                if (!EmitExpression(out, align->expr)) {
-                    return false;
-                }
+                EmitExpression(out, align->expr);
                 out << ")";
-                return true;
             },
-            [&](const ast::StrideAttribute* stride) {
-                out << "stride(" << stride->stride << ")";
-                return true;
-            },
+            [&](const ast::StrideAttribute* stride) { out << "stride(" << stride->stride << ")"; },
             [&](const ast::InternalAttribute* internal) {
                 out << "internal(" << internal->InternalName() << ")";
-                return true;
             },
             [&](Default) {
                 TINT_ICE(Writer, diagnostics_)
                     << "Unsupported attribute '" << attr->TypeInfo().name << "'";
-                return false;
             });
-
-        if (!ok) {
-            return false;
-        }
     }
-
-    return true;
 }
 
-bool GeneratorImpl::EmitBinary(utils::StringStream& out, const ast::BinaryExpression* expr) {
+void GeneratorImpl::EmitBinary(utils::StringStream& out, const ast::BinaryExpression* expr) {
     out << "(";
 
-    if (!EmitExpression(out, expr->lhs)) {
-        return false;
-    }
+    EmitExpression(out, expr->lhs);
     out << " ";
-    if (!EmitBinaryOp(out, expr->op)) {
-        return false;
-    }
+    EmitBinaryOp(out, expr->op);
     out << " ";
 
-    if (!EmitExpression(out, expr->rhs)) {
-        return false;
-    }
-
+    EmitExpression(out, expr->rhs);
     out << ")";
-    return true;
 }
 
-bool GeneratorImpl::EmitBinaryOp(utils::StringStream& out, const ast::BinaryOp op) {
+void GeneratorImpl::EmitBinaryOp(utils::StringStream& out, const ast::BinaryOp op) {
     switch (op) {
         case ast::BinaryOp::kAnd:
             out << "&";
@@ -739,12 +551,11 @@
             break;
         case ast::BinaryOp::kNone:
             diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
-            return false;
+            break;
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitUnaryOp(utils::StringStream& out, const ast::UnaryOpExpression* expr) {
+void GeneratorImpl::EmitUnaryOp(utils::StringStream& out, const ast::UnaryOpExpression* expr) {
     switch (expr->op) {
         case ast::UnaryOp::kAddressOf:
             out << "&";
@@ -763,131 +574,94 @@
             break;
     }
     out << "(";
-
-    if (!EmitExpression(out, expr->expr)) {
-        return false;
-    }
-
+    EmitExpression(out, expr->expr);
     out << ")";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
+void GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
     {
         auto out = line();
-        if (!EmitBlockHeader(out, stmt)) {
-            return false;
-        }
+        EmitBlockHeader(out, stmt);
     }
-    if (!EmitStatementsWithIndent(stmt->statements)) {
-        return false;
-    }
+    EmitStatementsWithIndent(stmt->statements);
     line() << "}";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitBlockHeader(utils::StringStream& out, const ast::BlockStatement* stmt) {
+void GeneratorImpl::EmitBlockHeader(utils::StringStream& out, const ast::BlockStatement* stmt) {
     if (!stmt->attributes.IsEmpty()) {
-        if (!EmitAttributes(out, stmt->attributes)) {
-            return false;
-        }
+        EmitAttributes(out, stmt->attributes);
         out << " ";
     }
     out << "{";
-    return true;
 }
 
-bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
-    return Switch(
+void GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
+    Switch(
         stmt,  //
-        [&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
-        [&](const ast::BlockStatement* b) { return EmitBlock(b); },
-        [&](const ast::BreakStatement* b) { return EmitBreak(b); },
-        [&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
+        [&](const ast::AssignmentStatement* a) { EmitAssign(a); },
+        [&](const ast::BlockStatement* b) { EmitBlock(b); },
+        [&](const ast::BreakStatement* b) { EmitBreak(b); },
+        [&](const ast::BreakIfStatement* b) { EmitBreakIf(b); },
         [&](const ast::CallStatement* c) {
             auto out = line();
-            if (!EmitCall(out, c->expr)) {
-                return false;
-            }
+            EmitCall(out, c->expr);
             out << ";";
-            return true;
         },
-        [&](const ast::CompoundAssignmentStatement* c) { return EmitCompoundAssign(c); },
-        [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
-        [&](const ast::DiscardStatement* d) { return EmitDiscard(d); },
-        [&](const ast::IfStatement* i) { return EmitIf(i); },
-        [&](const ast::IncrementDecrementStatement* l) { return EmitIncrementDecrement(l); },
-        [&](const ast::LoopStatement* l) { return EmitLoop(l); },
-        [&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
-        [&](const ast::WhileStatement* l) { return EmitWhile(l); },
-        [&](const ast::ReturnStatement* r) { return EmitReturn(r); },
-        [&](const ast::ConstAssert* c) { return EmitConstAssert(c); },
-        [&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
-        [&](const ast::VariableDeclStatement* v) { return EmitVariable(line(), v->variable); },
+        [&](const ast::CompoundAssignmentStatement* c) { EmitCompoundAssign(c); },
+        [&](const ast::ContinueStatement* c) { EmitContinue(c); },
+        [&](const ast::DiscardStatement* d) { EmitDiscard(d); },
+        [&](const ast::IfStatement* i) { EmitIf(i); },
+        [&](const ast::IncrementDecrementStatement* l) { EmitIncrementDecrement(l); },
+        [&](const ast::LoopStatement* l) { EmitLoop(l); },
+        [&](const ast::ForLoopStatement* l) { EmitForLoop(l); },
+        [&](const ast::WhileStatement* l) { EmitWhile(l); },
+        [&](const ast::ReturnStatement* r) { EmitReturn(r); },
+        [&](const ast::ConstAssert* c) { EmitConstAssert(c); },
+        [&](const ast::SwitchStatement* s) { EmitSwitch(s); },
+        [&](const ast::VariableDeclStatement* v) { EmitVariable(line(), v->variable); },
         [&](Default) {
             diagnostics_.add_error(diag::System::Writer,
                                    "unknown statement type: " + std::string(stmt->TypeInfo().name));
-            return false;
         });
 }
 
-bool GeneratorImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
+void GeneratorImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
     for (auto* s : stmts) {
-        if (!EmitStatement(s)) {
-            return false;
-        }
+        EmitStatement(s);
     }
-    return true;
 }
 
-bool GeneratorImpl::EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts) {
+void GeneratorImpl::EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts) {
     ScopedIndent si(this);
-    return EmitStatements(stmts);
+    EmitStatements(stmts);
 }
 
-bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) {
+void GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) {
     auto out = line();
 
-    if (!EmitExpression(out, stmt->lhs)) {
-        return false;
-    }
-
+    EmitExpression(out, stmt->lhs);
     out << " = ";
-
-    if (!EmitExpression(out, stmt->rhs)) {
-        return false;
-    }
-
+    EmitExpression(out, stmt->rhs);
     out << ";";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
+void GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
     line() << "break;";
-    return true;
 }
 
-bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
+void GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
     auto out = line();
 
     out << "break if ";
-    if (!EmitExpression(out, b->condition)) {
-        return false;
-    }
+    EmitExpression(out, b->condition);
     out << ";";
-    return true;
 }
 
-bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
+void GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
     if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
         auto out = line();
         out << "default: ";
-        if (!EmitBlockHeader(out, stmt->body)) {
-            return false;
-        }
+        EmitBlockHeader(out, stmt->body);
     } else {
         auto out = line();
         out << "case ";
@@ -902,74 +676,48 @@
 
             if (sel->IsDefault()) {
                 out << "default";
-            } else if (!EmitExpression(out, sel->expr)) {
-                return false;
+            } else {
+                EmitExpression(out, sel->expr);
             }
         }
         out << ": ";
-        if (!EmitBlockHeader(out, stmt->body)) {
-            return false;
-        }
+        EmitBlockHeader(out, stmt->body);
     }
-    if (!EmitStatementsWithIndent(stmt->body->statements)) {
-        return false;
-    }
-
+    EmitStatementsWithIndent(stmt->body->statements);
     line() << "}";
-    return true;
 }
 
-bool GeneratorImpl::EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt) {
+void GeneratorImpl::EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt) {
     auto out = line();
 
-    if (!EmitExpression(out, stmt->lhs)) {
-        return false;
-    }
-
+    EmitExpression(out, stmt->lhs);
     out << " ";
-    if (!EmitBinaryOp(out, stmt->op)) {
-        return false;
-    }
+    EmitBinaryOp(out, stmt->op);
     out << "= ";
-
-    if (!EmitExpression(out, stmt->rhs)) {
-        return false;
-    }
-
+    EmitExpression(out, stmt->rhs);
     out << ";";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitContinue(const ast::ContinueStatement*) {
+void GeneratorImpl::EmitContinue(const ast::ContinueStatement*) {
     line() << "continue;";
-    return true;
 }
 
-bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
+void GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
     {
         auto out = line();
 
         if (!stmt->attributes.IsEmpty()) {
-            if (!EmitAttributes(out, stmt->attributes)) {
-                return false;
-            }
+            EmitAttributes(out, stmt->attributes);
             out << " ";
         }
 
         out << "if (";
-        if (!EmitExpression(out, stmt->condition)) {
-            return false;
-        }
+        EmitExpression(out, stmt->condition);
         out << ") ";
-        if (!EmitBlockHeader(out, stmt->body)) {
-            return false;
-        }
+        EmitBlockHeader(out, stmt->body);
     }
 
-    if (!EmitStatementsWithIndent(stmt->body->statements)) {
-        return false;
-    }
+    EmitStatementsWithIndent(stmt->body->statements);
 
     const ast::Statement* e = stmt->else_statement;
     while (e) {
@@ -977,74 +725,52 @@
             {
                 auto out = line();
                 out << "} else if (";
-                if (!EmitExpression(out, elseif->condition)) {
-                    return false;
-                }
+                EmitExpression(out, elseif->condition);
                 out << ") ";
-                if (!EmitBlockHeader(out, elseif->body)) {
-                    return false;
-                }
+                EmitBlockHeader(out, elseif->body);
             }
-            if (!EmitStatementsWithIndent(elseif->body->statements)) {
-                return false;
-            }
+            EmitStatementsWithIndent(elseif->body->statements);
             e = elseif->else_statement;
         } else {
             auto* body = e->As<ast::BlockStatement>();
             {
                 auto out = line();
                 out << "} else ";
-                if (!EmitBlockHeader(out, body)) {
-                    return false;
-                }
+                EmitBlockHeader(out, body);
             }
-            if (!EmitStatementsWithIndent(body->statements)) {
-                return false;
-            }
+            EmitStatementsWithIndent(body->statements);
             break;
         }
     }
 
     line() << "}";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) {
+void GeneratorImpl::EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) {
     auto out = line();
-    if (!EmitExpression(out, stmt->lhs)) {
-        return false;
-    }
+    EmitExpression(out, stmt->lhs);
     out << (stmt->increment ? "++" : "--") << ";";
-    return true;
 }
 
-bool GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) {
+void GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) {
     line() << "discard;";
-    return true;
 }
 
-bool GeneratorImpl::EmitLoop(const ast::LoopStatement* stmt) {
+void GeneratorImpl::EmitLoop(const ast::LoopStatement* stmt) {
     {
         auto out = line();
 
         if (!stmt->attributes.IsEmpty()) {
-            if (!EmitAttributes(out, stmt->attributes)) {
-                return false;
-            }
+            EmitAttributes(out, stmt->attributes);
             out << " ";
         }
 
         out << "loop ";
-        if (!EmitBlockHeader(out, stmt->body)) {
-            return false;
-        }
+        EmitBlockHeader(out, stmt->body);
     }
     increment_indent();
 
-    if (!EmitStatements(stmt->body->statements)) {
-        return false;
-    }
+    EmitStatements(stmt->body->statements);
 
     if (stmt->continuing && !stmt->continuing->Empty()) {
         line();
@@ -1052,49 +778,37 @@
             auto out = line();
             out << "continuing ";
             if (!stmt->continuing->attributes.IsEmpty()) {
-                if (!EmitAttributes(out, stmt->continuing->attributes)) {
-                    return false;
-                }
+                EmitAttributes(out, stmt->continuing->attributes);
                 out << " ";
             }
             out << "{";
         }
-        if (!EmitStatementsWithIndent(stmt->continuing->statements)) {
-            return false;
-        }
+        EmitStatementsWithIndent(stmt->continuing->statements);
         line() << "}";
     }
 
     decrement_indent();
     line() << "}";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
+void GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
     TextBuffer init_buf;
     if (auto* init = stmt->initializer) {
         TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
-        if (!EmitStatement(init)) {
-            return false;
-        }
+        EmitStatement(init);
     }
 
     TextBuffer cont_buf;
     if (auto* cont = stmt->continuing) {
         TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
-        if (!EmitStatement(cont)) {
-            return false;
-        }
+        EmitStatement(cont);
     }
 
     {
         auto out = line();
 
         if (!stmt->attributes.IsEmpty()) {
-            if (!EmitAttributes(out, stmt->attributes)) {
-                return false;
-            }
+            EmitAttributes(out, stmt->attributes);
             out << " ";
         }
 
@@ -1119,9 +833,7 @@
             out << "; ";
 
             if (auto* cond = stmt->condition) {
-                if (!EmitExpression(out, cond)) {
-                    return false;
-                }
+                EmitExpression(out, cond);
             }
 
             out << "; ";
@@ -1142,28 +854,20 @@
             }
         }
         out << " ";
-        if (!EmitBlockHeader(out, stmt->body)) {
-            return false;
-        }
+        EmitBlockHeader(out, stmt->body);
     }
 
-    if (!EmitStatementsWithIndent(stmt->body->statements)) {
-        return false;
-    }
+    EmitStatementsWithIndent(stmt->body->statements);
 
     line() << "}";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
+void GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
     {
         auto out = line();
 
         if (!stmt->attributes.IsEmpty()) {
-            if (!EmitAttributes(out, stmt->attributes)) {
-                return false;
-            }
+            EmitAttributes(out, stmt->attributes);
             out << " ";
         }
 
@@ -1172,69 +876,50 @@
             ScopedParen sp(out);
 
             auto* cond = stmt->condition;
-            if (!EmitExpression(out, cond)) {
-                return false;
-            }
+            EmitExpression(out, cond);
         }
         out << " ";
-        if (!EmitBlockHeader(out, stmt->body)) {
-            return false;
-        }
+        EmitBlockHeader(out, stmt->body);
     }
 
-    if (!EmitStatementsWithIndent(stmt->body->statements)) {
-        return false;
-    }
+    EmitStatementsWithIndent(stmt->body->statements);
 
     line() << "}";
-
-    return true;
 }
 
-bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
+void GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
     auto out = line();
+
     out << "return";
     if (stmt->value) {
         out << " ";
-        if (!EmitExpression(out, stmt->value)) {
-            return false;
-        }
+        EmitExpression(out, stmt->value);
     }
     out << ";";
-    return true;
 }
 
-bool GeneratorImpl::EmitConstAssert(const ast::ConstAssert* stmt) {
+void GeneratorImpl::EmitConstAssert(const ast::ConstAssert* stmt) {
     auto out = line();
     out << "const_assert ";
-    if (!EmitExpression(out, stmt->condition)) {
-        return false;
-    }
+    EmitExpression(out, stmt->condition);
     out << ";";
-    return true;
 }
 
-bool GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
+void GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
     {
         auto out = line();
 
         if (!stmt->attributes.IsEmpty()) {
-            if (!EmitAttributes(out, stmt->attributes)) {
-                return false;
-            }
+            EmitAttributes(out, stmt->attributes);
             out << " ";
         }
 
         out << "switch(";
-        if (!EmitExpression(out, stmt->condition)) {
-            return false;
-        }
+        EmitExpression(out, stmt->condition);
         out << ") ";
 
         if (!stmt->body_attributes.IsEmpty()) {
-            if (!EmitAttributes(out, stmt->body_attributes)) {
-                return false;
-            }
+            EmitAttributes(out, stmt->body_attributes);
             out << " ";
         }
 
@@ -1244,14 +929,11 @@
     {
         ScopedIndent si(this);
         for (auto* s : stmt->body) {
-            if (!EmitCase(s)) {
-                return false;
-            }
+            EmitCase(s);
         }
     }
 
     line() << "}";
-    return true;
 }
 
 }  // namespace tint::writer::wgsl
diff --git a/src/tint/writer/wgsl/generator_impl.h b/src/tint/writer/wgsl/generator_impl.h
index c72d95e..5d69821 100644
--- a/src/tint/writer/wgsl/generator_impl.h
+++ b/src/tint/writer/wgsl/generator_impl.h
@@ -49,181 +49,141 @@
     ~GeneratorImpl();
 
     /// Generates the result data
-    /// @returns true on successful generation; false otherwise
-    bool Generate();
+    void Generate();
 
     /// Handles generating a diagnostic control
     /// @param out the output stream
     /// @param diagnostic the diagnostic control node
-    /// @returns true if the diagnostic control was emitted
-    bool EmitDiagnosticControl(utils::StringStream& out, const ast::DiagnosticControl& diagnostic);
+    void EmitDiagnosticControl(utils::StringStream& out, const ast::DiagnosticControl& diagnostic);
     /// Handles generating an enable directive
     /// @param enable the enable node
-    /// @returns true if the enable directive was emitted
-    bool EmitEnable(const ast::Enable* enable);
+    void EmitEnable(const ast::Enable* enable);
     /// Handles generating a declared type
     /// @param ty the declared type to generate
-    /// @returns true if the declared type was emitted
-    bool EmitTypeDecl(const ast::TypeDecl* ty);
+    void EmitTypeDecl(const ast::TypeDecl* ty);
     /// Handles an index accessor expression
     /// @param out the output stream
     /// @param expr the expression to emit
-    /// @returns true if the index accessor was emitted
-    bool EmitIndexAccessor(utils::StringStream& out, const ast::IndexAccessorExpression* expr);
+    void EmitIndexAccessor(utils::StringStream& out, const ast::IndexAccessorExpression* expr);
     /// Handles an assignment statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitAssign(const ast::AssignmentStatement* stmt);
+    void EmitAssign(const ast::AssignmentStatement* stmt);
     /// Handles generating a binary expression
     /// @param out the output stream
     /// @param expr the binary expression
-    /// @returns true if the expression was emitted, false otherwise
-    bool EmitBinary(utils::StringStream& out, const ast::BinaryExpression* expr);
+    void EmitBinary(utils::StringStream& out, const ast::BinaryExpression* expr);
     /// Handles generating a binary operator
     /// @param out the output stream
     /// @param op the binary operator
-    /// @returns true if the operator was emitted, false otherwise
-    bool EmitBinaryOp(utils::StringStream& out, const ast::BinaryOp op);
+    void EmitBinaryOp(utils::StringStream& out, const ast::BinaryOp op);
     /// Handles generating a bitcast expression
     /// @param out the output stream
     /// @param expr the bitcast expression
-    /// @returns true if the bitcast was emitted
-    bool EmitBitcast(utils::StringStream& out, const ast::BitcastExpression* expr);
+    void EmitBitcast(utils::StringStream& out, const ast::BitcastExpression* expr);
     /// Handles a block statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBlock(const ast::BlockStatement* stmt);
+    void EmitBlock(const ast::BlockStatement* stmt);
     /// Handles emitting the start of a block statement (including attributes)
     /// @param out the output stream to write the header to
     /// @param stmt the block statement to emit the header for
-    /// @returns true if the statement was emitted successfully
-    bool EmitBlockHeader(utils::StringStream& out, const ast::BlockStatement* stmt);
+    void EmitBlockHeader(utils::StringStream& out, const ast::BlockStatement* stmt);
     /// Handles a break statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreak(const ast::BreakStatement* stmt);
+    void EmitBreak(const ast::BreakStatement* stmt);
     /// Handles a break-if statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreakIf(const ast::BreakIfStatement* stmt);
+    void EmitBreakIf(const ast::BreakIfStatement* stmt);
     /// Handles generating a call expression
     /// @param out the output stream
     /// @param expr the call expression
-    /// @returns true if the call expression is emitted
-    bool EmitCall(utils::StringStream& out, const ast::CallExpression* expr);
+    void EmitCall(utils::StringStream& out, const ast::CallExpression* expr);
     /// Handles a case statement
     /// @param stmt the statement
-    /// @returns true if the statment was emitted successfully
-    bool EmitCase(const ast::CaseStatement* stmt);
+    void EmitCase(const ast::CaseStatement* stmt);
     /// Handles a compound assignment statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt);
+    void EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt);
     /// Handles generating a literal expression
     /// @param out the output stream
     /// @param expr the literal expression expression
-    /// @returns true if the literal expression is emitted
-    bool EmitLiteral(utils::StringStream& out, const ast::LiteralExpression* expr);
+    void EmitLiteral(utils::StringStream& out, const ast::LiteralExpression* expr);
     /// Handles a continue statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitContinue(const ast::ContinueStatement* stmt);
+    void EmitContinue(const ast::ContinueStatement* stmt);
     /// Handles generate an Expression
     /// @param out the output stream
     /// @param expr the expression
-    /// @returns true if the expression was emitted
-    bool EmitExpression(utils::StringStream& out, const ast::Expression* expr);
+    void EmitExpression(utils::StringStream& out, const ast::Expression* expr);
     /// Handles generating a function
     /// @param func the function to generate
-    /// @returns true if the function was emitted
-    bool EmitFunction(const ast::Function* func);
+    void EmitFunction(const ast::Function* func);
     /// Handles generating an identifier expression
     /// @param out the output stream
     /// @param expr the identifier expression
-    /// @returns true if the identifier was emitted
-    bool EmitIdentifier(utils::StringStream& out, const ast::IdentifierExpression* expr);
+    void EmitIdentifier(utils::StringStream& out, const ast::IdentifierExpression* expr);
     /// Handles generating an identifier
     /// @param out the output of the expression stream
     /// @param ident the identifier
-    /// @returns true if the identifier was emitted
-    bool EmitIdentifier(utils::StringStream& out, const ast::Identifier* ident);
+    void EmitIdentifier(utils::StringStream& out, const ast::Identifier* ident);
     /// Handles an if statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitIf(const ast::IfStatement* stmt);
+    void EmitIf(const ast::IfStatement* stmt);
     /// Handles an increment/decrement statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt);
+    void EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt);
     /// Handles generating a discard statement
     /// @param stmt the discard statement
-    /// @returns true if the statement was successfully emitted
-    bool EmitDiscard(const ast::DiscardStatement* stmt);
+    void EmitDiscard(const ast::DiscardStatement* stmt);
     /// Handles a loop statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emtited
-    bool EmitLoop(const ast::LoopStatement* stmt);
+    void EmitLoop(const ast::LoopStatement* stmt);
     /// Handles a for-loop statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emtited
-    bool EmitForLoop(const ast::ForLoopStatement* stmt);
+    void EmitForLoop(const ast::ForLoopStatement* stmt);
     /// Handles a while statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emtited
-    bool EmitWhile(const ast::WhileStatement* stmt);
+    void EmitWhile(const ast::WhileStatement* stmt);
     /// Handles a member accessor expression
     /// @param out the output stream
     /// @param expr the member accessor expression
-    /// @returns true if the member accessor was emitted
-    bool EmitMemberAccessor(utils::StringStream& out, const ast::MemberAccessorExpression* expr);
+    void EmitMemberAccessor(utils::StringStream& out, const ast::MemberAccessorExpression* expr);
     /// Handles return statements
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitReturn(const ast::ReturnStatement* stmt);
+    void EmitReturn(const ast::ReturnStatement* stmt);
     /// Handles const assertion statements
     /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitConstAssert(const ast::ConstAssert* stmt);
+    void EmitConstAssert(const ast::ConstAssert* stmt);
     /// Handles statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitStatement(const ast::Statement* stmt);
+    void EmitStatement(const ast::Statement* stmt);
     /// Handles a statement list
     /// @param stmts the statements to emit
-    /// @returns true if the statements were emitted
-    bool EmitStatements(utils::VectorRef<const ast::Statement*> stmts);
+    void EmitStatements(utils::VectorRef<const ast::Statement*> stmts);
     /// Handles a statement list with an increased indentation
     /// @param stmts the statements to emit
-    /// @returns true if the statements were emitted
-    bool EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts);
+    void EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts);
     /// Handles generating a switch statement
     /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitSwitch(const ast::SwitchStatement* stmt);
+    void EmitSwitch(const ast::SwitchStatement* stmt);
     /// Handles generating a struct declaration
     /// @param str the struct
-    /// @returns true if the struct is emitted
-    bool EmitStructType(const ast::Struct* str);
+    void EmitStructType(const ast::Struct* str);
     /// Handles emitting an image format
     /// @param out the output stream
     /// @param fmt the format to generate
-    /// @returns true if the format is emitted
-    bool EmitImageFormat(utils::StringStream& out, const builtin::TexelFormat fmt);
+    void EmitImageFormat(utils::StringStream& out, const builtin::TexelFormat fmt);
     /// Handles a unary op expression
     /// @param out the output stream
     /// @param expr the expression to emit
-    /// @returns true if the expression was emitted
-    bool EmitUnaryOp(utils::StringStream& out, const ast::UnaryOpExpression* expr);
+    void EmitUnaryOp(utils::StringStream& out, const ast::UnaryOpExpression* expr);
     /// Handles generating a variable
     /// @param out the output stream
     /// @param var the variable to generate
-    /// @returns true if the variable was emitted
-    bool EmitVariable(utils::StringStream& out, const ast::Variable* var);
+    void EmitVariable(utils::StringStream& out, const ast::Variable* var);
     /// Handles generating a attribute list
     /// @param out the output stream
     /// @param attrs the attribute list
-    /// @returns true if the attributes were emitted
-    bool EmitAttributes(utils::StringStream& out, utils::VectorRef<const ast::Attribute*> attrs);
+    void EmitAttributes(utils::StringStream& out, utils::VectorRef<const ast::Attribute*> attrs);
 };
 
 }  // namespace tint::writer::wgsl
diff --git a/src/tint/writer/wgsl/generator_impl_alias_type_test.cc b/src/tint/writer/wgsl/generator_impl_alias_type_test.cc
index 10a56b1..9c76148 100644
--- a/src/tint/writer/wgsl/generator_impl_alias_type_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_alias_type_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -23,8 +25,9 @@
     auto* alias = Alias("a", ty.f32());
 
     GeneratorImpl& gen = Build();
+    gen.EmitTypeDecl(alias);
 
-    ASSERT_TRUE(gen.EmitTypeDecl(alias)) << gen.error();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(alias a = f32;
 )");
 }
@@ -38,9 +41,11 @@
     auto* alias = Alias("B", ty.Of(s));
 
     GeneratorImpl& gen = Build();
+    gen.EmitTypeDecl(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
 
-    ASSERT_TRUE(gen.EmitTypeDecl(s)) << gen.error();
-    ASSERT_TRUE(gen.EmitTypeDecl(alias)) << gen.error();
+    gen.EmitTypeDecl(alias);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct A {
   a : f32,
   b : i32,
@@ -59,7 +64,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitTypeDecl(alias)) << gen.error();
+    gen.EmitTypeDecl(alias);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(alias B = A;
 )");
 }
diff --git a/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc b/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
index 0cf7cf4..d24a1a7 100644
--- a/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -30,7 +32,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    gen.EmitExpression(out, expr);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "ary[5i]");
 }
 
@@ -44,7 +47,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    gen.EmitExpression(out, expr);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "(*(p))[5i]");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_assign_test.cc b/src/tint/writer/wgsl/generator_impl_assign_test.cc
index c802cb1..5d7ab23 100644
--- a/src/tint/writer/wgsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_assign_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -29,7 +31,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(assign)) << gen.error();
+    gen.EmitStatement(assign);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  lhs = rhs;\n");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_binary_test.cc b/src/tint/writer/wgsl/generator_impl_binary_test.cc
index fbb6c32..1829cf8 100644
--- a/src/tint/writer/wgsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_binary_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -51,7 +53,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    gen.EmitExpression(out, expr);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
diff --git a/src/tint/writer/wgsl/generator_impl_bitcast_test.cc b/src/tint/writer/wgsl/generator_impl_bitcast_test.cc
index df2a9aa..18c2f6d 100644
--- a/src/tint/writer/wgsl/generator_impl_bitcast_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_bitcast_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -29,7 +31,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.error();
+    gen.EmitExpression(out, bitcast);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "bitcast<f32>(1i)");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_block_test.cc b/src/tint/writer/wgsl/generator_impl_block_test.cc
index 0609f97..d5af303 100644
--- a/src/tint/writer/wgsl/generator_impl_block_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_block_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -27,7 +29,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    gen.EmitStatement(b);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  {
     return;
   }
diff --git a/src/tint/writer/wgsl/generator_impl_break_test.cc b/src/tint/writer/wgsl/generator_impl_break_test.cc
index 8f7275a..d60cef4 100644
--- a/src/tint/writer/wgsl/generator_impl_break_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_break_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -27,7 +29,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
+    gen.EmitStatement(b);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  break;\n");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_call_test.cc b/src/tint/writer/wgsl/generator_impl_call_test.cc
index f484cb6..5cb8eac 100644
--- a/src/tint/writer/wgsl/generator_impl_call_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_call_test.cc
@@ -16,6 +16,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -35,7 +37,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    gen.EmitExpression(out, call);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "my_func()");
 }
 
@@ -58,7 +61,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
+    gen.EmitExpression(out, call);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "my_func(param1, param2)");
 }
 
@@ -79,7 +83,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    gen.EmitStatement(stmt);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  my_func(param1, param2);\n");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_case_test.cc b/src/tint/writer/wgsl/generator_impl_case_test.cc
index 39c28cb..05b0d9e 100644
--- a/src/tint/writer/wgsl/generator_impl_case_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_case_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -30,7 +32,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    gen.EmitCase(s->body[0]);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  case 5i: {
     break;
   }
@@ -52,7 +55,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    gen.EmitCase(s->body[0]);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  case 5i, 6i: {
     break;
   }
@@ -67,7 +71,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
+    gen.EmitCase(s->body[0]);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  default: {
     break;
   }
diff --git a/src/tint/writer/wgsl/generator_impl_cast_test.cc b/src/tint/writer/wgsl/generator_impl_cast_test.cc
index 31e3ad0..890c52c 100644
--- a/src/tint/writer/wgsl/generator_impl_cast_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_cast_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -29,7 +31,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    gen.EmitExpression(out, cast);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "f32(1i)");
 }
 
@@ -42,7 +45,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    gen.EmitExpression(out, cast);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "f16(1i)");
 }
 
@@ -53,7 +57,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    gen.EmitExpression(out, cast);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "vec3<f32>(vec3<i32>(1i, 2i, 3i))");
 }
 
@@ -66,7 +71,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+    gen.EmitExpression(out, cast);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "vec3<f16>(vec3<i32>(1i, 2i, 3i))");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_const_assert_test.cc b/src/tint/writer/wgsl/generator_impl_const_assert_test.cc
index b785357..d3f22ef 100644
--- a/src/tint/writer/wgsl/generator_impl_const_assert_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_const_assert_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -25,8 +27,8 @@
     GlobalConstAssert(true);
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(const_assert true;
 )");
 }
@@ -35,8 +37,8 @@
     Func("f", utils::Empty, ty.void_(), utils::Vector{ConstAssert(true)});
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const_assert true;
 }
diff --git a/src/tint/writer/wgsl/generator_impl_constructor_test.cc b/src/tint/writer/wgsl/generator_impl_constructor_test.cc
index 7278736..fa40ff6 100644
--- a/src/tint/writer/wgsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_constructor_test.cc
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "gmock/gmock.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using ::testing::HasSubstr;
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -28,8 +29,8 @@
     WrapInFunction(Expr(false));
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("false"));
 }
 
@@ -37,8 +38,8 @@
     WrapInFunction(Expr(-12345_i));
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("-12345"));
 }
 
@@ -46,8 +47,8 @@
     WrapInFunction(Expr(56779_u));
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("56779u"));
 }
 
@@ -57,7 +58,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
 }
 
@@ -69,7 +71,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("32752.0h"));
 }
 
@@ -78,7 +81,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("f32(-0.00001200000042445026f)"));
 }
 
@@ -89,7 +93,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("f16(-0.00001198053359985352h)"));
 }
 
@@ -98,7 +103,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("bool(true)"));
 }
 
@@ -107,7 +113,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("i32(-12345i)"));
 }
 
@@ -116,7 +123,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("u32(12345u)"));
 }
 
@@ -125,7 +133,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("vec3<f32>(1.0f, 2.0f, 3.0f)"));
 }
 
@@ -136,7 +145,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("vec3<f16>(1.0h, 2.0h, 3.0h)"));
 }
 
@@ -145,7 +155,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("mat2x3<f32>(vec3<f32>(1.0f, 2.0f, 3.0f), "
                                         "vec3<f32>(3.0f, 4.0f, 5.0f))"));
 }
@@ -157,7 +168,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(), HasSubstr("mat2x3<f16>(vec3<f16>(1.0h, 2.0h, 3.0h), "
                                         "vec3<f16>(3.0h, 4.0h, 5.0h))"));
 }
@@ -168,7 +180,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(),
                 HasSubstr("array<vec3<f32>, 3u>(vec3<f32>(1.0f, 2.0f, 3.0f), "
                           "vec3<f32>(4.0f, 5.0f, 6.0f), vec3<f32>(7.0f, 8.0f, 9.0f))"));
@@ -180,7 +193,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_THAT(gen.result(),
                 HasSubstr("array(vec3<f32>(1.0f, 2.0f, 3.0f), "
                           "vec3<f32>(4.0f, 5.0f, 6.0f), vec3<f32>(7.0f, 8.0f, 9.0f))"));
diff --git a/src/tint/writer/wgsl/generator_impl_continue_test.cc b/src/tint/writer/wgsl/generator_impl_continue_test.cc
index 46f2bbd..e1b9056 100644
--- a/src/tint/writer/wgsl/generator_impl_continue_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_continue_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -29,7 +31,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(c)) << gen.error();
+    gen.EmitStatement(c);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  continue;\n");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc b/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
index 301bff4..2a2f4b6 100644
--- a/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -23,8 +25,8 @@
     DiagnosticDirective(builtin::DiagnosticSeverity::kError, "chromium_unreachable_code");
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(diagnostic(error, chromium_unreachable_code);
 
 )");
@@ -36,8 +38,8 @@
     Func("foo", {}, ty.void_(), {}, utils::Vector{attr});
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(@diagnostic(error, chromium_unreachable_code)
 fn foo() {
 }
diff --git a/src/tint/writer/wgsl/generator_impl_discard_test.cc b/src/tint/writer/wgsl/generator_impl_discard_test.cc
index 79dcd47..4a5ae25 100644
--- a/src/tint/writer/wgsl/generator_impl_discard_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_discard_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -29,7 +31,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    gen.EmitStatement(stmt);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  discard;\n");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_enable_test.cc b/src/tint/writer/wgsl/generator_impl_enable_test.cc
index 739d893..34d1614 100644
--- a/src/tint/writer/wgsl/generator_impl_enable_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_enable_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -24,7 +26,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitEnable(enable));
+    gen.EmitEnable(enable);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(enable f16;
 )");
 }
diff --git a/src/tint/writer/wgsl/generator_impl_function_test.cc b/src/tint/writer/wgsl/generator_impl_function_test.cc
index b49d690..14de0c2 100644
--- a/src/tint/writer/wgsl/generator_impl_function_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_function_test.cc
@@ -18,6 +18,8 @@
 #include "src/tint/builtin/builtin_value.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -34,8 +36,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitFunction(func));
+    gen.EmitFunction(func);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  fn my_func() {
     return;
   }
@@ -56,8 +58,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitFunction(func));
+    gen.EmitFunction(func);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  fn my_func(a : f32, b : i32) {
     return;
   }
@@ -77,8 +79,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitFunction(func));
+    gen.EmitFunction(func);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  @compute @workgroup_size(2i, 4i, 6i)
   fn my_func() {
     return;
@@ -98,8 +100,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitFunction(func));
+    gen.EmitFunction(func);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  @must_use
   fn my_func() -> i32 {
     return 1i;
@@ -121,8 +123,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitFunction(func));
+    gen.EmitFunction(func);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  @compute @workgroup_size(2i, height)
   fn my_func() {
     return;
@@ -148,8 +150,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitFunction(func));
+    gen.EmitFunction(func);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  @fragment
   fn frag_main(@builtin(position) coord : vec4<f32>, @location(1) loc1 : f32) {
   }
@@ -171,8 +173,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitFunction(func));
+    gen.EmitFunction(func);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  @fragment
   fn frag_main() -> @location(1) f32 {
     return 1.0f;
@@ -234,7 +236,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct Data {
   d : f32,
 }
diff --git a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
index cef2c74..9d97f85 100644
--- a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
@@ -18,6 +18,8 @@
 #include "src/tint/type/texture_dimension.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -34,8 +36,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  @compute @workgroup_size(1i, 1i, 1i)
   fn test_function() {
     var a : f32;
@@ -78,8 +80,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  var<private> a0 : f32;
 
   struct S0 {
@@ -111,8 +113,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  @group(0) @binding(0) var s : sampler;\n");
 }
 
@@ -123,8 +125,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  @group(0) @binding(0) var t : texture_1d<f32>;\n");
 }
 
@@ -135,8 +137,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  const explicit : f32 = 1.0f;
 
   const inferred = 1.0f;
@@ -150,8 +152,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  override a : f32;
 
   @id(7) override b : f32;
diff --git a/src/tint/writer/wgsl/generator_impl_identifier_test.cc b/src/tint/writer/wgsl/generator_impl_identifier_test.cc
index f270ede..be2663f 100644
--- a/src/tint/writer/wgsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_identifier_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -28,7 +30,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.error();
+    gen.EmitExpression(out, i);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "glsl");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_if_test.cc b/src/tint/writer/wgsl/generator_impl_if_test.cc
index 8b9d443..968b03a 100644
--- a/src/tint/writer/wgsl/generator_impl_if_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_if_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -31,7 +33,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    gen.EmitStatement(i);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   }
@@ -54,7 +57,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    gen.EmitStatement(i);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else if (else_cond) {
@@ -77,7 +81,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    gen.EmitStatement(i);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else {
@@ -105,7 +110,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
+    gen.EmitStatement(i);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  if (cond) {
     return;
   } else if (else_cond) {
diff --git a/src/tint/writer/wgsl/generator_impl_literal_test.cc b/src/tint/writer/wgsl/generator_impl_literal_test.cc
index b70f535..b170baa 100644
--- a/src/tint/writer/wgsl/generator_impl_literal_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_literal_test.cc
@@ -17,6 +17,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -116,7 +118,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitLiteral(out, v)) << gen.error();
+    gen.EmitLiteral(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), GetParam().expected);
 }
 
@@ -164,7 +167,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitLiteral(out, v)) << gen.error();
+    gen.EmitLiteral(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), GetParam().expected);
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_loop_test.cc b/src/tint/writer/wgsl/generator_impl_loop_test.cc
index 62910fc..58b4ea8 100644
--- a/src/tint/writer/wgsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_loop_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -33,7 +35,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    gen.EmitStatement(l);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  loop {
     break;
   }
@@ -54,7 +57,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    gen.EmitStatement(l);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  loop {
     break;
 
@@ -79,7 +83,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
+    gen.EmitStatement(l);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  loop {
     discard;
 
@@ -105,7 +110,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  for({
     _ = 1i;
     _ = 2i;
@@ -127,7 +133,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  for(; true; ) {
     return;
   }
@@ -147,7 +154,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  for(; ; i = (i + 1i)) {
     return;
   }
@@ -169,7 +177,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  for(; ; {
     _ = 1i;
     _ = 2i;
@@ -191,7 +200,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  for(var i : i32; true; i = (i + 1i)) {
     return;
   }
@@ -213,7 +223,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  for({
     _ = 1i;
     _ = 2i;
@@ -238,7 +249,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  while(true) {
     return;
   }
@@ -257,7 +269,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  while(true) {
     continue;
   }
@@ -278,7 +291,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+    gen.EmitStatement(f);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  while((true && false)) {
     return;
   }
diff --git a/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc b/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
index 8317305..4e5ce9a 100644
--- a/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -30,7 +32,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    gen.EmitExpression(out, expr);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "str.mem");
 }
 
@@ -45,7 +48,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    gen.EmitExpression(out, expr);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "(*(p)).mem");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_return_test.cc b/src/tint/writer/wgsl/generator_impl_return_test.cc
index fbbf9de..631dbaf 100644
--- a/src/tint/writer/wgsl/generator_impl_return_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_return_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -29,7 +31,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    gen.EmitStatement(r);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  return;\n");
 }
 
@@ -41,7 +44,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
+    gen.EmitStatement(r);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  return 123i;\n");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_switch_test.cc b/src/tint/writer/wgsl/generator_impl_switch_test.cc
index d16808a..7ec5584 100644
--- a/src/tint/writer/wgsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_switch_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -43,7 +45,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    gen.EmitStatement(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5i: {
       break;
@@ -68,8 +71,8 @@
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
-
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
+    gen.EmitStatement(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5i, default: {
       break;
diff --git a/src/tint/writer/wgsl/generator_impl_test.cc b/src/tint/writer/wgsl/generator_impl_test.cc
index 4d98158..b58c0be 100644
--- a/src/tint/writer/wgsl/generator_impl_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/sem/variable.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -25,7 +27,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.Generate()) << gen.error();
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn my_func() {
 }
 )");
diff --git a/src/tint/writer/wgsl/generator_impl_type_test.cc b/src/tint/writer/wgsl/generator_impl_type_test.cc
index aa22d73..9bf9a4c 100644
--- a/src/tint/writer/wgsl/generator_impl_type_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_type_test.cc
@@ -20,6 +20,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -34,7 +36,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "alias");
 }
 
@@ -44,7 +47,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "array<bool, 4u>");
 }
 
@@ -55,7 +59,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "@stride(16) array<bool, 4u>");
 }
 
@@ -65,7 +70,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "array<bool>");
 }
 
@@ -75,7 +81,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "bool");
 }
 
@@ -85,7 +92,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "f32");
 }
 
@@ -97,7 +105,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "f16");
 }
 
@@ -107,7 +116,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "i32");
 }
 
@@ -117,7 +127,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "mat2x3<f32>");
 }
 
@@ -129,7 +140,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "mat2x3<f16>");
 }
 
@@ -140,7 +152,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "ptr<workgroup, f32>");
 }
 
@@ -152,7 +165,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "ptr<storage, f32, read_write>");
 }
 
@@ -166,7 +180,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "S");
 }
 
@@ -178,7 +193,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
+    gen.EmitStructType(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct S {
   @size(8)
   padding : u32,
@@ -201,7 +217,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
+    gen.EmitStructType(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct S {
   @size(8)
   padding : u32,
@@ -223,7 +240,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
+    gen.EmitStructType(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct S {
   @align(8)
   a : i32,
@@ -241,7 +259,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
+    gen.EmitStructType(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct S {
   @size(16)
   a : i32,
@@ -259,7 +278,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
+    gen.EmitStructType(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct S {
   a : i32,
   @align(8)
@@ -277,7 +297,8 @@
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
+    gen.EmitStructType(s);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(struct S {
   @builtin(vertex_index)
   a : u32,
@@ -293,7 +314,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "u32");
 }
 
@@ -303,7 +325,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "vec3<f32>");
 }
 
@@ -315,7 +338,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "vec3<f16>");
 }
 
@@ -337,7 +361,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), param.name);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -358,7 +383,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), std::string(param.name) + "<f32>");
 }
 
@@ -371,7 +397,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), std::string(param.name) + "<i32>");
 }
 
@@ -384,7 +411,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), std::string(param.name) + "<u32>");
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -407,7 +435,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), std::string(param.name) + "<f32>");
 }
 
@@ -420,7 +449,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), std::string(param.name) + "<i32>");
 }
 
@@ -433,7 +463,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), std::string(param.name) + "<u32>");
 }
 INSTANTIATE_TEST_SUITE_P(WgslGeneratorImplTest,
@@ -461,7 +492,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), param.name);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -492,7 +524,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitImageFormat(out, param.fmt)) << gen.error();
+    gen.EmitImageFormat(out, param.fmt);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), param.name);
 }
 
@@ -523,7 +556,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "sampler");
 }
 
@@ -534,7 +568,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, type)) << gen.error();
+    gen.EmitExpression(out, type);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "sampler_comparison");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_unary_op_test.cc b/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
index 537f5d4..7c0ce54 100644
--- a/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 namespace tint::writer::wgsl {
 namespace {
 
@@ -28,7 +30,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    gen.EmitExpression(out, op);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "&(expr)");
 }
 
@@ -40,7 +43,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    gen.EmitExpression(out, op);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "~(expr)");
 }
 
@@ -53,7 +57,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    gen.EmitExpression(out, op);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "*(expr)");
 }
 
@@ -65,7 +70,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    gen.EmitExpression(out, op);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "!(expr)");
 }
 
@@ -77,7 +83,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+    gen.EmitExpression(out, op);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), "-(expr)");
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
index f31aa65..59d761e 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -32,7 +34,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    gen.EmitStatement(stmt);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  var a : f32;\n");
 }
 
@@ -46,7 +49,8 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    gen.EmitStatement(stmt);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), "  var a = 123i;\n");
 }
 
@@ -59,9 +63,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = 1;
   let l = C;
@@ -78,9 +81,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = 1.0;
   let l = C;
@@ -97,9 +99,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = 1i;
   let l = C;
@@ -116,9 +117,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = 1u;
   let l = C;
@@ -135,9 +135,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = 1.0f;
   let l = C;
@@ -156,9 +155,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(enable f16;
 
 fn f() {
@@ -177,9 +175,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = vec3(1, 2, 3);
   let l = C;
@@ -196,9 +193,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = vec3(1.0, 2.0, 3.0);
   let l = C;
@@ -215,9 +211,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = vec3<f32>(1.0f, 2.0f, 3.0f);
   let l = C;
@@ -236,9 +231,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(enable f16;
 
 fn f() {
@@ -257,9 +251,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = mat2x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
   let l = C;
@@ -276,9 +269,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = mat2x3<f32>(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);
   let l = C;
@@ -297,9 +289,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(enable f16;
 
 fn f() {
@@ -318,9 +309,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = array<f32, 3u>(1.0f, 2.0f, 3.0f);
   let l = C;
@@ -340,9 +330,8 @@
          });
 
     GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-
+    gen.Generate();
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(gen.result(), R"(fn f() {
   const C = array<vec2<bool>, 3u>(vec2<bool>(true, false), vec2<bool>(false, true), vec2<bool>(true, true));
   let l = C;
diff --git a/src/tint/writer/wgsl/generator_impl_variable_test.cc b/src/tint/writer/wgsl/generator_impl_variable_test.cc
index 2706c30..b9bbb40 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
+#include "gmock/gmock.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::writer::wgsl {
@@ -28,7 +30,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(var<private> a : f32;)");
 }
 
@@ -38,7 +41,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(var<private> a : f32;)");
 }
 
@@ -50,7 +54,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(@binding(0) @group(0) var<storage, read> a : S;)");
 }
 
@@ -62,7 +67,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(@binding(0) @group(0) var<storage, read_write> a : S;)");
 }
 
@@ -72,7 +78,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(@group(1) @binding(2) var a : sampler;)");
 }
 
@@ -82,7 +89,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(var<private> a : f32 = 1.0f;)");
 }
 
@@ -93,7 +101,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(let a : f32 = 1.0f;)");
 }
 
@@ -104,7 +113,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(let a = 1.0f;)");
 }
 
@@ -115,7 +125,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(const a : f32 = 1.0f;)");
 }
 
@@ -126,7 +137,8 @@
     GeneratorImpl& gen = Build();
 
     utils::StringStream out;
-    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    gen.EmitVariable(out, v);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
     EXPECT_EQ(out.str(), R"(const a = 1.0f;)");
 }