ast: Remove to_str() and type_name()

This is no longer used.

Fixed: tint:1225
Change-Id: I0cfe9955687a2b7ded3e645c573f3bffbc2f1f84
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/66380
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/fuzzers/tint_ast_clone_fuzzer.cc b/fuzzers/tint_ast_clone_fuzzer.cc
index 2e642c7..bdaf643 100644
--- a/fuzzers/tint_ast_clone_fuzzer.cc
+++ b/fuzzers/tint_ast_clone_fuzzer.cc
@@ -69,8 +69,8 @@
   // Clone the src program to dst
   tint::Program dst(src.Clone());
 
-  // Expect the demangled AST printed with to_str() to match
-  ASSERT_EQ(src.to_str(), dst.to_str());
+  // Expect the printed strings to match
+  ASSERT_EQ(tint::Program::printer(&src), tint::Program::printer(&dst));
 
   // Check that none of the AST nodes or type pointers in dst are found in src
   std::unordered_set<tint::ast::Node*> src_nodes;
diff --git a/samples/main.cc b/samples/main.cc
index c7c9999..ca2c456 100644
--- a/samples/main.cc
+++ b/samples/main.cc
@@ -69,7 +69,6 @@
   std::string output_file = "-";  // Default to stdout
 
   bool parse_only = false;
-  bool dump_ast = false;
   bool disable_workgroup_init = false;
   bool validate = false;
   bool demangle = false;
@@ -409,8 +408,6 @@
       opts->transforms = split_transform_names(args[i]);
     } else if (arg == "--parse-only") {
       opts->parse_only = true;
-    } else if (arg == "--dump-ast") {
-      opts->dump_ast = true;
     } else if (arg == "--disable-workgroup-init") {
       opts->disable_workgroup_init = true;
     } else if (arg == "--demangle") {
@@ -1021,10 +1018,6 @@
     diag_formatter.format(program->Diagnostics(), diag_printer.get());
   }
 
-  if (options.dump_ast) {
-    std::cout << std::endl << program->to_str(options.demangle) << std::endl;
-  }
-
   if (!program->IsValid()) {
     return 1;
   }
diff --git a/src/ast/alias.cc b/src/ast/alias.cc
index ffa32ac..8a8bba8 100644
--- a/src/ast/alias.cc
+++ b/src/ast/alias.cc
@@ -25,9 +25,7 @@
              const Source& source,
              const Symbol& name,
              Type* subtype)
-    : Base(program_id, source, name),
-      subtype_(subtype),
-      type_name_("__alias_" + name.to_str() + subtype->type_name()) {
+    : Base(program_id, source, name), subtype_(subtype) {
   TINT_ASSERT(AST, subtype_);
 }
 
@@ -35,10 +33,6 @@
 
 Alias::~Alias() = default;
 
-std::string Alias::type_name() const {
-  return type_name_;
-}
-
 Alias* Alias::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/alias.h b/src/ast/alias.h
index 6e3dd8d..9ca50b8 100644
--- a/src/ast/alias.h
+++ b/src/ast/alias.h
@@ -45,9 +45,6 @@
   /// @returns the alias type
   Type* type() const { return subtype_; }
 
-  /// @returns the type_name for this type
-  std::string type_name() const override;
-
   /// Clones this type and all transitive types using the `CloneContext` `ctx`.
   /// @param ctx the clone context
   /// @return the newly cloned type
@@ -55,7 +52,6 @@
 
  private:
   Type* const subtype_;
-  std::string const type_name_;
 };
 
 }  // namespace ast
diff --git a/src/ast/alias_test.cc b/src/ast/alias_test.cc
index 8ae7a63..8a744cf 100644
--- a/src/ast/alias_test.cc
+++ b/src/ast/alias_test.cc
@@ -40,24 +40,6 @@
   EXPECT_EQ(a->type(), u32);
 }
 
-// Check for linear-time evaluation of Alias::type_name().
-// If type_name() is non-linear, this test should noticeably stall.
-// See: crbug.com/1200936
-TEST_F(AstAliasTest, TypeName_LinearTime) {
-  Type* type = ty.i32();
-  for (int i = 0; i < 1024; i++) {
-    type = ty.Of(Alias(Symbols().New(), type));
-  }
-  for (int i = 0; i < 16384; i++) {
-    type->type_name();
-  }
-}
-
-TEST_F(AstAliasTest, TypeName) {
-  auto* at = Alias("Particle", create<I32>());
-  EXPECT_EQ(at->type_name(), "__alias_$1__i32");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/array.cc b/src/ast/array.cc
index 9550897..b9993a5 100644
--- a/src/ast/array.cc
+++ b/src/ast/array.cc
@@ -30,10 +30,10 @@
   if (auto* ident = size->As<ast::IdentifierExpression>()) {
     if (symbols) {
       return symbols->NameFor(ident->symbol());
-    } else {
-      return ident->symbol().to_str();
     }
-  } else if (auto* scalar = size->As<ast::ScalarConstructorExpression>()) {
+    return "<unknown>";
+  }
+  if (auto* scalar = size->As<ast::ScalarConstructorExpression>()) {
     auto* literal = scalar->literal()->As<ast::IntLiteral>();
     if (literal) {
       return std::to_string(literal->value_as_u32());
@@ -59,22 +59,6 @@
 
 Array::~Array() = default;
 
-std::string Array::type_name() const {
-  TINT_ASSERT(AST, subtype_);
-
-  std::string type_name = "__array" + subtype_->type_name();
-  if (!IsRuntimeArray()) {
-    type_name += "_" + SizeExprToString(size_);
-  }
-  for (auto* deco : decos_) {
-    if (auto* stride = deco->As<ast::StrideDecoration>()) {
-      type_name += "_stride_" + std::to_string(stride->stride());
-    }
-  }
-
-  return type_name;
-}
-
 std::string Array::FriendlyName(const SymbolTable& symbols) const {
   std::ostringstream out;
   for (auto* deco : decos_) {
diff --git a/src/ast/array.h b/src/ast/array.h
index 7e4b004..df0ba88 100644
--- a/src/ast/array.h
+++ b/src/ast/array.h
@@ -58,9 +58,6 @@
   /// @returns the array size, or nullptr for a runtime array
   ast::Expression* Size() const { return size_; }
 
-  /// @returns the name for the type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/array_accessor_expression.cc b/src/ast/array_accessor_expression.cc
index 23fc1b2..b7b9a41 100644
--- a/src/ast/array_accessor_expression.cc
+++ b/src/ast/array_accessor_expression.cc
@@ -46,16 +46,5 @@
   return ctx->dst->create<ArrayAccessorExpression>(src, arr, idx);
 }
 
-void ArrayAccessorExpression::to_str(const sem::Info& sem,
-                                     std::ostream& out,
-                                     size_t indent) const {
-  make_indent(out, indent);
-  out << "ArrayAccessor[" << result_type_str(sem) << "]{" << std::endl;
-  array_->to_str(sem, out, indent + 2);
-  idx_expr_->to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/array_accessor_expression.h b/src/ast/array_accessor_expression.h
index 2656a0f..891884d 100644
--- a/src/ast/array_accessor_expression.h
+++ b/src/ast/array_accessor_expression.h
@@ -49,14 +49,6 @@
   /// @return the newly cloned node
   ArrayAccessorExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   ArrayAccessorExpression(const ArrayAccessorExpression&) = delete;
 
diff --git a/src/ast/array_accessor_expression_test.cc b/src/ast/array_accessor_expression_test.cc
index ec2ad4b..90f93ac 100644
--- a/src/ast/array_accessor_expression_test.cc
+++ b/src/ast/array_accessor_expression_test.cc
@@ -87,18 +87,6 @@
       "internal compiler error");
 }
 
-TEST_F(ArrayAccessorExpressionTest, ToStr) {
-  auto* ary = Expr("ary");
-  auto* idx = Expr("idx");
-
-  auto* exp = create<ArrayAccessorExpression>(ary, idx);
-  EXPECT_EQ(str(exp), R"(ArrayAccessor[not set]{
-  Identifier[not set]{ary}
-  Identifier[not set]{idx}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/array_test.cc b/src/ast/array_test.cc
index f6413d9..8bea47b 100644
--- a/src/ast/array_test.cc
+++ b/src/ast/array_test.cc
@@ -41,12 +41,6 @@
   EXPECT_TRUE(arr->IsRuntimeArray());
 }
 
-TEST_F(AstArrayTest, TypeName) {
-  auto* i32 = create<I32>();
-  auto* arr = create<Array>(i32, nullptr, DecorationList{});
-  EXPECT_EQ(arr->type_name(), "__array__i32");
-}
-
 TEST_F(AstArrayTest, FriendlyName_RuntimeSized) {
   auto* i32 = create<I32>();
   auto* arr = create<Array>(i32, nullptr, DecorationList{});
@@ -72,31 +66,6 @@
   EXPECT_EQ(arr->FriendlyName(Symbols()), "[[stride(32)]] array<i32, 5>");
 }
 
-TEST_F(AstArrayTest, TypeName_RuntimeSized) {
-  auto* i32 = create<I32>();
-  auto* arr = create<Array>(i32, nullptr, DecorationList{});
-  EXPECT_EQ(arr->type_name(), "__array__i32");
-}
-
-TEST_F(AstArrayTest, TypeName_LiteralSized) {
-  auto* i32 = create<I32>();
-  auto* arr = create<Array>(i32, Expr(3), DecorationList{});
-  EXPECT_EQ(arr->type_name(), "__array__i32_3");
-}
-
-TEST_F(AstArrayTest, TypeName_ConstantSized) {
-  auto* i32 = create<I32>();
-  auto* arr = create<Array>(i32, Expr("size"), DecorationList{});
-  EXPECT_EQ(arr->type_name(), "__array__i32_$1");
-}
-
-TEST_F(AstArrayTest, TypeName_WithStride) {
-  auto* i32 = create<I32>();
-  auto* arr =
-      create<Array>(i32, Expr(3), DecorationList{create<StrideDecoration>(16)});
-  EXPECT_EQ(arr->type_name(), "__array__i32_3_stride_16");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/assignment_statement.cc b/src/ast/assignment_statement.cc
index 7af726b..5569a70 100644
--- a/src/ast/assignment_statement.cc
+++ b/src/ast/assignment_statement.cc
@@ -44,16 +44,5 @@
   return ctx->dst->create<AssignmentStatement>(src, l, r);
 }
 
-void AssignmentStatement::to_str(const sem::Info& sem,
-                                 std::ostream& out,
-                                 size_t indent) const {
-  make_indent(out, indent);
-  out << "Assignment{" << std::endl;
-  lhs_->to_str(sem, out, indent + 2);
-  rhs_->to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/assignment_statement.h b/src/ast/assignment_statement.h
index f73ca0c..6857a05 100644
--- a/src/ast/assignment_statement.h
+++ b/src/ast/assignment_statement.h
@@ -48,14 +48,6 @@
   /// @return the newly cloned node
   AssignmentStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   AssignmentStatement(const AssignmentStatement&) = delete;
 
diff --git a/src/ast/assignment_statement_test.cc b/src/ast/assignment_statement_test.cc
index 80d324e..677f0bc 100644
--- a/src/ast/assignment_statement_test.cc
+++ b/src/ast/assignment_statement_test.cc
@@ -89,18 +89,6 @@
       "internal compiler error");
 }
 
-TEST_F(AssignmentStatementTest, ToStr) {
-  auto* lhs = Expr("lhs");
-  auto* rhs = Expr("rhs");
-
-  auto* stmt = create<AssignmentStatement>(lhs, rhs);
-  EXPECT_EQ(str(stmt), R"(Assignment{
-  Identifier[not set]{lhs}
-  Identifier[not set]{rhs}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/ast_type.cc b/src/ast/ast_type.cc
index c5d6871..4676d29 100644
--- a/src/ast/ast_type.cc
+++ b/src/ast/ast_type.cc
@@ -24,6 +24,7 @@
 #include "src/ast/texture.h"
 #include "src/ast/u32.h"
 #include "src/ast/vector.h"
+#include "src/symbol_table.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::ast::Type);
 
@@ -109,10 +110,5 @@
   return IsAnyOf<Sampler, Texture>();
 }
 
-void Type::to_str(const sem::Info&, std::ostream& out, size_t indent) const {
-  make_indent(out, indent);
-  out << type_name();
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/atomic.cc b/src/ast/atomic.cc
index 9f4c2dc..7ecd74d 100644
--- a/src/ast/atomic.cc
+++ b/src/ast/atomic.cc
@@ -24,12 +24,6 @@
 Atomic::Atomic(ProgramID program_id, const Source& source, Type* const subtype)
     : Base(program_id, source), subtype_(subtype) {}
 
-std::string Atomic::type_name() const {
-  std::ostringstream out;
-  out << "__atomic" << subtype_->type_name();
-  return out.str();
-}
-
 std::string Atomic::FriendlyName(const SymbolTable& symbols) const {
   std::ostringstream out;
   out << "atomic<" << subtype_->FriendlyName(symbols) << ">";
diff --git a/src/ast/atomic.h b/src/ast/atomic.h
index edd41d6..8488ee6 100644
--- a/src/ast/atomic.h
+++ b/src/ast/atomic.h
@@ -37,9 +37,6 @@
   /// @returns the pointee type
   Type* type() const { return const_cast<Type*>(subtype_); }
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/atomic_test.cc b/src/ast/atomic_test.cc
index 0d28d5b..33ffaae 100644
--- a/src/ast/atomic_test.cc
+++ b/src/ast/atomic_test.cc
@@ -29,12 +29,6 @@
   EXPECT_EQ(p->type(), i32);
 }
 
-TEST_F(AstAtomicTest, TypeName) {
-  auto* i32 = create<I32>();
-  auto* p = create<Atomic>(i32);
-  EXPECT_EQ(p->type_name(), "__atomic__i32");
-}
-
 TEST_F(AstAtomicTest, FriendlyName) {
   auto* i32 = create<I32>();
   auto* p = create<Atomic>(i32);
diff --git a/src/ast/binary_expression.cc b/src/ast/binary_expression.cc
index 6da0c57..c5fb860 100644
--- a/src/ast/binary_expression.cc
+++ b/src/ast/binary_expression.cc
@@ -46,20 +46,5 @@
   return ctx->dst->create<BinaryExpression>(src, op_, l, r);
 }
 
-void BinaryExpression::to_str(const sem::Info& sem,
-                              std::ostream& out,
-                              size_t indent) const {
-  make_indent(out, indent);
-  out << "Binary[" << result_type_str(sem) << "]{" << std::endl;
-  lhs_->to_str(sem, out, indent + 2);
-
-  make_indent(out, indent + 2);
-  out << op_ << std::endl;
-
-  rhs_->to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/binary_expression.h b/src/ast/binary_expression.h
index 1fa9f61..fedcd06 100644
--- a/src/ast/binary_expression.h
+++ b/src/ast/binary_expression.h
@@ -120,14 +120,6 @@
   /// @return the newly cloned node
   BinaryExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   BinaryExpression(const BinaryExpression&) = delete;
 
diff --git a/src/ast/binary_expression_test.cc b/src/ast/binary_expression_test.cc
index d5ef079..343e073 100644
--- a/src/ast/binary_expression_test.cc
+++ b/src/ast/binary_expression_test.cc
@@ -90,19 +90,6 @@
       "internal compiler error");
 }
 
-TEST_F(BinaryExpressionTest, ToStr) {
-  auto* lhs = Expr("lhs");
-  auto* rhs = Expr("rhs");
-
-  auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
-  EXPECT_EQ(str(r), R"(Binary[not set]{
-  Identifier[not set]{lhs}
-  equal
-  Identifier[not set]{rhs}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/binding_decoration.cc b/src/ast/binding_decoration.cc
index f172eca..3dbce49 100644
--- a/src/ast/binding_decoration.cc
+++ b/src/ast/binding_decoration.cc
@@ -34,13 +34,6 @@
   return "binding";
 }
 
-void BindingDecoration::to_str(const sem::Info&,
-                               std::ostream& out,
-                               size_t indent) const {
-  make_indent(out, indent);
-  out << "BindingDecoration{" << value_ << "}" << std::endl;
-}
-
 BindingDecoration* BindingDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/binding_decoration.h b/src/ast/binding_decoration.h
index fe4c3ee..0bb68a9 100644
--- a/src/ast/binding_decoration.h
+++ b/src/ast/binding_decoration.h
@@ -38,14 +38,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/binding_decoration_test.cc b/src/ast/binding_decoration_test.cc
index d9db97f..7ebf189 100644
--- a/src/ast/binding_decoration_test.cc
+++ b/src/ast/binding_decoration_test.cc
@@ -26,12 +26,6 @@
   EXPECT_EQ(2u, d->value());
 }
 
-TEST_F(BindingDecorationTest, ToStr) {
-  auto* d = create<BindingDecoration>(2);
-  EXPECT_EQ(str(d), R"(BindingDecoration{2}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/bitcast_expression.cc b/src/ast/bitcast_expression.cc
index aafa160..268a51f 100644
--- a/src/ast/bitcast_expression.cc
+++ b/src/ast/bitcast_expression.cc
@@ -42,16 +42,5 @@
   return ctx->dst->create<BitcastExpression>(src, ty, e);
 }
 
-void BitcastExpression::to_str(const sem::Info& sem,
-                               std::ostream& out,
-                               size_t indent) const {
-  make_indent(out, indent);
-  out << "Bitcast[" << result_type_str(sem) << "]<" << type_->type_name()
-      << ">{" << std::endl;
-  expr_->to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/bitcast_expression.h b/src/ast/bitcast_expression.h
index f901fec..413e64d 100644
--- a/src/ast/bitcast_expression.h
+++ b/src/ast/bitcast_expression.h
@@ -50,14 +50,6 @@
   /// @return the newly cloned node
   BitcastExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   BitcastExpression(const BitcastExpression&) = delete;
 
diff --git a/src/ast/bitcast_expression_test.cc b/src/ast/bitcast_expression_test.cc
index c058768..3af6a75 100644
--- a/src/ast/bitcast_expression_test.cc
+++ b/src/ast/bitcast_expression_test.cc
@@ -76,16 +76,6 @@
       "internal compiler error");
 }
 
-TEST_F(BitcastExpressionTest, ToStr) {
-  auto* expr = Expr("expr");
-
-  auto* exp = create<BitcastExpression>(ty.f32(), expr);
-  EXPECT_EQ(str(exp), R"(Bitcast[not set]<__f32>{
-  Identifier[not set]{expr}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/block_statement.cc b/src/ast/block_statement.cc
index c1d6e0b..01190f4 100644
--- a/src/ast/block_statement.cc
+++ b/src/ast/block_statement.cc
@@ -42,19 +42,5 @@
   return ctx->dst->create<BlockStatement>(src, stmts);
 }
 
-void BlockStatement::to_str(const sem::Info& sem,
-                            std::ostream& out,
-                            size_t indent) const {
-  make_indent(out, indent);
-  out << "Block{" << std::endl;
-
-  for (auto* stmt : *this) {
-    stmt->to_str(sem, out, indent + 2);
-  }
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/block_statement.h b/src/ast/block_statement.h
index 6bff1c5..dad2858 100644
--- a/src/ast/block_statement.h
+++ b/src/ast/block_statement.h
@@ -77,14 +77,6 @@
   /// @return the newly cloned node
   BlockStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   BlockStatement(const BlockStatement&) = delete;
 
diff --git a/src/ast/block_statement_test.cc b/src/ast/block_statement_test.cc
index 6cca315..031523a 100644
--- a/src/ast/block_statement_test.cc
+++ b/src/ast/block_statement_test.cc
@@ -66,17 +66,6 @@
       "internal compiler error");
 }
 
-TEST_F(BlockStatementTest, ToStr) {
-  auto* b = create<BlockStatement>(ast::StatementList{
-      create<DiscardStatement>(),
-  });
-
-  EXPECT_EQ(str(b), R"(Block{
-  Discard{}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/bool.cc b/src/ast/bool.cc
index d212812..5ca1cdb 100644
--- a/src/ast/bool.cc
+++ b/src/ast/bool.cc
@@ -28,10 +28,6 @@
 
 Bool::~Bool() = default;
 
-std::string Bool::type_name() const {
-  return "__bool";
-}
-
 std::string Bool::FriendlyName(const SymbolTable&) const {
   return "bool";
 }
diff --git a/src/ast/bool.h b/src/ast/bool.h
index ad5b858..8031319 100644
--- a/src/ast/bool.h
+++ b/src/ast/bool.h
@@ -39,9 +39,6 @@
   Bool(Bool&&);
   ~Bool() override;
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/bool_literal.cc b/src/ast/bool_literal.cc
index cddc27d..5c7554a 100644
--- a/src/ast/bool_literal.cc
+++ b/src/ast/bool_literal.cc
@@ -26,10 +26,6 @@
 
 BoolLiteral::~BoolLiteral() = default;
 
-std::string BoolLiteral::to_str(const sem::Info&) const {
-  return value_ ? "true" : "false";
-}
-
 std::string BoolLiteral::name() const {
   return value_ ? "__bool_true" : "__bool_false";
 }
diff --git a/src/ast/bool_literal.h b/src/ast/bool_literal.h
index 926e50d..b728276 100644
--- a/src/ast/bool_literal.h
+++ b/src/ast/bool_literal.h
@@ -40,10 +40,6 @@
   /// @returns the name for this literal. This name is unique to this value.
   std::string name() const override;
 
-  /// @param sem the semantic info for the program
-  /// @returns the literal as a string
-  std::string to_str(const sem::Info& sem) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/bool_literal_test.cc b/src/ast/bool_literal_test.cc
index e5cc313..d36671a 100644
--- a/src/ast/bool_literal_test.cc
+++ b/src/ast/bool_literal_test.cc
@@ -34,14 +34,6 @@
   ASSERT_TRUE(b->IsFalse());
 }
 
-TEST_F(BoolLiteralTest, ToStr) {
-  auto* t = create<BoolLiteral>(true);
-  auto* f = create<BoolLiteral>(false);
-
-  EXPECT_EQ(str(t), "true");
-  EXPECT_EQ(str(f), "false");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/bool_test.cc b/src/ast/bool_test.cc
index 7c8a32e..b4b0108 100644
--- a/src/ast/bool_test.cc
+++ b/src/ast/bool_test.cc
@@ -22,11 +22,6 @@
 
 using AstBoolTest = TestHelper;
 
-TEST_F(AstBoolTest, TypeName) {
-  auto* b = create<Bool>();
-  EXPECT_EQ(b->type_name(), "__bool");
-}
-
 TEST_F(AstBoolTest, FriendlyName) {
   auto* b = create<Bool>();
   EXPECT_EQ(b->FriendlyName(Symbols()), "bool");
diff --git a/src/ast/break_statement.cc b/src/ast/break_statement.cc
index cb38990..154bc32 100644
--- a/src/ast/break_statement.cc
+++ b/src/ast/break_statement.cc
@@ -34,12 +34,5 @@
   return ctx->dst->create<BreakStatement>(src);
 }
 
-void BreakStatement::to_str(const sem::Info&,
-                            std::ostream& out,
-                            size_t indent) const {
-  make_indent(out, indent);
-  out << "Break{}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/break_statement.h b/src/ast/break_statement.h
index 1373ece..67a20d0 100644
--- a/src/ast/break_statement.h
+++ b/src/ast/break_statement.h
@@ -37,14 +37,6 @@
   /// @return the newly cloned node
   BreakStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   BreakStatement(const BreakStatement&) = delete;
 };
diff --git a/src/ast/break_statement_test.cc b/src/ast/break_statement_test.cc
index 236f151..10d8e8e 100644
--- a/src/ast/break_statement_test.cc
+++ b/src/ast/break_statement_test.cc
@@ -34,12 +34,6 @@
   EXPECT_TRUE(stmt->Is<BreakStatement>());
 }
 
-TEST_F(BreakStatementTest, ToStr) {
-  auto* stmt = create<BreakStatement>();
-  EXPECT_EQ(str(stmt), R"(Break{}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/builtin_decoration.cc b/src/ast/builtin_decoration.cc
index 58e93bf..c891493 100644
--- a/src/ast/builtin_decoration.cc
+++ b/src/ast/builtin_decoration.cc
@@ -34,13 +34,6 @@
   return "builtin";
 }
 
-void BuiltinDecoration::to_str(const sem::Info&,
-                               std::ostream& out,
-                               size_t indent) const {
-  make_indent(out, indent);
-  out << "BuiltinDecoration{" << builtin_ << "}" << std::endl;
-}
-
 BuiltinDecoration* BuiltinDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/builtin_decoration.h b/src/ast/builtin_decoration.h
index 9711743..cfb201c 100644
--- a/src/ast/builtin_decoration.h
+++ b/src/ast/builtin_decoration.h
@@ -41,14 +41,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/builtin_decoration_test.cc b/src/ast/builtin_decoration_test.cc
index 4d71482..e42999d 100644
--- a/src/ast/builtin_decoration_test.cc
+++ b/src/ast/builtin_decoration_test.cc
@@ -26,12 +26,6 @@
   EXPECT_EQ(Builtin::kFragDepth, d->value());
 }
 
-TEST_F(BuiltinDecorationTest, ToStr) {
-  auto* d = create<BuiltinDecoration>(Builtin::kFragDepth);
-  EXPECT_EQ(str(d), R"(BuiltinDecoration{frag_depth}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/call_expression.cc b/src/ast/call_expression.cc
index 7bf0749..7cd0f2a 100644
--- a/src/ast/call_expression.cc
+++ b/src/ast/call_expression.cc
@@ -46,24 +46,5 @@
   return ctx->dst->create<CallExpression>(src, fn, p);
 }
 
-void CallExpression::to_str(const sem::Info& sem,
-                            std::ostream& out,
-                            size_t indent) const {
-  make_indent(out, indent);
-  out << "Call[" << result_type_str(sem) << "]{" << std::endl;
-  func_->to_str(sem, out, indent + 2);
-
-  make_indent(out, indent + 2);
-  out << "(" << std::endl;
-  for (auto* arg : args_)
-    arg->to_str(sem, out, indent + 4);
-
-  make_indent(out, indent + 2);
-  out << ")" << std::endl;
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/call_expression.h b/src/ast/call_expression.h
index 0a6de98..fef115a 100644
--- a/src/ast/call_expression.h
+++ b/src/ast/call_expression.h
@@ -50,14 +50,6 @@
   /// @return the newly cloned node
   CallExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   CallExpression(const CallExpression&) = delete;
 
diff --git a/src/ast/call_expression_test.cc b/src/ast/call_expression_test.cc
index 18c50f7..b36c39a 100644
--- a/src/ast/call_expression_test.cc
+++ b/src/ast/call_expression_test.cc
@@ -94,34 +94,6 @@
       "internal compiler error");
 }
 
-TEST_F(CallExpressionTest, ToStr_NoParams) {
-  auto* func = Expr("func");
-  auto* stmt = create<CallExpression>(func, ExpressionList{});
-  EXPECT_EQ(str(stmt), R"(Call[not set]{
-  Identifier[not set]{func}
-  (
-  )
-}
-)");
-}
-
-TEST_F(CallExpressionTest, ToStr_WithParams) {
-  auto* func = Expr("func");
-  ExpressionList params;
-  params.push_back(Expr("param1"));
-  params.push_back(Expr("param2"));
-
-  auto* stmt = create<CallExpression>(func, params);
-  EXPECT_EQ(str(stmt), R"(Call[not set]{
-  Identifier[not set]{func}
-  (
-    Identifier[not set]{param1}
-    Identifier[not set]{param2}
-  )
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/call_statement.cc b/src/ast/call_statement.cc
index 83361f6..c0ac3ad 100644
--- a/src/ast/call_statement.cc
+++ b/src/ast/call_statement.cc
@@ -40,11 +40,5 @@
   return ctx->dst->create<CallStatement>(src, call);
 }
 
-void CallStatement::to_str(const sem::Info& sem,
-                           std::ostream& out,
-                           size_t indent) const {
-  call_->to_str(sem, out, indent);
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/call_statement.h b/src/ast/call_statement.h
index ca52454..e389503 100644
--- a/src/ast/call_statement.h
+++ b/src/ast/call_statement.h
@@ -44,14 +44,6 @@
   /// @return the newly cloned node
   CallStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   CallStatement(const CallStatement&) = delete;
 
diff --git a/src/ast/call_statement_test.cc b/src/ast/call_statement_test.cc
index 1112ba4..3bff4cf 100644
--- a/src/ast/call_statement_test.cc
+++ b/src/ast/call_statement_test.cc
@@ -55,18 +55,6 @@
       "internal compiler error");
 }
 
-TEST_F(CallStatementTest, ToStr) {
-  auto* c = create<CallStatement>(
-      create<CallExpression>(Expr("func"), ExpressionList{}));
-
-  EXPECT_EQ(str(c), R"(Call[not set]{
-  Identifier[not set]{func}
-  (
-  )
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/case_statement.cc b/src/ast/case_statement.cc
index 0002e03..82406df 100644
--- a/src/ast/case_statement.cc
+++ b/src/ast/case_statement.cc
@@ -46,35 +46,5 @@
   return ctx->dst->create<CaseStatement>(src, sel, b);
 }
 
-void CaseStatement::to_str(const sem::Info& sem,
-                           std::ostream& out,
-                           size_t indent) const {
-  make_indent(out, indent);
-
-  if (IsDefault()) {
-    out << "Default{" << std::endl;
-  } else {
-    out << "Case ";
-    bool first = true;
-    for (auto* selector : selectors_) {
-      if (!first)
-        out << ", ";
-
-      first = false;
-      out << selector->to_str(sem);
-    }
-    out << "{" << std::endl;
-  }
-
-  if (body_ != nullptr) {
-    for (auto* stmt : *body_) {
-      stmt->to_str(sem, out, indent + 2);
-    }
-  }
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/case_statement.h b/src/ast/case_statement.h
index 68301a7..ebf3e9a 100644
--- a/src/ast/case_statement.h
+++ b/src/ast/case_statement.h
@@ -58,14 +58,6 @@
   /// @return the newly cloned node
   CaseStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   CaseStatement(const CaseStatement&) = delete;
 
diff --git a/src/ast/case_statement_test.cc b/src/ast/case_statement_test.cc
index 4110664..aeb9097 100644
--- a/src/ast/case_statement_test.cc
+++ b/src/ast/case_statement_test.cc
@@ -131,64 +131,6 @@
       "internal compiler error");
 }
 
-TEST_F(CaseStatementTest, ToStr_WithSelectors_i32) {
-  CaseSelectorList b;
-  b.push_back(create<SintLiteral>(-2));
-
-  auto* body = create<BlockStatement>(StatementList{
-      create<DiscardStatement>(),
-  });
-  auto* c = create<CaseStatement>(CaseSelectorList{b}, body);
-
-  EXPECT_EQ(str(c), R"(Case -2{
-  Discard{}
-}
-)");
-}
-
-TEST_F(CaseStatementTest, ToStr_WithSelectors_u32) {
-  CaseSelectorList b;
-  b.push_back(create<UintLiteral>(2));
-
-  auto* body = create<BlockStatement>(StatementList{
-      create<DiscardStatement>(),
-  });
-  auto* c = create<CaseStatement>(CaseSelectorList{b}, body);
-
-  EXPECT_EQ(str(c), R"(Case 2u{
-  Discard{}
-}
-)");
-}
-
-TEST_F(CaseStatementTest, ToStr_WithMultipleSelectors) {
-  CaseSelectorList b;
-  b.push_back(create<SintLiteral>(1));
-  b.push_back(create<SintLiteral>(2));
-
-  auto* body = create<BlockStatement>(StatementList{
-      create<DiscardStatement>(),
-  });
-  auto* c = create<CaseStatement>(b, body);
-
-  EXPECT_EQ(str(c), R"(Case 1, 2{
-  Discard{}
-}
-)");
-}
-
-TEST_F(CaseStatementTest, ToStr_WithoutSelectors) {
-  auto* body = create<BlockStatement>(StatementList{
-      create<DiscardStatement>(),
-  });
-  auto* c = create<CaseStatement>(CaseSelectorList{}, body);
-
-  EXPECT_EQ(str(c), R"(Default{
-  Discard{}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/continue_statement.cc b/src/ast/continue_statement.cc
index d52a926..a07572f 100644
--- a/src/ast/continue_statement.cc
+++ b/src/ast/continue_statement.cc
@@ -34,12 +34,5 @@
   return ctx->dst->create<ContinueStatement>(src);
 }
 
-void ContinueStatement::to_str(const sem::Info&,
-                               std::ostream& out,
-                               size_t indent) const {
-  make_indent(out, indent);
-  out << "Continue{}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/continue_statement.h b/src/ast/continue_statement.h
index 7ab3797..a618637 100644
--- a/src/ast/continue_statement.h
+++ b/src/ast/continue_statement.h
@@ -37,14 +37,6 @@
   /// @return the newly cloned node
   ContinueStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   ContinueStatement(const ContinueStatement&) = delete;
 };
diff --git a/src/ast/continue_statement_test.cc b/src/ast/continue_statement_test.cc
index dd3fd94..addad15 100644
--- a/src/ast/continue_statement_test.cc
+++ b/src/ast/continue_statement_test.cc
@@ -34,12 +34,6 @@
   EXPECT_TRUE(stmt->Is<ContinueStatement>());
 }
 
-TEST_F(ContinueStatementTest, ToStr) {
-  auto* stmt = create<ContinueStatement>();
-  EXPECT_EQ(str(stmt), R"(Continue{}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/depth_multisampled_texture.cc b/src/ast/depth_multisampled_texture.cc
index bfa5501..b822195 100644
--- a/src/ast/depth_multisampled_texture.cc
+++ b/src/ast/depth_multisampled_texture.cc
@@ -40,12 +40,6 @@
 
 DepthMultisampledTexture::~DepthMultisampledTexture() = default;
 
-std::string DepthMultisampledTexture::type_name() const {
-  std::ostringstream out;
-  out << "__depth_multisampled_texture_" << dim();
-  return out.str();
-}
-
 std::string DepthMultisampledTexture::FriendlyName(const SymbolTable&) const {
   std::ostringstream out;
   out << "texture_depth_multisampled_" << dim();
diff --git a/src/ast/depth_multisampled_texture.h b/src/ast/depth_multisampled_texture.h
index b25658e..4f23933 100644
--- a/src/ast/depth_multisampled_texture.h
+++ b/src/ast/depth_multisampled_texture.h
@@ -37,9 +37,6 @@
   DepthMultisampledTexture(DepthMultisampledTexture&&);
   ~DepthMultisampledTexture() override;
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/depth_multisampled_texture_test.cc b/src/ast/depth_multisampled_texture_test.cc
index 4c092a2..7c1ce0f 100644
--- a/src/ast/depth_multisampled_texture_test.cc
+++ b/src/ast/depth_multisampled_texture_test.cc
@@ -27,11 +27,6 @@
   EXPECT_EQ(d->dim(), TextureDimension::k2d);
 }
 
-TEST_F(AstDepthMultisampledTextureTest, TypeName) {
-  auto* d = create<DepthMultisampledTexture>(TextureDimension::k2d);
-  EXPECT_EQ(d->type_name(), "__depth_multisampled_texture_2d");
-}
-
 TEST_F(AstDepthMultisampledTextureTest, FriendlyName) {
   auto* d = create<DepthMultisampledTexture>(TextureDimension::k2d);
   EXPECT_EQ(d->FriendlyName(Symbols()), "texture_depth_multisampled_2d");
diff --git a/src/ast/depth_texture.cc b/src/ast/depth_texture.cc
index f33f9aa..8cad4ba 100644
--- a/src/ast/depth_texture.cc
+++ b/src/ast/depth_texture.cc
@@ -40,12 +40,6 @@
 
 DepthTexture::~DepthTexture() = default;
 
-std::string DepthTexture::type_name() const {
-  std::ostringstream out;
-  out << "__depth_texture_" << dim();
-  return out.str();
-}
-
 std::string DepthTexture::FriendlyName(const SymbolTable&) const {
   std::ostringstream out;
   out << "texture_depth_" << dim();
diff --git a/src/ast/depth_texture.h b/src/ast/depth_texture.h
index 7fe82e5..e0ff248 100644
--- a/src/ast/depth_texture.h
+++ b/src/ast/depth_texture.h
@@ -36,9 +36,6 @@
   DepthTexture(DepthTexture&&);
   ~DepthTexture() override;
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/depth_texture_test.cc b/src/ast/depth_texture_test.cc
index d7b7249..9e760d9 100644
--- a/src/ast/depth_texture_test.cc
+++ b/src/ast/depth_texture_test.cc
@@ -34,11 +34,6 @@
   EXPECT_EQ(d->dim(), TextureDimension::kCube);
 }
 
-TEST_F(AstDepthTextureTest, TypeName) {
-  auto* d = create<DepthTexture>(TextureDimension::kCube);
-  EXPECT_EQ(d->type_name(), "__depth_texture_cube");
-}
-
 TEST_F(AstDepthTextureTest, FriendlyName) {
   auto* d = create<DepthTexture>(TextureDimension::kCube);
   EXPECT_EQ(d->FriendlyName(Symbols()), "texture_depth_cube");
diff --git a/src/ast/discard_statement.cc b/src/ast/discard_statement.cc
index aa3ff08..129cc56 100644
--- a/src/ast/discard_statement.cc
+++ b/src/ast/discard_statement.cc
@@ -34,12 +34,5 @@
   return ctx->dst->create<DiscardStatement>(src);
 }
 
-void DiscardStatement::to_str(const sem::Info&,
-                              std::ostream& out,
-                              size_t indent) const {
-  make_indent(out, indent);
-  out << "Discard{}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/discard_statement.h b/src/ast/discard_statement.h
index 04e99d9..0a363c8 100644
--- a/src/ast/discard_statement.h
+++ b/src/ast/discard_statement.h
@@ -37,14 +37,6 @@
   /// @return the newly cloned node
   DiscardStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   DiscardStatement(const DiscardStatement&) = delete;
 };
diff --git a/src/ast/discard_statement_test.cc b/src/ast/discard_statement_test.cc
index dc79806..97b459e 100644
--- a/src/ast/discard_statement_test.cc
+++ b/src/ast/discard_statement_test.cc
@@ -44,12 +44,6 @@
   EXPECT_TRUE(stmt->Is<DiscardStatement>());
 }
 
-TEST_F(DiscardStatementTest, ToStr) {
-  auto* stmt = create<DiscardStatement>();
-  EXPECT_EQ(str(stmt), R"(Discard{}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/else_statement.cc b/src/ast/else_statement.cc
index 40271d3..ed88bbd 100644
--- a/src/ast/else_statement.cc
+++ b/src/ast/else_statement.cc
@@ -43,36 +43,5 @@
   return ctx->dst->create<ElseStatement>(src, cond, b);
 }
 
-void ElseStatement::to_str(const sem::Info& sem,
-                           std::ostream& out,
-                           size_t indent) const {
-  make_indent(out, indent);
-  out << "Else{" << std::endl;
-  if (condition_ != nullptr) {
-    make_indent(out, indent + 2);
-    out << "(" << std::endl;
-
-    condition_->to_str(sem, out, indent + 4);
-
-    make_indent(out, indent + 2);
-    out << ")" << std::endl;
-  }
-
-  make_indent(out, indent + 2);
-  out << "{" << std::endl;
-
-  if (body_ != nullptr) {
-    for (auto* stmt : *body_) {
-      stmt->to_str(sem, out, indent + 4);
-    }
-  }
-
-  make_indent(out, indent + 2);
-  out << "}" << std::endl;
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/else_statement.h b/src/ast/else_statement.h
index aa49f9a..26a03c5 100644
--- a/src/ast/else_statement.h
+++ b/src/ast/else_statement.h
@@ -55,14 +55,6 @@
   /// @return the newly cloned node
   ElseStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   ElseStatement(const ElseStatement&) = delete;
 
diff --git a/src/ast/else_statement_test.cc b/src/ast/else_statement_test.cc
index 5036e80..5f3e697 100644
--- a/src/ast/else_statement_test.cc
+++ b/src/ast/else_statement_test.cc
@@ -89,36 +89,6 @@
       "internal compiler error");
 }
 
-TEST_F(ElseStatementTest, ToStr) {
-  auto* cond = Expr(true);
-  auto* body = create<BlockStatement>(StatementList{
-      create<DiscardStatement>(),
-  });
-  auto* e = create<ElseStatement>(cond, body);
-  EXPECT_EQ(str(e), R"(Else{
-  (
-    ScalarConstructor[not set]{true}
-  )
-  {
-    Discard{}
-  }
-}
-)");
-}
-
-TEST_F(ElseStatementTest, ToStr_NoCondition) {
-  auto* body = create<BlockStatement>(StatementList{
-      create<DiscardStatement>(),
-  });
-  auto* e = create<ElseStatement>(nullptr, body);
-  EXPECT_EQ(str(e), R"(Else{
-  {
-    Discard{}
-  }
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/external_texture.cc b/src/ast/external_texture.cc
index bc50f53..16376b6 100644
--- a/src/ast/external_texture.cc
+++ b/src/ast/external_texture.cc
@@ -29,10 +29,6 @@
 
 ExternalTexture::~ExternalTexture() = default;
 
-std::string ExternalTexture::type_name() const {
-  return "__external_texture";
-}
-
 std::string ExternalTexture::FriendlyName(const SymbolTable&) const {
   return "texture_external";
 }
diff --git a/src/ast/external_texture.h b/src/ast/external_texture.h
index a7bbea4..38d1791 100644
--- a/src/ast/external_texture.h
+++ b/src/ast/external_texture.h
@@ -34,9 +34,6 @@
   ExternalTexture(ExternalTexture&&);
   ~ExternalTexture() override;
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/external_texture_test.cc b/src/ast/external_texture_test.cc
index ff11eae..7cd7220 100644
--- a/src/ast/external_texture_test.cc
+++ b/src/ast/external_texture_test.cc
@@ -36,11 +36,6 @@
   EXPECT_EQ(ty->dim(), ast::TextureDimension::k2d);
 }
 
-TEST_F(AstExternalTextureTest, TypeName) {
-  auto* ty = create<ExternalTexture>();
-  EXPECT_EQ(ty->type_name(), "__external_texture");
-}
-
 TEST_F(AstExternalTextureTest, FriendlyName) {
   auto* ty = create<ExternalTexture>();
   EXPECT_EQ(ty->FriendlyName(Symbols()), "texture_external");
diff --git a/src/ast/f32.cc b/src/ast/f32.cc
index 66a8fe0..039cf7f 100644
--- a/src/ast/f32.cc
+++ b/src/ast/f32.cc
@@ -28,10 +28,6 @@
 
 F32::~F32() = default;
 
-std::string F32::type_name() const {
-  return "__f32";
-}
-
 std::string F32::FriendlyName(const SymbolTable&) const {
   return "f32";
 }
diff --git a/src/ast/f32.h b/src/ast/f32.h
index 42e9bc7..1e0d4c6 100644
--- a/src/ast/f32.h
+++ b/src/ast/f32.h
@@ -33,9 +33,6 @@
   F32(F32&&);
   ~F32() override;
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/f32_test.cc b/src/ast/f32_test.cc
index 12eeec8..f8ea6b8 100644
--- a/src/ast/f32_test.cc
+++ b/src/ast/f32_test.cc
@@ -22,11 +22,6 @@
 
 using AstF32Test = TestHelper;
 
-TEST_F(AstF32Test, TypeName) {
-  auto* f = create<F32>();
-  EXPECT_EQ(f->type_name(), "__f32");
-}
-
 TEST_F(AstF32Test, FriendlyName) {
   auto* f = create<F32>();
   EXPECT_EQ(f->FriendlyName(Symbols()), "f32");
diff --git a/src/ast/fallthrough_statement.cc b/src/ast/fallthrough_statement.cc
index 855e7f7..4fcc2f7 100644
--- a/src/ast/fallthrough_statement.cc
+++ b/src/ast/fallthrough_statement.cc
@@ -35,12 +35,5 @@
   return ctx->dst->create<FallthroughStatement>(src);
 }
 
-void FallthroughStatement::to_str(const sem::Info&,
-                                  std::ostream& out,
-                                  size_t indent) const {
-  make_indent(out, indent);
-  out << "Fallthrough{}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/fallthrough_statement.h b/src/ast/fallthrough_statement.h
index 9cfee21..7976b2b 100644
--- a/src/ast/fallthrough_statement.h
+++ b/src/ast/fallthrough_statement.h
@@ -37,14 +37,6 @@
   /// @return the newly cloned node
   FallthroughStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   FallthroughStatement(const FallthroughStatement&) = delete;
 };
diff --git a/src/ast/fallthrough_statement_test.cc b/src/ast/fallthrough_statement_test.cc
index 047af88..9081a8d 100644
--- a/src/ast/fallthrough_statement_test.cc
+++ b/src/ast/fallthrough_statement_test.cc
@@ -42,12 +42,6 @@
   EXPECT_TRUE(stmt->Is<FallthroughStatement>());
 }
 
-TEST_F(FallthroughStatementTest, ToStr) {
-  auto* stmt = create<FallthroughStatement>();
-  EXPECT_EQ(str(stmt), R"(Fallthrough{}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/float_literal.cc b/src/ast/float_literal.cc
index b6453e9..57cd430 100644
--- a/src/ast/float_literal.cc
+++ b/src/ast/float_literal.cc
@@ -30,10 +30,6 @@
 
 FloatLiteral::~FloatLiteral() = default;
 
-std::string FloatLiteral::to_str(const sem::Info&) const {
-  return std::to_string(value_);
-}
-
 std::string FloatLiteral::name() const {
   std::ostringstream out;
   out.flags(out.flags() | std::ios_base::showpoint);
diff --git a/src/ast/float_literal.h b/src/ast/float_literal.h
index 5b247b4..5453430 100644
--- a/src/ast/float_literal.h
+++ b/src/ast/float_literal.h
@@ -38,10 +38,6 @@
   /// @returns the name for this literal. This name is unique to this value.
   std::string name() const override;
 
-  /// @param sem the semantic info for the program
-  /// @returns the literal as a string
-  std::string to_str(const sem::Info& sem) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/float_literal_test.cc b/src/ast/float_literal_test.cc
index e1e0498..a6483a7 100644
--- a/src/ast/float_literal_test.cc
+++ b/src/ast/float_literal_test.cc
@@ -26,11 +26,6 @@
   EXPECT_EQ(f->value(), 47.2f);
 }
 
-TEST_F(FloatLiteralTest, ToStr) {
-  auto* f = create<FloatLiteral>(42.1f);
-  EXPECT_EQ(str(f), "42.099998");
-}
-
 TEST_F(FloatLiteralTest, ToName) {
   auto* f = create<FloatLiteral>(42.1f);
   EXPECT_EQ(f->name(), "__float42.0999985");
diff --git a/src/ast/for_loop_statement.cc b/src/ast/for_loop_statement.cc
index dc77b80..50d8184 100644
--- a/src/ast/for_loop_statement.cc
+++ b/src/ast/for_loop_statement.cc
@@ -55,38 +55,5 @@
   return ctx->dst->create<ForLoopStatement>(src, init, cond, cont, b);
 }
 
-void ForLoopStatement::to_str(const sem::Info& sem,
-                              std::ostream& out,
-                              size_t indent) const {
-  make_indent(out, indent);
-  out << "ForLoop {" << std::endl;
-
-  if (initializer_) {
-    make_indent(out, indent + 2);
-    out << "initializer:" << std::endl;
-    initializer_->to_str(sem, out, indent + 4);
-  }
-
-  if (condition_) {
-    make_indent(out, indent + 2);
-    out << "condition:" << std::endl;
-    condition_->to_str(sem, out, indent + 4);
-  }
-
-  if (continuing_) {
-    make_indent(out, indent + 2);
-    out << "continuing:" << std::endl;
-    continuing_->to_str(sem, out, indent + 4);
-  }
-
-  make_indent(out, indent + 2);
-  out << "body:" << std::endl;
-  for (auto* stmt : *body_) {
-    stmt->to_str(sem, out, indent + 4);
-  }
-
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/for_loop_statement.h b/src/ast/for_loop_statement.h
index 124a17f..e5dbcc7 100644
--- a/src/ast/for_loop_statement.h
+++ b/src/ast/for_loop_statement.h
@@ -68,14 +68,6 @@
   /// @return the newly cloned node
   ForLoopStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   ForLoopStatement(const ForLoopStatement&) = delete;
 
diff --git a/src/ast/for_loop_statement_test.cc b/src/ast/for_loop_statement_test.cc
index 1542cd0..8217e3f 100644
--- a/src/ast/for_loop_statement_test.cc
+++ b/src/ast/for_loop_statement_test.cc
@@ -99,77 +99,6 @@
       "internal compiler error");
 }
 
-TEST_F(ForLoopStatementTest, ToStr) {
-  auto* body = Block(Return());
-  auto* l = For(nullptr, nullptr, nullptr, body);
-
-  EXPECT_EQ(str(l), R"(ForLoop {
-  body:
-    Return{}
-}
-)");
-}
-
-TEST_F(ForLoopStatementTest, ToStr_With_Init) {
-  auto* body = Block(Return());
-  auto* l = For(Block(), nullptr, nullptr, body);
-
-  EXPECT_EQ(str(l), R"(ForLoop {
-  initializer:
-    Block{
-    }
-  body:
-    Return{}
-}
-)");
-}
-
-TEST_F(ForLoopStatementTest, ToStr_With_Cond) {
-  auto* body = Block(Return());
-  auto* l = For(nullptr, Expr(true), nullptr, body);
-
-  EXPECT_EQ(str(l), R"(ForLoop {
-  condition:
-    ScalarConstructor[not set]{true}
-  body:
-    Return{}
-}
-)");
-}
-
-TEST_F(ForLoopStatementTest, ToStr_With_Cont) {
-  auto* body = Block(Return());
-  auto* l = For(nullptr, nullptr, Block(), body);
-
-  EXPECT_EQ(str(l), R"(ForLoop {
-  continuing:
-    Block{
-    }
-  body:
-    Return{}
-}
-)");
-}
-
-TEST_F(ForLoopStatementTest, ToStr_With_All) {
-  auto* body = Block(Return());
-  auto* l = For(Block(), Expr(true), Block(), body);
-
-  EXPECT_EQ(str(l), R"(ForLoop {
-  initializer:
-    Block{
-    }
-  condition:
-    ScalarConstructor[not set]{true}
-  continuing:
-    Block{
-    }
-  body:
-    Return{}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/function.cc b/src/ast/function.cc
index c0014e1..d6dfc9e 100644
--- a/src/ast/function.cc
+++ b/src/ast/function.cc
@@ -81,56 +81,6 @@
   return ctx->dst->create<Function>(src, sym, p, ret, b, decos, ret_decos);
 }
 
-void Function::to_str(const sem::Info& sem,
-                      std::ostream& out,
-                      size_t indent) const {
-  make_indent(out, indent);
-  out << "Function " << symbol_.to_str() << " -> " << return_type_->type_name()
-      << std::endl;
-
-  for (auto* deco : decorations()) {
-    deco->to_str(sem, out, indent);
-  }
-
-  make_indent(out, indent);
-  out << "(";
-
-  if (params_.size() > 0) {
-    out << std::endl;
-
-    for (auto* param : params_)
-      param->to_str(sem, out, indent + 2);
-
-    make_indent(out, indent);
-  }
-  out << ")" << std::endl;
-
-  make_indent(out, indent);
-  out << "{" << std::endl;
-
-  if (body_ != nullptr) {
-    for (auto* stmt : *body_) {
-      stmt->to_str(sem, out, indent + 2);
-    }
-  }
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
-std::string Function::type_name() const {
-  std::ostringstream out;
-
-  out << "__func" + return_type_->type_name();
-  for (auto* param : params_) {
-    // No need for the sem::Variable here, functions params must have a
-    // type
-    out << param->type()->type_name();
-  }
-
-  return out.str();
-}
-
 Function* FunctionList::Find(Symbol sym) const {
   for (auto* func : *this) {
     if (func->symbol() == sym) {
diff --git a/src/ast/function.h b/src/ast/function.h
index 86e85bb..2cc83e9 100644
--- a/src/ast/function.h
+++ b/src/ast/function.h
@@ -94,17 +94,6 @@
   /// @return the newly cloned node
   Function* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
-  /// @returns the type name for this function
-  std::string type_name() const;
-
  private:
   Function(const Function&) = delete;
 
diff --git a/src/ast/function_test.cc b/src/ast/function_test.cc
index 61ee8a6..839e9b2 100644
--- a/src/ast/function_test.cc
+++ b/src/ast/function_test.cc
@@ -139,81 +139,6 @@
       "internal compiler error");
 }
 
-TEST_F(FunctionTest, ToStr) {
-  auto* f = Func("func", VariableList{}, ty.void_(),
-                 StatementList{
-                     create<DiscardStatement>(),
-                 },
-                 DecorationList{});
-
-  EXPECT_EQ(str(f), R"(Function func -> __void
-()
-{
-  Discard{}
-}
-)");
-}
-
-TEST_F(FunctionTest, ToStr_WithDecoration) {
-  auto* f = Func("func", VariableList{}, ty.void_(),
-                 StatementList{
-                     create<DiscardStatement>(),
-                 },
-                 DecorationList{WorkgroupSize(2, 4, 6)});
-
-  EXPECT_EQ(str(f), R"(Function func -> __void
-WorkgroupDecoration{
-  ScalarConstructor[not set]{2}
-  ScalarConstructor[not set]{4}
-  ScalarConstructor[not set]{6}
-}
-()
-{
-  Discard{}
-}
-)");
-}
-
-TEST_F(FunctionTest, ToStr_WithParams) {
-  VariableList params;
-  params.push_back(Param("var", ty.i32()));
-
-  auto* f = Func("func", params, ty.void_(),
-                 StatementList{
-                     create<DiscardStatement>(),
-                 },
-                 DecorationList{});
-
-  EXPECT_EQ(str(f), R"(Function func -> __void
-(
-  VariableConst{
-    var
-    none
-    undefined
-    __i32
-  }
-)
-{
-  Discard{}
-}
-)");
-}
-
-TEST_F(FunctionTest, TypeName) {
-  auto* f = Func("func", VariableList{}, ty.void_(), StatementList{},
-                 DecorationList{});
-  EXPECT_EQ(f->type_name(), "__func__void");
-}
-
-TEST_F(FunctionTest, TypeName_WithParams) {
-  VariableList params;
-  params.push_back(Param("var1", ty.i32()));
-  params.push_back(Param("var2", ty.f32()));
-
-  auto* f = Func("func", params, ty.void_(), StatementList{}, DecorationList{});
-  EXPECT_EQ(f->type_name(), "__func__void__i32__f32");
-}
-
 TEST_F(FunctionTest, GetLastStatement) {
   VariableList params;
   auto* stmt = create<DiscardStatement>();
diff --git a/src/ast/group_decoration.cc b/src/ast/group_decoration.cc
index 44175c7..c72f444 100644
--- a/src/ast/group_decoration.cc
+++ b/src/ast/group_decoration.cc
@@ -34,13 +34,6 @@
   return "group";
 }
 
-void GroupDecoration::to_str(const sem::Info&,
-                             std::ostream& out,
-                             size_t indent) const {
-  make_indent(out, indent);
-  out << "GroupDecoration{" << value_ << "}" << std::endl;
-}
-
 GroupDecoration* GroupDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/group_decoration.h b/src/ast/group_decoration.h
index b621f1a..1d86128 100644
--- a/src/ast/group_decoration.h
+++ b/src/ast/group_decoration.h
@@ -38,14 +38,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/group_decoration_test.cc b/src/ast/group_decoration_test.cc
index ec3327f..7398b75 100644
--- a/src/ast/group_decoration_test.cc
+++ b/src/ast/group_decoration_test.cc
@@ -26,13 +26,6 @@
   EXPECT_EQ(2u, d->value());
 }
 
-
-TEST_F(GroupDecorationTest, ToStr) {
-  auto* d = create<GroupDecoration>(2);
-  EXPECT_EQ(str(d), R"(GroupDecoration{2}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/i32.cc b/src/ast/i32.cc
index 358e775..e59ab25 100644
--- a/src/ast/i32.cc
+++ b/src/ast/i32.cc
@@ -28,10 +28,6 @@
 
 I32::~I32() = default;
 
-std::string I32::type_name() const {
-  return "__i32";
-}
-
 std::string I32::FriendlyName(const SymbolTable&) const {
   return "i32";
 }
diff --git a/src/ast/i32.h b/src/ast/i32.h
index 182f102..5134b8b 100644
--- a/src/ast/i32.h
+++ b/src/ast/i32.h
@@ -33,9 +33,6 @@
   I32(I32&&);
   ~I32() override;
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/i32_test.cc b/src/ast/i32_test.cc
index 6c224ca..df8bca8 100644
--- a/src/ast/i32_test.cc
+++ b/src/ast/i32_test.cc
@@ -22,11 +22,6 @@
 
 using AstI32Test = TestHelper;
 
-TEST_F(AstI32Test, TypeName) {
-  auto* i = create<I32>();
-  EXPECT_EQ(i->type_name(), "__i32");
-}
-
 TEST_F(AstI32Test, FriendlyName) {
   auto* i = create<I32>();
   EXPECT_EQ(i->FriendlyName(Symbols()), "i32");
diff --git a/src/ast/identifier_expression.cc b/src/ast/identifier_expression.cc
index df9e084..c316062 100644
--- a/src/ast/identifier_expression.cc
+++ b/src/ast/identifier_expression.cc
@@ -40,13 +40,5 @@
   return ctx->dst->create<IdentifierExpression>(src, sym);
 }
 
-void IdentifierExpression::to_str(const sem::Info& sem,
-                                  std::ostream& out,
-                                  size_t indent) const {
-  make_indent(out, indent);
-  out << "Identifier[" << result_type_str(sem) << "]{" << sym_.to_str() << "}"
-      << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/identifier_expression.h b/src/ast/identifier_expression.h
index 3deff16..b47a150 100644
--- a/src/ast/identifier_expression.h
+++ b/src/ast/identifier_expression.h
@@ -41,14 +41,6 @@
   /// @return the newly cloned node
   IdentifierExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   IdentifierExpression(const IdentifierExpression&) = delete;
 
diff --git a/src/ast/identifier_expression_test.cc b/src/ast/identifier_expression_test.cc
index 82a70f4..6d22969 100644
--- a/src/ast/identifier_expression_test.cc
+++ b/src/ast/identifier_expression_test.cc
@@ -59,12 +59,6 @@
       "internal compiler error");
 }
 
-TEST_F(IdentifierExpressionTest, ToStr) {
-  auto* i = Expr("ident");
-  EXPECT_EQ(str(i), R"(Identifier[not set]{ident}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/if_statement.cc b/src/ast/if_statement.cc
index b0daf49..0d4db22 100644
--- a/src/ast/if_statement.cc
+++ b/src/ast/if_statement.cc
@@ -53,44 +53,5 @@
   return ctx->dst->create<IfStatement>(src, cond, b, el);
 }
 
-void IfStatement::to_str(const sem::Info& sem,
-                         std::ostream& out,
-                         size_t indent) const {
-  make_indent(out, indent);
-  out << "If{" << std::endl;
-
-  // Open if conditional
-  make_indent(out, indent + 2);
-  out << "(" << std::endl;
-
-  condition_->to_str(sem, out, indent + 4);
-
-  // Close if conditional
-  make_indent(out, indent + 2);
-  out << ")" << std::endl;
-
-  // Open if body
-  make_indent(out, indent + 2);
-  out << "{" << std::endl;
-
-  if (body_ != nullptr) {
-    for (auto* stmt : *body_) {
-      stmt->to_str(sem, out, indent + 4);
-    }
-  }
-
-  // Close the if body
-  make_indent(out, indent + 2);
-  out << "}" << std::endl;
-
-  // Close the If
-  make_indent(out, indent);
-  out << "}" << std::endl;
-
-  for (auto* e : else_statements_) {
-    e->to_str(sem, out, indent);
-  }
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/if_statement.h b/src/ast/if_statement.h
index 6414f23..ed12df4 100644
--- a/src/ast/if_statement.h
+++ b/src/ast/if_statement.h
@@ -59,14 +59,6 @@
   /// @return the newly cloned node
   IfStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   IfStatement(const IfStatement&) = delete;
 
diff --git a/src/ast/if_statement_test.cc b/src/ast/if_statement_test.cc
index c19ec8b..c5740ce 100644
--- a/src/ast/if_statement_test.cc
+++ b/src/ast/if_statement_test.cc
@@ -101,60 +101,6 @@
       "internal compiler error");
 }
 
-TEST_F(IfStatementTest, ToStr) {
-  auto* cond = Expr("cond");
-  auto* stmt = create<IfStatement>(cond, Block(create<DiscardStatement>()),
-                                   ElseStatementList{});
-
-  EXPECT_EQ(str(stmt), R"(If{
-  (
-    Identifier[not set]{cond}
-  )
-  {
-    Discard{}
-  }
-}
-)");
-}
-
-TEST_F(IfStatementTest, ToStr_WithElseStatements) {
-  auto* cond = Expr("cond");
-  auto* body = Block(create<DiscardStatement>());
-  auto* else_if_body = Block(create<DiscardStatement>());
-  auto* else_body =
-      Block(create<DiscardStatement>(), create<DiscardStatement>());
-  auto* stmt = create<IfStatement>(
-      cond, body,
-      ElseStatementList{
-          create<ElseStatement>(Expr("ident"), else_if_body),
-          create<ElseStatement>(nullptr, else_body),
-      });
-
-  EXPECT_EQ(str(stmt), R"(If{
-  (
-    Identifier[not set]{cond}
-  )
-  {
-    Discard{}
-  }
-}
-Else{
-  (
-    Identifier[not set]{ident}
-  )
-  {
-    Discard{}
-  }
-}
-Else{
-  {
-    Discard{}
-    Discard{}
-  }
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/internal_decoration.cc b/src/ast/internal_decoration.cc
index f5e2237..01489f5 100644
--- a/src/ast/internal_decoration.cc
+++ b/src/ast/internal_decoration.cc
@@ -28,12 +28,5 @@
   return "internal";
 }
 
-void InternalDecoration::to_str(const sem::Info&,
-                                std::ostream& out,
-                                size_t indent) const {
-  make_indent(out, indent);
-  out << "tint_internal(" << InternalName() << ")";
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/internal_decoration.h b/src/ast/internal_decoration.h
index 55bb65b..7196056 100644
--- a/src/ast/internal_decoration.h
+++ b/src/ast/internal_decoration.h
@@ -40,14 +40,6 @@
 
   /// @returns the WGSL name for the decoration
   std::string name() const override;
-
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
 };
 
 }  // namespace ast
diff --git a/src/ast/interpolate_decoration.cc b/src/ast/interpolate_decoration.cc
index 333d7c2..35a12f5 100644
--- a/src/ast/interpolate_decoration.cc
+++ b/src/ast/interpolate_decoration.cc
@@ -35,14 +35,6 @@
   return "interpolate";
 }
 
-void InterpolateDecoration::to_str(const sem::Info&,
-                                   std::ostream& out,
-                                   size_t indent) const {
-  make_indent(out, indent);
-  out << "InterpolateDecoration{" << type_ << " " << sampling_ << "}"
-      << std::endl;
-}
-
 InterpolateDecoration* InterpolateDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/interpolate_decoration.h b/src/ast/interpolate_decoration.h
index 75e20b6..a219499 100644
--- a/src/ast/interpolate_decoration.h
+++ b/src/ast/interpolate_decoration.h
@@ -53,14 +53,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/interpolate_decoration_test.cc b/src/ast/interpolate_decoration_test.cc
index e80bf21..d6ce1c6 100644
--- a/src/ast/interpolate_decoration_test.cc
+++ b/src/ast/interpolate_decoration_test.cc
@@ -29,13 +29,6 @@
   EXPECT_EQ(InterpolationSampling::kCenter, d->sampling());
 }
 
-TEST_F(InterpolateDecorationTest, ToStr) {
-  auto* d = create<InterpolateDecoration>(InterpolationType::kPerspective,
-                                          InterpolationSampling::kSample);
-  EXPECT_EQ(str(d), R"(InterpolateDecoration{perspective sample}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/invariant_decoration.cc b/src/ast/invariant_decoration.cc
index 590ea0b..6425d14 100644
--- a/src/ast/invariant_decoration.cc
+++ b/src/ast/invariant_decoration.cc
@@ -31,13 +31,6 @@
   return "invariant";
 }
 
-void InvariantDecoration::to_str(const sem::Info&,
-                                 std::ostream& out,
-                                 size_t indent) const {
-  make_indent(out, indent);
-  out << "InvariantDecoration";
-}
-
 InvariantDecoration* InvariantDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/invariant_decoration.h b/src/ast/invariant_decoration.h
index 1118658..290741c 100644
--- a/src/ast/invariant_decoration.h
+++ b/src/ast/invariant_decoration.h
@@ -34,14 +34,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/invariant_decoration_test.cc b/src/ast/invariant_decoration_test.cc
index f710e4e..819e047 100644
--- a/src/ast/invariant_decoration_test.cc
+++ b/src/ast/invariant_decoration_test.cc
@@ -22,11 +22,6 @@
 
 using InvariantDecorationTest = TestHelper;
 
-TEST_F(InvariantDecorationTest, ToStr) {
-  auto* d = create<InvariantDecoration>();
-  EXPECT_EQ(str(d), "InvariantDecoration");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/literal.cc b/src/ast/literal.cc
index 2690708..0c6e26f 100644
--- a/src/ast/literal.cc
+++ b/src/ast/literal.cc
@@ -24,12 +24,5 @@
 
 Literal::~Literal() = default;
 
-void Literal::to_str(const sem::Info& sem,
-                     std::ostream& out,
-                     size_t indent) const {
-  make_indent(out, indent);
-  out << to_str(sem);
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/literal.h b/src/ast/literal.h
index e8828d7..0b86d62 100644
--- a/src/ast/literal.h
+++ b/src/ast/literal.h
@@ -27,18 +27,6 @@
  public:
   ~Literal() override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
-  /// @param sem the semantic info for the program
-  /// @returns the literal as a string
-  virtual std::string to_str(const sem::Info& sem) const = 0;
-
   /// @returns the name for this literal. This name is unique to this value.
   virtual std::string name() const = 0;
 
diff --git a/src/ast/location_decoration.cc b/src/ast/location_decoration.cc
index 932e6d1..ff5b9c9 100644
--- a/src/ast/location_decoration.cc
+++ b/src/ast/location_decoration.cc
@@ -34,13 +34,6 @@
   return "location";
 }
 
-void LocationDecoration::to_str(const sem::Info&,
-                                std::ostream& out,
-                                size_t indent) const {
-  make_indent(out, indent);
-  out << "LocationDecoration{" << value_ << "}" << std::endl;
-}
-
 LocationDecoration* LocationDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/location_decoration.h b/src/ast/location_decoration.h
index 415a330..7e8eef8 100644
--- a/src/ast/location_decoration.h
+++ b/src/ast/location_decoration.h
@@ -40,14 +40,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/location_decoration_test.cc b/src/ast/location_decoration_test.cc
index 706b958..66a57e1 100644
--- a/src/ast/location_decoration_test.cc
+++ b/src/ast/location_decoration_test.cc
@@ -26,12 +26,6 @@
   EXPECT_EQ(2u, d->value());
 }
 
-TEST_F(LocationDecorationTest, ToStr) {
-  auto* d = create<LocationDecoration>(2);
-  EXPECT_EQ(str(d), R"(LocationDecoration{2}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/loop_statement.cc b/src/ast/loop_statement.cc
index e6e2134..cbbca68 100644
--- a/src/ast/loop_statement.cc
+++ b/src/ast/loop_statement.cc
@@ -43,33 +43,5 @@
   return ctx->dst->create<LoopStatement>(src, b, cont);
 }
 
-void LoopStatement::to_str(const sem::Info& sem,
-                           std::ostream& out,
-                           size_t indent) const {
-  make_indent(out, indent);
-  out << "Loop{" << std::endl;
-
-  if (body_ != nullptr) {
-    for (auto* stmt : *body_) {
-      stmt->to_str(sem, out, indent + 2);
-    }
-  }
-
-  if (continuing_ != nullptr && continuing_->size() > 0) {
-    make_indent(out, indent + 2);
-    out << "continuing {" << std::endl;
-
-    for (auto* stmt : *continuing_) {
-      stmt->to_str(sem, out, indent + 4);
-    }
-
-    make_indent(out, indent + 2);
-    out << "}" << std::endl;
-  }
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/loop_statement.h b/src/ast/loop_statement.h
index 792d886..9ddafe2 100644
--- a/src/ast/loop_statement.h
+++ b/src/ast/loop_statement.h
@@ -56,14 +56,6 @@
   /// @return the newly cloned node
   LoopStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   LoopStatement(const LoopStatement&) = delete;
 
diff --git a/src/ast/loop_statement_test.cc b/src/ast/loop_statement_test.cc
index 548515e..cc06c76 100644
--- a/src/ast/loop_statement_test.cc
+++ b/src/ast/loop_statement_test.cc
@@ -100,31 +100,6 @@
       "internal compiler error");
 }
 
-TEST_F(LoopStatementTest, ToStr) {
-  auto* body = Block(create<DiscardStatement>());
-
-  auto* l = create<LoopStatement>(body, nullptr);
-  EXPECT_EQ(str(l), R"(Loop{
-  Discard{}
-}
-)");
-}
-
-TEST_F(LoopStatementTest, ToStr_WithContinuing) {
-  auto* body = Block(create<DiscardStatement>());
-
-  auto* continuing = Block(create<DiscardStatement>());
-
-  auto* l = create<LoopStatement>(body, continuing);
-  EXPECT_EQ(str(l), R"(Loop{
-  Discard{}
-  continuing {
-    Discard{}
-  }
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/matrix.cc b/src/ast/matrix.cc
index 23ab60b..357fe52 100644
--- a/src/ast/matrix.cc
+++ b/src/ast/matrix.cc
@@ -41,11 +41,6 @@
 
 Matrix::~Matrix() = default;
 
-std::string Matrix::type_name() const {
-  return "__mat_" + std::to_string(rows_) + "_" + std::to_string(columns_) +
-         subtype_->type_name();
-}
-
 std::string Matrix::FriendlyName(const SymbolTable& symbols) const {
   std::ostringstream out;
   out << "mat" << columns_ << "x" << rows_ << "<"
diff --git a/src/ast/matrix.h b/src/ast/matrix.h
index 1a419dd0..27417c2 100644
--- a/src/ast/matrix.h
+++ b/src/ast/matrix.h
@@ -47,9 +47,6 @@
   /// @returns the number of columns in the matrix
   uint32_t columns() const { return columns_; }
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/matrix_test.cc b/src/ast/matrix_test.cc
index 30b14a1..4e1ed2c 100644
--- a/src/ast/matrix_test.cc
+++ b/src/ast/matrix_test.cc
@@ -41,12 +41,6 @@
   EXPECT_EQ(m->columns(), 4u);
 }
 
-TEST_F(AstMatrixTest, TypeName) {
-  auto* i32 = create<I32>();
-  auto* m = create<Matrix>(i32, 2, 3);
-  EXPECT_EQ(m->type_name(), "__mat_2_3__i32");
-}
-
 TEST_F(AstMatrixTest, FriendlyName) {
   auto* i32 = create<I32>();
   auto* m = create<Matrix>(i32, 3, 2);
diff --git a/src/ast/member_accessor_expression.cc b/src/ast/member_accessor_expression.cc
index 7a25750..7e75488 100644
--- a/src/ast/member_accessor_expression.cc
+++ b/src/ast/member_accessor_expression.cc
@@ -46,16 +46,5 @@
   return ctx->dst->create<MemberAccessorExpression>(src, str, mem);
 }
 
-void MemberAccessorExpression::to_str(const sem::Info& sem,
-                                      std::ostream& out,
-                                      size_t indent) const {
-  make_indent(out, indent);
-  out << "MemberAccessor[" << result_type_str(sem) << "]{" << std::endl;
-  struct_->to_str(sem, out, indent + 2);
-  member_->to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/member_accessor_expression.h b/src/ast/member_accessor_expression.h
index f3a99ce..46ddd5d 100644
--- a/src/ast/member_accessor_expression.h
+++ b/src/ast/member_accessor_expression.h
@@ -48,14 +48,6 @@
   /// @return the newly cloned node
   MemberAccessorExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   MemberAccessorExpression(const MemberAccessorExpression&) = delete;
 
diff --git a/src/ast/member_accessor_expression_test.cc b/src/ast/member_accessor_expression_test.cc
index 023ad0f..66179ce 100644
--- a/src/ast/member_accessor_expression_test.cc
+++ b/src/ast/member_accessor_expression_test.cc
@@ -84,16 +84,6 @@
       "internal compiler error");
 }
 
-TEST_F(MemberAccessorExpressionTest, ToStr) {
-  auto* stmt =
-      create<MemberAccessorExpression>(Expr("structure"), Expr("member"));
-  EXPECT_EQ(str(stmt), R"(MemberAccessor[not set]{
-  Identifier[not set]{structure}
-  Identifier[not set]{member}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/module.cc b/src/ast/module.cc
index 894d435..029af15 100644
--- a/src/ast/module.cc
+++ b/src/ast/module.cc
@@ -118,35 +118,5 @@
   }
 }
 
-void Module::to_str(const sem::Info& sem,
-                    std::ostream& out,
-                    size_t indent) const {
-  make_indent(out, indent);
-  out << "Module{" << std::endl;
-  indent += 2;
-  for (auto* ty : type_decls_) {
-    make_indent(out, indent);
-    if (auto* alias = ty->As<ast::Alias>()) {
-      out << alias->symbol().to_str() << " -> " << alias->type()->type_name()
-          << std::endl;
-    } else if (auto* str = ty->As<ast::Struct>()) {
-      str->to_str(sem, out, indent);
-    }
-  }
-  for (auto* var : global_variables_) {
-    var->to_str(sem, out, indent);
-  }
-  for (auto* func : functions_) {
-    func->to_str(sem, out, indent);
-  }
-  out << "}" << std::endl;
-}
-
-std::string Module::to_str(const sem::Info& sem) const {
-  std::ostringstream out;
-  to_str(sem, out, 0);
-  return out.str();
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/module.h b/src/ast/module.h
index 8ce8e7e..ab52095 100644
--- a/src/ast/module.h
+++ b/src/ast/module.h
@@ -102,18 +102,6 @@
   /// @param src the module to copy into this module
   void Copy(CloneContext* ctx, const Module* src);
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
-  /// @param sem the semantic info for the program
-  /// @returns a string representation of the Builder
-  std::string to_str(const sem::Info& sem) const;
-
  private:
   std::vector<ast::Node*> global_declarations_;
   std::vector<ast::TypeDecl*> type_decls_;
diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc
index 934996c..f54a996 100644
--- a/src/ast/module_clone_test.cc
+++ b/src/ast/module_clone_test.cc
@@ -128,8 +128,8 @@
 
   ASSERT_TRUE(dst.IsValid()) << diag::Formatter().format(dst.Diagnostics());
 
-  // Expect the AST printed with to_str() to match
-  EXPECT_EQ(src.to_str(), dst.to_str());
+  // Expect the printed strings to match
+  EXPECT_EQ(Program::printer(&src), Program::printer(&dst));
 
   // Check that none of the AST nodes or type pointers in dst are found in src
   std::unordered_set<ast::Node*> src_nodes;
@@ -141,10 +141,10 @@
     src_types.emplace(src_type);
   }
   for (auto* dst_node : dst.ASTNodes().Objects()) {
-    ASSERT_EQ(src_nodes.count(dst_node), 0u) << dst.str(dst_node);
+    ASSERT_EQ(src_nodes.count(dst_node), 0u);
   }
   for (auto* dst_type : dst.Types()) {
-    ASSERT_EQ(src_types.count(dst_type), 0u) << dst_type->type_name();
+    ASSERT_EQ(src_types.count(dst_type), 0u);
   }
 
   // Regenerate the wgsl for the src program. We use this instead of the
diff --git a/src/ast/module_test.cc b/src/ast/module_test.cc
index 510844a..861c4d0 100644
--- a/src/ast/module_test.cc
+++ b/src/ast/module_test.cc
@@ -26,12 +26,6 @@
   EXPECT_EQ(Program(std::move(*this)).AST().Functions().size(), 0u);
 }
 
-TEST_F(ModuleTest, ToStrEmitsPreambleAndPostamble) {
-  const auto str = Program(std::move(*this)).to_str();
-  auto* const expected = "Module{\n}\n";
-  EXPECT_EQ(str, expected);
-}
-
 TEST_F(ModuleTest, LookupFunction) {
   auto* func = Func("main", VariableList{}, ty.f32(), StatementList{},
                     ast::DecorationList{});
diff --git a/src/ast/multisampled_texture.cc b/src/ast/multisampled_texture.cc
index 51048d3..a5328f9 100644
--- a/src/ast/multisampled_texture.cc
+++ b/src/ast/multisampled_texture.cc
@@ -33,12 +33,6 @@
 
 MultisampledTexture::~MultisampledTexture() = default;
 
-std::string MultisampledTexture::type_name() const {
-  std::ostringstream out;
-  out << "__multisampled_texture_" << dim() << type_->type_name();
-  return out.str();
-}
-
 std::string MultisampledTexture::FriendlyName(
     const SymbolTable& symbols) const {
   std::ostringstream out;
diff --git a/src/ast/multisampled_texture.h b/src/ast/multisampled_texture.h
index 9fcc2a8..0c44a91 100644
--- a/src/ast/multisampled_texture.h
+++ b/src/ast/multisampled_texture.h
@@ -41,9 +41,6 @@
   /// @returns the subtype of the sampled texture
   Type* type() const { return type_; }
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/multisampled_texture_test.cc b/src/ast/multisampled_texture_test.cc
index 5671f1e..4067749 100644
--- a/src/ast/multisampled_texture_test.cc
+++ b/src/ast/multisampled_texture_test.cc
@@ -59,12 +59,6 @@
   EXPECT_EQ(s->type(), f32);
 }
 
-TEST_F(AstMultisampledTextureTest, TypeName) {
-  auto* f32 = create<F32>();
-  auto* s = create<MultisampledTexture>(TextureDimension::k3d, f32);
-  EXPECT_EQ(s->type_name(), "__multisampled_texture_3d__f32");
-}
-
 TEST_F(AstMultisampledTextureTest, FriendlyName) {
   auto* f32 = create<F32>();
   auto* s = create<MultisampledTexture>(TextureDimension::k3d, f32);
diff --git a/src/ast/node.cc b/src/ast/node.cc
index 1827bd6..c4c7591 100644
--- a/src/ast/node.cc
+++ b/src/ast/node.cc
@@ -31,11 +31,5 @@
     out << " ";
 }
 
-std::string Node::str(const sem::Info& sem) const {
-  std::ostringstream out;
-  to_str(sem, out, 0);
-  return out.str();
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/node.h b/src/ast/node.h
index 24320bb..cfc1111 100644
--- a/src/ast/node.h
+++ b/src/ast/node.h
@@ -44,19 +44,6 @@
   /// @returns the node source data
   const Source& source() const { return source_; }
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  virtual void to_str(const sem::Info& sem,
-                      std::ostream& out,
-                      size_t indent) const = 0;
-
-  /// Convenience wrapper around the to_str() method.
-  /// @param sem the semantic info for the program
-  /// @returns the node as a string
-  std::string str(const sem::Info& sem) const;
-
  protected:
   /// Create a new node
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/ast/override_decoration.cc b/src/ast/override_decoration.cc
index 942364b..312c7fd 100644
--- a/src/ast/override_decoration.cc
+++ b/src/ast/override_decoration.cc
@@ -38,17 +38,6 @@
   return "override";
 }
 
-void OverrideDecoration::to_str(const sem::Info&,
-                                std::ostream& out,
-                                size_t indent) const {
-  make_indent(out, indent);
-  out << "OverrideDecoration";
-  if (has_value_) {
-    out << "{" << value_ << "}";
-  }
-  out << std::endl;
-}
-
 OverrideDecoration* OverrideDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/override_decoration.h b/src/ast/override_decoration.h
index 14ba825..09e251f 100644
--- a/src/ast/override_decoration.h
+++ b/src/ast/override_decoration.h
@@ -45,14 +45,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/override_decoration_test.cc b/src/ast/override_decoration_test.cc
index fe1d151..fc655b2 100644
--- a/src/ast/override_decoration_test.cc
+++ b/src/ast/override_decoration_test.cc
@@ -33,12 +33,6 @@
   EXPECT_FALSE(d->HasValue());
 }
 
-TEST_F(OverrideDecorationTest, ToStr) {
-  auto* d = create<OverrideDecoration>(1200);
-  EXPECT_EQ(str(d), R"(OverrideDecoration{1200}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/pointer.cc b/src/ast/pointer.cc
index 77d8958..2c97ae4 100644
--- a/src/ast/pointer.cc
+++ b/src/ast/pointer.cc
@@ -31,15 +31,6 @@
       storage_class_(storage_class),
       access_(access) {}
 
-std::string Pointer::type_name() const {
-  std::ostringstream out;
-  out << "__ptr_" << storage_class_ << subtype_->type_name();
-  if (access_ != ast::Access::kUndefined) {
-    out << "_" << access_;
-  }
-  return out.str();
-}
-
 std::string Pointer::FriendlyName(const SymbolTable& symbols) const {
   std::ostringstream out;
   out << "ptr<";
diff --git a/src/ast/pointer.h b/src/ast/pointer.h
index c7bba99..2469d65 100644
--- a/src/ast/pointer.h
+++ b/src/ast/pointer.h
@@ -51,9 +51,6 @@
   /// @returns the access control of the pointer
   ast::Access access() const { return access_; }
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/pointer_test.cc b/src/ast/pointer_test.cc
index 454b4d7..9e18782 100644
--- a/src/ast/pointer_test.cc
+++ b/src/ast/pointer_test.cc
@@ -31,19 +31,6 @@
   EXPECT_EQ(p->access(), Access::kRead);
 }
 
-TEST_F(AstPointerTest, TypeName) {
-  auto* i32 = create<I32>();
-  auto* p =
-      create<Pointer>(i32, ast::StorageClass::kWorkgroup, Access::kUndefined);
-  EXPECT_EQ(p->type_name(), "__ptr_workgroup__i32");
-}
-
-TEST_F(AstPointerTest, TypeNameWithAccess) {
-  auto* i32 = create<I32>();
-  auto* p = create<Pointer>(i32, ast::StorageClass::kWorkgroup, Access::kRead);
-  EXPECT_EQ(p->type_name(), "__ptr_workgroup__i32_read");
-}
-
 TEST_F(AstPointerTest, FriendlyName) {
   auto* i32 = create<I32>();
   auto* p =
diff --git a/src/ast/return_statement.cc b/src/ast/return_statement.cc
index a8e1bfe..5888470 100644
--- a/src/ast/return_statement.cc
+++ b/src/ast/return_statement.cc
@@ -42,27 +42,5 @@
   return ctx->dst->create<ReturnStatement>(src, ret);
 }
 
-void ReturnStatement::to_str(const sem::Info& sem,
-                             std::ostream& out,
-                             size_t indent) const {
-  make_indent(out, indent);
-  out << "Return{";
-
-  if (value_) {
-    out << std::endl;
-
-    make_indent(out, indent + 2);
-    out << "{" << std::endl;
-
-    value_->to_str(sem, out, indent + 4);
-
-    make_indent(out, indent + 2);
-    out << "}" << std::endl;
-
-    make_indent(out, indent);
-  }
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/return_statement.h b/src/ast/return_statement.h
index 40306dc..c8420f8 100644
--- a/src/ast/return_statement.h
+++ b/src/ast/return_statement.h
@@ -51,14 +51,6 @@
   /// @return the newly cloned node
   ReturnStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   ReturnStatement(const ReturnStatement&) = delete;
 
diff --git a/src/ast/return_statement_test.cc b/src/ast/return_statement_test.cc
index 89bfd21..dbc5c11 100644
--- a/src/ast/return_statement_test.cc
+++ b/src/ast/return_statement_test.cc
@@ -53,23 +53,6 @@
   EXPECT_TRUE(r->has_value());
 }
 
-TEST_F(ReturnStatementTest, ToStr_WithValue) {
-  auto* expr = Expr("expr");
-  auto* r = create<ReturnStatement>(expr);
-  EXPECT_EQ(str(r), R"(Return{
-  {
-    Identifier[not set]{expr}
-  }
-}
-)");
-}
-
-TEST_F(ReturnStatementTest, ToStr_WithoutValue) {
-  auto* r = create<ReturnStatement>();
-  EXPECT_EQ(str(r), R"(Return{}
-)");
-}
-
 TEST_F(ReturnStatementTest, Assert_DifferentProgramID_Expr) {
   EXPECT_FATAL_FAILURE(
       {
diff --git a/src/ast/sampled_texture.cc b/src/ast/sampled_texture.cc
index 69cad40..cc1109f 100644
--- a/src/ast/sampled_texture.cc
+++ b/src/ast/sampled_texture.cc
@@ -33,12 +33,6 @@
 
 SampledTexture::~SampledTexture() = default;
 
-std::string SampledTexture::type_name() const {
-  std::ostringstream out;
-  out << "__sampled_texture_" << dim() << type_->type_name();
-  return out.str();
-}
-
 std::string SampledTexture::FriendlyName(const SymbolTable& symbols) const {
   std::ostringstream out;
   out << "texture_" << dim() << "<" << type_->FriendlyName(symbols) << ">";
diff --git a/src/ast/sampled_texture.h b/src/ast/sampled_texture.h
index 3520d17..9bfc52a 100644
--- a/src/ast/sampled_texture.h
+++ b/src/ast/sampled_texture.h
@@ -41,9 +41,6 @@
   /// @returns the subtype of the sampled texture
   Type* type() const { return const_cast<Type*>(type_); }
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/sampled_texture_test.cc b/src/ast/sampled_texture_test.cc
index 35e932e..12f1b8f 100644
--- a/src/ast/sampled_texture_test.cc
+++ b/src/ast/sampled_texture_test.cc
@@ -43,12 +43,6 @@
   EXPECT_EQ(s->type(), f32);
 }
 
-TEST_F(AstSampledTextureTest, TypeName) {
-  auto* f32 = create<F32>();
-  auto* s = create<SampledTexture>(TextureDimension::k3d, f32);
-  EXPECT_EQ(s->type_name(), "__sampled_texture_3d__f32");
-}
-
 TEST_F(AstSampledTextureTest, FriendlyName) {
   auto* f32 = create<F32>();
   auto* s = create<SampledTexture>(TextureDimension::k3d, f32);
diff --git a/src/ast/sampler.cc b/src/ast/sampler.cc
index bcf95c8..5a6ca23 100644
--- a/src/ast/sampler.cc
+++ b/src/ast/sampler.cc
@@ -40,11 +40,6 @@
 
 Sampler::~Sampler() = default;
 
-std::string Sampler::type_name() const {
-  return std::string("__sampler_") +
-         (kind_ == SamplerKind::kSampler ? "sampler" : "comparison");
-}
-
 std::string Sampler::FriendlyName(const SymbolTable&) const {
   return kind_ == SamplerKind::kSampler ? "sampler" : "sampler_comparison";
 }
diff --git a/src/ast/sampler.h b/src/ast/sampler.h
index 3800de9..797a4f2 100644
--- a/src/ast/sampler.h
+++ b/src/ast/sampler.h
@@ -53,9 +53,6 @@
   /// @returns true if this is a comparison sampler
   bool IsComparison() const { return kind_ == SamplerKind::kComparisonSampler; }
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/sampler_test.cc b/src/ast/sampler_test.cc
index 186eb48..2f9c202 100644
--- a/src/ast/sampler_test.cc
+++ b/src/ast/sampler_test.cc
@@ -33,16 +33,6 @@
   EXPECT_TRUE(s->IsComparison());
 }
 
-TEST_F(AstSamplerTest, TypeName_Sampler) {
-  auto* s = create<Sampler>(SamplerKind::kSampler);
-  EXPECT_EQ(s->type_name(), "__sampler_sampler");
-}
-
-TEST_F(AstSamplerTest, TypeName_Comparison) {
-  auto* s = create<Sampler>(SamplerKind::kComparisonSampler);
-  EXPECT_EQ(s->type_name(), "__sampler_comparison");
-}
-
 TEST_F(AstSamplerTest, FriendlyNameSampler) {
   auto* s = create<Sampler>(SamplerKind::kSampler);
   EXPECT_EQ(s->FriendlyName(Symbols()), "sampler");
diff --git a/src/ast/scalar_constructor_expression.cc b/src/ast/scalar_constructor_expression.cc
index d74e681..83b9da3 100644
--- a/src/ast/scalar_constructor_expression.cc
+++ b/src/ast/scalar_constructor_expression.cc
@@ -42,13 +42,5 @@
   return ctx->dst->create<ScalarConstructorExpression>(src, lit);
 }
 
-void ScalarConstructorExpression::to_str(const sem::Info& sem,
-                                         std::ostream& out,
-                                         size_t indent) const {
-  make_indent(out, indent);
-  out << "ScalarConstructor[" << result_type_str(sem) << "]{"
-      << literal_->to_str(sem) << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/scalar_constructor_expression.h b/src/ast/scalar_constructor_expression.h
index 0bdfa0b..0a7925d 100644
--- a/src/ast/scalar_constructor_expression.h
+++ b/src/ast/scalar_constructor_expression.h
@@ -45,14 +45,6 @@
   /// @return the newly cloned node
   ScalarConstructorExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   ScalarConstructorExpression(const ScalarConstructorExpression&) = delete;
 
diff --git a/src/ast/scalar_constructor_expression_test.cc b/src/ast/scalar_constructor_expression_test.cc
index 2795545..8548d0d 100644
--- a/src/ast/scalar_constructor_expression_test.cc
+++ b/src/ast/scalar_constructor_expression_test.cc
@@ -34,12 +34,6 @@
   EXPECT_EQ(src.range.begin.column, 2u);
 }
 
-TEST_F(ScalarConstructorExpressionTest, ToStr) {
-  auto* c = Expr(true);
-  EXPECT_EQ(str(c), R"(ScalarConstructor[not set]{true}
-)");
-}
-
 TEST_F(ScalarConstructorExpressionTest, Assert_DifferentProgramID_Literal) {
   EXPECT_FATAL_FAILURE(
       {
diff --git a/src/ast/sint_literal.cc b/src/ast/sint_literal.cc
index 8e33db8..710ecab 100644
--- a/src/ast/sint_literal.cc
+++ b/src/ast/sint_literal.cc
@@ -28,10 +28,6 @@
 
 SintLiteral::~SintLiteral() = default;
 
-std::string SintLiteral::to_str(const sem::Info&) const {
-  return std::to_string(value());
-}
-
 std::string SintLiteral::name() const {
   return "__sint_" + std::to_string(value());
 }
diff --git a/src/ast/sint_literal.h b/src/ast/sint_literal.h
index a8e3325..b0a2baa 100644
--- a/src/ast/sint_literal.h
+++ b/src/ast/sint_literal.h
@@ -38,10 +38,6 @@
   /// @returns the name for this literal. This name is unique to this value.
   std::string name() const override;
 
-  /// @param sem the semantic info for the program
-  /// @returns the literal as a string
-  std::string to_str(const sem::Info& sem) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/sint_literal_test.cc b/src/ast/sint_literal_test.cc
index 8843105..d9805f3 100644
--- a/src/ast/sint_literal_test.cc
+++ b/src/ast/sint_literal_test.cc
@@ -26,11 +26,6 @@
   EXPECT_EQ(i->value(), 47);
 }
 
-TEST_F(SintLiteralTest, ToStr) {
-  auto* i = create<SintLiteral>(-42);
-  EXPECT_EQ(str(i), "-42");
-}
-
 TEST_F(SintLiteralTest, Name_I32) {
   auto* i = create<SintLiteral>(2);
   EXPECT_EQ("__sint_2", i->name());
diff --git a/src/ast/stage_decoration.cc b/src/ast/stage_decoration.cc
index 1843071..af12ba2 100644
--- a/src/ast/stage_decoration.cc
+++ b/src/ast/stage_decoration.cc
@@ -34,13 +34,6 @@
   return "stage";
 }
 
-void StageDecoration::to_str(const sem::Info&,
-                             std::ostream& out,
-                             size_t indent) const {
-  make_indent(out, indent);
-  out << "StageDecoration{" << stage_ << "}" << std::endl;
-}
-
 StageDecoration* StageDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/stage_decoration.h b/src/ast/stage_decoration.h
index 83ba14c..d411d7a 100644
--- a/src/ast/stage_decoration.h
+++ b/src/ast/stage_decoration.h
@@ -41,14 +41,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/stage_decoration_test.cc b/src/ast/stage_decoration_test.cc
index 11c8077..e6fc5be 100644
--- a/src/ast/stage_decoration_test.cc
+++ b/src/ast/stage_decoration_test.cc
@@ -28,12 +28,6 @@
   EXPECT_EQ(d->value(), PipelineStage::kFragment);
 }
 
-TEST_F(StageDecorationTest, ToStr) {
-  auto* d = create<StageDecoration>(PipelineStage::kFragment);
-  EXPECT_EQ(str(d), R"(StageDecoration{fragment}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/storage_texture.cc b/src/ast/storage_texture.cc
index 38207de..39f5a1e 100644
--- a/src/ast/storage_texture.cc
+++ b/src/ast/storage_texture.cc
@@ -155,13 +155,6 @@
 
 StorageTexture::~StorageTexture() = default;
 
-std::string StorageTexture::type_name() const {
-  std::ostringstream out;
-  out << "__storage_texture_" << dim() << "_" << image_format_ << "_"
-      << access_;
-  return out.str();
-}
-
 std::string StorageTexture::FriendlyName(const SymbolTable&) const {
   std::ostringstream out;
   out << "texture_storage_" << dim() << "<" << image_format_ << ", " << access_
diff --git a/src/ast/storage_texture.h b/src/ast/storage_texture.h
index 9d45b95..c89a230 100644
--- a/src/ast/storage_texture.h
+++ b/src/ast/storage_texture.h
@@ -107,9 +107,6 @@
   /// @returns true if the access control is read/write
   bool is_read_write() const { return access_ == Access::kReadWrite; }
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/storage_texture_test.cc b/src/ast/storage_texture_test.cc
index 6098ccb..7642282 100644
--- a/src/ast/storage_texture_test.cc
+++ b/src/ast/storage_texture_test.cc
@@ -48,14 +48,6 @@
   EXPECT_EQ(s->image_format(), ImageFormat::kRgba32Float);
 }
 
-TEST_F(AstStorageTextureTest, TypeName) {
-  auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
-  auto* s =
-      create<StorageTexture>(TextureDimension::k2dArray,
-                             ImageFormat::kRgba32Float, subtype, Access::kRead);
-  EXPECT_EQ(s->type_name(), "__storage_texture_2d_array_rgba32float_read");
-}
-
 TEST_F(AstStorageTextureTest, FriendlyName) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
   auto* s =
diff --git a/src/ast/stride_decoration.cc b/src/ast/stride_decoration.cc
index 023984a..1a4c47b 100644
--- a/src/ast/stride_decoration.cc
+++ b/src/ast/stride_decoration.cc
@@ -34,13 +34,6 @@
   return "stride";
 }
 
-void StrideDecoration::to_str(const sem::Info&,
-                              std::ostream& out,
-                              size_t indent) const {
-  make_indent(out, indent);
-  out << "stride " << stride_;
-}
-
 StrideDecoration* StrideDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/stride_decoration.h b/src/ast/stride_decoration.h
index e8102d9..6d0a92c 100644
--- a/src/ast/stride_decoration.h
+++ b/src/ast/stride_decoration.h
@@ -38,14 +38,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/struct.cc b/src/ast/struct.cc
index 9138ee4..b49dd28 100644
--- a/src/ast/struct.cc
+++ b/src/ast/struct.cc
@@ -68,26 +68,5 @@
   return ctx->dst->create<Struct>(src, n, mem, decos);
 }
 
-void Struct::to_str(const sem::Info& sem,
-                    std::ostream& out,
-                    size_t indent) const {
-  out << "Struct " << name().to_str() << " {" << std::endl;
-  for (auto* deco : decorations_) {
-    make_indent(out, indent + 2);
-    out << "[[";
-    deco->to_str(sem, out, 0);
-    out << "]]" << std::endl;
-  }
-  for (auto* member : members_) {
-    member->to_str(sem, out, indent + 2);
-  }
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
-std::string Struct::type_name() const {
-  return "__struct_" + name().to_str();
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/struct.h b/src/ast/struct.h
index 48a7e7e..dcc5b59 100644
--- a/src/ast/struct.h
+++ b/src/ast/struct.h
@@ -64,17 +64,6 @@
   /// @return the newly cloned node
   Struct* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
-  /// @returns the name for the type
-  std::string type_name() const override;
-
  private:
   Struct(const Struct&) = delete;
 
diff --git a/src/ast/struct_block_decoration.cc b/src/ast/struct_block_decoration.cc
index 75b9b9f..a83d828 100644
--- a/src/ast/struct_block_decoration.cc
+++ b/src/ast/struct_block_decoration.cc
@@ -33,13 +33,6 @@
   return "block";
 }
 
-void StructBlockDecoration::to_str(const sem::Info&,
-                                   std::ostream& out,
-                                   size_t indent) const {
-  make_indent(out, indent);
-  out << "block";
-}
-
 StructBlockDecoration* StructBlockDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/struct_block_decoration.h b/src/ast/struct_block_decoration.h
index 9f77e37..8a304a7 100644
--- a/src/ast/struct_block_decoration.h
+++ b/src/ast/struct_block_decoration.h
@@ -36,14 +36,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/struct_member.cc b/src/ast/struct_member.cc
index 743cae0..995aedc 100644
--- a/src/ast/struct_member.cc
+++ b/src/ast/struct_member.cc
@@ -64,20 +64,5 @@
   return ctx->dst->create<StructMember>(src, sym, ty, decos);
 }
 
-void StructMember::to_str(const sem::Info& sem,
-                          std::ostream& out,
-                          size_t indent) const {
-  make_indent(out, indent);
-  out << "StructMember{";
-  if (decorations_.size() > 0) {
-    out << "[[ ";
-    for (auto* deco : decorations_)
-      out << deco->str(sem) << " ";
-    out << "]] ";
-  }
-
-  out << symbol_.to_str() << ": " << type_->type_name() << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/struct_member.h b/src/ast/struct_member.h
index 8fae5ff..6635153 100644
--- a/src/ast/struct_member.h
+++ b/src/ast/struct_member.h
@@ -65,14 +65,6 @@
   /// @return the newly cloned node
   StructMember* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   StructMember(const StructMember&) = delete;
 
diff --git a/src/ast/struct_member_align_decoration.cc b/src/ast/struct_member_align_decoration.cc
index d27ae3c..770e888 100644
--- a/src/ast/struct_member_align_decoration.cc
+++ b/src/ast/struct_member_align_decoration.cc
@@ -35,13 +35,6 @@
   return "align";
 }
 
-void StructMemberAlignDecoration::to_str(const sem::Info&,
-                                         std::ostream& out,
-                                         size_t indent) const {
-  make_indent(out, indent);
-  out << "align " << std::to_string(align_);
-}
-
 StructMemberAlignDecoration* StructMemberAlignDecoration::Clone(
     CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
diff --git a/src/ast/struct_member_align_decoration.h b/src/ast/struct_member_align_decoration.h
index b782e57..d25dfae 100644
--- a/src/ast/struct_member_align_decoration.h
+++ b/src/ast/struct_member_align_decoration.h
@@ -42,14 +42,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/struct_member_offset_decoration.cc b/src/ast/struct_member_offset_decoration.cc
index 4e03dda..7939b19 100644
--- a/src/ast/struct_member_offset_decoration.cc
+++ b/src/ast/struct_member_offset_decoration.cc
@@ -34,13 +34,6 @@
   return "offset";
 }
 
-void StructMemberOffsetDecoration::to_str(const sem::Info&,
-                                          std::ostream& out,
-                                          size_t indent) const {
-  make_indent(out, indent);
-  out << "offset " << std::to_string(offset_);
-}
-
 StructMemberOffsetDecoration* StructMemberOffsetDecoration::Clone(
     CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
diff --git a/src/ast/struct_member_offset_decoration.h b/src/ast/struct_member_offset_decoration.h
index 6bb4c09..8cd9db1 100644
--- a/src/ast/struct_member_offset_decoration.h
+++ b/src/ast/struct_member_offset_decoration.h
@@ -50,14 +50,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/struct_member_size_decoration.cc b/src/ast/struct_member_size_decoration.cc
index d8d283d..1b679c6 100644
--- a/src/ast/struct_member_size_decoration.cc
+++ b/src/ast/struct_member_size_decoration.cc
@@ -35,13 +35,6 @@
   return "size";
 }
 
-void StructMemberSizeDecoration::to_str(const sem::Info&,
-                                        std::ostream& out,
-                                        size_t indent) const {
-  make_indent(out, indent);
-  out << "size " << std::to_string(size_);
-}
-
 StructMemberSizeDecoration* StructMemberSizeDecoration::Clone(
     CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
diff --git a/src/ast/struct_member_size_decoration.h b/src/ast/struct_member_size_decoration.h
index 0c8e120..25cfb2a 100644
--- a/src/ast/struct_member_size_decoration.h
+++ b/src/ast/struct_member_size_decoration.h
@@ -42,14 +42,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/struct_member_test.cc b/src/ast/struct_member_test.cc
index c0d3041..6bf3edd 100644
--- a/src/ast/struct_member_test.cc
+++ b/src/ast/struct_member_test.cc
@@ -93,16 +93,6 @@
       "internal compiler error");
 }
 
-TEST_F(StructMemberTest, ToStr) {
-  auto* st = Member("a", ty.i32(), {MemberSize(4)});
-  EXPECT_EQ(str(st), "StructMember{[[ size 4 ]] a: __i32}\n");
-}
-
-TEST_F(StructMemberTest, ToStrNoDecorations) {
-  auto* st = Member("a", ty.i32());
-  EXPECT_EQ(str(st), "StructMember{a: __i32}\n");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/struct_test.cc b/src/ast/struct_test.cc
index 1404ce8..7fe50e4 100644
--- a/src/ast/struct_test.cc
+++ b/src/ast/struct_test.cc
@@ -126,24 +126,6 @@
       "internal compiler error");
 }
 
-TEST_F(AstStructTest, ToStr) {
-  auto* s = create<Struct>(Sym("S"), StructMemberList{Member("a", ty.i32())},
-                           DecorationList{create<StructBlockDecoration>()});
-
-  EXPECT_EQ(str(s), R"(Struct S {
-  [[block]]
-  StructMember{a: __i32}
-}
-)");
-}
-
-TEST_F(AstStructTest, TypeName) {
-  auto name = Sym("my_struct");
-  auto* s =
-      create<ast::Struct>(name, ast::StructMemberList{}, ast::DecorationList{});
-  EXPECT_EQ(s->type_name(), "__struct_$1");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/switch_statement.cc b/src/ast/switch_statement.cc
index 2dab086..7091b3e 100644
--- a/src/ast/switch_statement.cc
+++ b/src/ast/switch_statement.cc
@@ -46,26 +46,5 @@
   return ctx->dst->create<SwitchStatement>(src, cond, b);
 }
 
-void SwitchStatement::to_str(const sem::Info& sem,
-                             std::ostream& out,
-                             size_t indent) const {
-  make_indent(out, indent);
-  out << "Switch{" << std::endl;
-  condition_->to_str(sem, out, indent + 2);
-
-  make_indent(out, indent + 2);
-  out << "{" << std::endl;
-
-  for (auto* stmt : body_) {
-    stmt->to_str(sem, out, indent + 4);
-  }
-
-  make_indent(out, indent + 2);
-  out << "}" << std::endl;
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/switch_statement.h b/src/ast/switch_statement.h
index a0ee1e3..fae33f5 100644
--- a/src/ast/switch_statement.h
+++ b/src/ast/switch_statement.h
@@ -51,14 +51,6 @@
   /// @return the newly cloned node
   SwitchStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   SwitchStatement(const SwitchStatement&) = delete;
 
diff --git a/src/ast/switch_statement_test.cc b/src/ast/switch_statement_test.cc
index 659a2a9..66c29e4 100644
--- a/src/ast/switch_statement_test.cc
+++ b/src/ast/switch_statement_test.cc
@@ -113,37 +113,6 @@
       "internal compiler error");
 }
 
-TEST_F(SwitchStatementTest, ToStr_Empty) {
-  auto* ident = Expr("ident");
-
-  auto* stmt = create<SwitchStatement>(ident, CaseStatementList{});
-  EXPECT_EQ(str(stmt), R"(Switch{
-  Identifier[not set]{ident}
-  {
-  }
-}
-)");
-}
-
-TEST_F(SwitchStatementTest, ToStr) {
-  CaseSelectorList lit;
-  lit.push_back(create<SintLiteral>(2));
-
-  auto* ident = Expr("ident");
-  CaseStatementList body;
-  body.push_back(create<CaseStatement>(lit, Block()));
-
-  auto* stmt = create<SwitchStatement>(ident, body);
-  EXPECT_EQ(str(stmt), R"(Switch{
-  Identifier[not set]{ident}
-  {
-    Case 2{
-    }
-  }
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/type.h b/src/ast/type.h
index 73f44dc..26c9daf 100644
--- a/src/ast/type.h
+++ b/src/ast/type.h
@@ -35,9 +35,6 @@
   Type(Type&&);
   ~Type() override;
 
-  /// @returns the name for this type. The type name is unique over all types.
-  virtual std::string type_name() const = 0;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
@@ -80,14 +77,6 @@
   /// @returns true if this type is a handle type
   bool is_handle() const;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  protected:
   /// Constructor
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/ast/type_constructor_expression.cc b/src/ast/type_constructor_expression.cc
index 84e7be4..e382b12 100644
--- a/src/ast/type_constructor_expression.cc
+++ b/src/ast/type_constructor_expression.cc
@@ -47,20 +47,5 @@
   return ctx->dst->create<TypeConstructorExpression>(src, ty, vals);
 }
 
-void TypeConstructorExpression::to_str(const sem::Info& sem,
-                                       std::ostream& out,
-                                       size_t indent) const {
-  make_indent(out, indent);
-  out << "TypeConstructor[" << result_type_str(sem) << "]{" << std::endl;
-  make_indent(out, indent + 2);
-  out << type_->type_name() << std::endl;
-
-  for (auto* val : values_) {
-    val->to_str(sem, out, indent + 2);
-  }
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/type_constructor_expression.h b/src/ast/type_constructor_expression.h
index 8bd15a5..b23aeeb 100644
--- a/src/ast/type_constructor_expression.h
+++ b/src/ast/type_constructor_expression.h
@@ -54,14 +54,6 @@
   /// @return the newly cloned node
   TypeConstructorExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   TypeConstructorExpression(const TypeConstructorExpression&) = delete;
 
diff --git a/src/ast/type_constructor_expression_test.cc b/src/ast/type_constructor_expression_test.cc
index b526fcf..7ef5eb8 100644
--- a/src/ast/type_constructor_expression_test.cc
+++ b/src/ast/type_constructor_expression_test.cc
@@ -80,22 +80,6 @@
       "internal compiler error");
 }
 
-TEST_F(TypeConstructorExpressionTest, ToStr) {
-  ExpressionList expr;
-  expr.push_back(Expr("expr_1"));
-  expr.push_back(Expr("expr_2"));
-  expr.push_back(Expr("expr_3"));
-
-  auto* t = create<TypeConstructorExpression>(ty.vec3<f32>(), expr);
-  EXPECT_EQ(str(t), R"(TypeConstructor[not set]{
-  __vec_3__f32
-  Identifier[not set]{expr_1}
-  Identifier[not set]{expr_2}
-  Identifier[not set]{expr_3}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/type_decl.cc b/src/ast/type_decl.cc
index 566fceb..41491ee 100644
--- a/src/ast/type_decl.cc
+++ b/src/ast/type_decl.cc
@@ -30,12 +30,5 @@
 
 TypeDecl::~TypeDecl() = default;
 
-void TypeDecl::to_str(const sem::Info&,
-                      std::ostream& out,
-                      size_t indent) const {
-  make_indent(out, indent);
-  out << type_name();
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/type_decl.h b/src/ast/type_decl.h
index c942b0c..0f5c7a8 100644
--- a/src/ast/type_decl.h
+++ b/src/ast/type_decl.h
@@ -38,17 +38,6 @@
   /// @returns the name of the type declaration
   Symbol name() const { return name_; }
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
-  /// @returns the name for this type. The type name is unique over all types.
-  virtual std::string type_name() const = 0;
-
  private:
   TypeDecl(const TypeDecl&) = delete;
   TypeDecl& operator=(const TypeDecl&) = delete;
diff --git a/src/ast/type_name.cc b/src/ast/type_name.cc
index 7049505..943e66f 100644
--- a/src/ast/type_name.cc
+++ b/src/ast/type_name.cc
@@ -22,18 +22,12 @@
 namespace ast {
 
 TypeName::TypeName(ProgramID program_id, const Source& source, Symbol name)
-    : Base(program_id, source),
-      name_(name),
-      type_name_("__type_name_" + name.to_str()) {}
+    : Base(program_id, source), name_(name) {}
 
 TypeName::~TypeName() = default;
 
 TypeName::TypeName(TypeName&&) = default;
 
-std::string TypeName::type_name() const {
-  return type_name_;
-}
-
 std::string TypeName::FriendlyName(const SymbolTable& symbols) const {
   return symbols.NameFor(name_);
 }
diff --git a/src/ast/type_name.h b/src/ast/type_name.h
index e3759c3..fb4cc8c 100644
--- a/src/ast/type_name.h
+++ b/src/ast/type_name.h
@@ -38,9 +38,6 @@
   /// @return the type name
   const Symbol& name() const { return name_; }
 
-  /// @returns the name for th type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
@@ -53,7 +50,6 @@
 
  private:
   Symbol name_;
-  std::string const type_name_;
 };
 
 }  // namespace ast
diff --git a/src/ast/u32.cc b/src/ast/u32.cc
index 8a533c1..21bcc16 100644
--- a/src/ast/u32.cc
+++ b/src/ast/u32.cc
@@ -28,10 +28,6 @@
 
 U32::U32(U32&&) = default;
 
-std::string U32::type_name() const {
-  return "__u32";
-}
-
 std::string U32::FriendlyName(const SymbolTable&) const {
   return "u32";
 }
diff --git a/src/ast/u32.h b/src/ast/u32.h
index 22d3959..2acf667 100644
--- a/src/ast/u32.h
+++ b/src/ast/u32.h
@@ -33,9 +33,6 @@
   U32(U32&&);
   ~U32() override;
 
-  /// @returns the name for th type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/u32_test.cc b/src/ast/u32_test.cc
index e13ed85..04c17de 100644
--- a/src/ast/u32_test.cc
+++ b/src/ast/u32_test.cc
@@ -22,11 +22,6 @@
 
 using AstU32Test = TestHelper;
 
-TEST_F(AstU32Test, TypeName) {
-  auto* u = create<U32>();
-  EXPECT_EQ(u->type_name(), "__u32");
-}
-
 TEST_F(AstU32Test, FriendlyName) {
   auto* u = create<U32>();
   EXPECT_EQ(u->FriendlyName(Symbols()), "u32");
diff --git a/src/ast/uint_literal.cc b/src/ast/uint_literal.cc
index f556f71..c00846e 100644
--- a/src/ast/uint_literal.cc
+++ b/src/ast/uint_literal.cc
@@ -28,10 +28,6 @@
 
 UintLiteral::~UintLiteral() = default;
 
-std::string UintLiteral::to_str(const sem::Info&) const {
-  return std::to_string(value()) + "u";
-}
-
 std::string UintLiteral::name() const {
   return "__uint" + std::to_string(value());
 }
diff --git a/src/ast/uint_literal.h b/src/ast/uint_literal.h
index e4dc96f..f6b084e 100644
--- a/src/ast/uint_literal.h
+++ b/src/ast/uint_literal.h
@@ -38,10 +38,6 @@
   /// @returns the name for this literal. This name is unique to this value.
   std::string name() const override;
 
-  /// @param sem the semantic info for the program
-  /// @returns the literal as a string
-  std::string to_str(const sem::Info& sem) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/uint_literal_test.cc b/src/ast/uint_literal_test.cc
index f88ec44..1da417d 100644
--- a/src/ast/uint_literal_test.cc
+++ b/src/ast/uint_literal_test.cc
@@ -26,11 +26,6 @@
   EXPECT_EQ(u->value(), 47u);
 }
 
-TEST_F(UintLiteralTest, ToStr) {
-  auto* u = create<UintLiteral>(42u);
-  EXPECT_EQ(str(u), "42u");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/unary_op_expression.cc b/src/ast/unary_op_expression.cc
index 41845a2..52eb687 100644
--- a/src/ast/unary_op_expression.cc
+++ b/src/ast/unary_op_expression.cc
@@ -41,17 +41,5 @@
   return ctx->dst->create<UnaryOpExpression>(src, op_, e);
 }
 
-void UnaryOpExpression::to_str(const sem::Info& sem,
-                               std::ostream& out,
-                               size_t indent) const {
-  make_indent(out, indent);
-  out << "UnaryOp[" << result_type_str(sem) << "]{" << std::endl;
-  make_indent(out, indent + 2);
-  out << op_ << std::endl;
-  expr_->to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/unary_op_expression.h b/src/ast/unary_op_expression.h
index 3f8e887..94f3137 100644
--- a/src/ast/unary_op_expression.h
+++ b/src/ast/unary_op_expression.h
@@ -48,14 +48,6 @@
   /// @return the newly cloned node
   UnaryOpExpression* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   UnaryOpExpression(const UnaryOpExpression&) = delete;
 
diff --git a/src/ast/unary_op_expression_test.cc b/src/ast/unary_op_expression_test.cc
index 6f8a253..db11eff 100644
--- a/src/ast/unary_op_expression_test.cc
+++ b/src/ast/unary_op_expression_test.cc
@@ -65,16 +65,6 @@
       "internal compiler error");
 }
 
-TEST_F(UnaryOpExpressionTest, ToStr) {
-  auto* ident = Expr("ident");
-  auto* u = create<UnaryOpExpression>(UnaryOp::kNot, ident);
-  EXPECT_EQ(str(u), R"(UnaryOp[not set]{
-  not
-  Identifier[not set]{ident}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/variable.cc b/src/ast/variable.cc
index be91229..c355054 100644
--- a/src/ast/variable.cc
+++ b/src/ast/variable.cc
@@ -73,64 +73,5 @@
                                     decos);
 }
 
-void Variable::info_to_str(const sem::Info& sem,
-                           std::ostream& out,
-                           size_t indent) const {
-  auto* var_sem = sem.Get(this);
-  make_indent(out, indent);
-  out << symbol_.to_str() << std::endl;
-  make_indent(out, indent);
-  out << (var_sem ? var_sem->StorageClass() : declared_storage_class())
-      << std::endl;
-  make_indent(out, indent);
-  out << declared_access_ << std::endl;
-  make_indent(out, indent);
-  if (type_) {
-    out << type_->type_name();
-  }
-  out << std::endl;
-}
-
-void Variable::constructor_to_str(const sem::Info& sem,
-                                  std::ostream& out,
-                                  size_t indent) const {
-  if (constructor_ == nullptr)
-    return;
-
-  make_indent(out, indent);
-  out << "{" << std::endl;
-
-  constructor_->to_str(sem, out, indent + 2);
-
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
-void Variable::to_str(const sem::Info& sem,
-                      std::ostream& out,
-                      size_t indent) const {
-  make_indent(out, indent);
-  out << "Variable";
-  if (is_const()) {
-    out << "Const";
-  }
-  out << "{" << std::endl;
-
-  if (!decorations_.empty()) {
-    make_indent(out, indent + 2);
-    out << "Decorations{" << std::endl;
-    for (auto* deco : decorations_) {
-      deco->to_str(sem, out, indent + 4);
-    }
-    make_indent(out, indent + 2);
-    out << "}" << std::endl;
-  }
-
-  info_to_str(sem, out, indent + 2);
-  constructor_to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/variable.h b/src/ast/variable.h
index a618b0b..2cbeaf2 100644
--- a/src/ast/variable.h
+++ b/src/ast/variable.h
@@ -157,30 +157,6 @@
   /// @return the newly cloned node
   Variable* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
- protected:
-  /// Output information for this variable.
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void info_to_str(const sem::Info& sem,
-                   std::ostream& out,
-                   size_t indent) const;
-  /// Output constructor for this variable.
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void constructor_to_str(const sem::Info& sem,
-                          std::ostream& out,
-                          size_t indent) const;
-
  private:
   Variable(const Variable&) = delete;
 
diff --git a/src/ast/variable_decl_statement.cc b/src/ast/variable_decl_statement.cc
index 549ffea..b686a83 100644
--- a/src/ast/variable_decl_statement.cc
+++ b/src/ast/variable_decl_statement.cc
@@ -40,15 +40,5 @@
   return ctx->dst->create<VariableDeclStatement>(src, var);
 }
 
-void VariableDeclStatement::to_str(const sem::Info& sem,
-                                   std::ostream& out,
-                                   size_t indent) const {
-  make_indent(out, indent);
-  out << "VariableDeclStatement{" << std::endl;
-  variable_->to_str(sem, out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/variable_decl_statement.h b/src/ast/variable_decl_statement.h
index 2456497..f1b3083 100644
--- a/src/ast/variable_decl_statement.h
+++ b/src/ast/variable_decl_statement.h
@@ -45,14 +45,6 @@
   /// @return the newly cloned node
   VariableDeclStatement* Clone(CloneContext* ctx) const override;
 
-  /// Writes a representation of the node to the output stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
  private:
   VariableDeclStatement(const VariableDeclStatement&) = delete;
 
diff --git a/src/ast/variable_decl_statement_test.cc b/src/ast/variable_decl_statement_test.cc
index e1deb63..cb61967 100644
--- a/src/ast/variable_decl_statement_test.cc
+++ b/src/ast/variable_decl_statement_test.cc
@@ -67,22 +67,6 @@
       "internal compiler error");
 }
 
-TEST_F(VariableDeclStatementTest, ToStr) {
-  auto* var = Var("a", ty.f32(), StorageClass::kNone);
-
-  auto* stmt =
-      create<VariableDeclStatement>(Source{Source::Location{20, 2}}, var);
-  EXPECT_EQ(str(stmt), R"(VariableDeclStatement{
-  Variable{
-    a
-    none
-    undefined
-    __f32
-  }
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/variable_test.cc b/src/ast/variable_test.cc
index fd223ac..9f29307 100644
--- a/src/ast/variable_test.cc
+++ b/src/ast/variable_test.cc
@@ -91,18 +91,6 @@
       "internal compiler error");
 }
 
-TEST_F(VariableTest, to_str) {
-  auto* v =
-      Var("my_var", ty.f32(), StorageClass::kFunction, ast::Access::kReadWrite);
-  EXPECT_EQ(str(v), R"(Variable{
-  my_var
-  function
-  read_write
-  __f32
-}
-)");
-}
-
 TEST_F(VariableTest, WithDecorations) {
   auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, nullptr,
                   DecorationList{
@@ -162,30 +150,6 @@
   EXPECT_EQ(var->binding_point().binding, nullptr);
 }
 
-TEST_F(VariableTest, Decorated_to_str) {
-  auto* var = Var("my_var", ty.f32(), StorageClass::kFunction,
-                  ast::Access::kRead, Expr("expr"),
-                  DecorationList{
-                      create<BindingDecoration>(2),
-                      create<GroupDecoration>(1),
-                  });
-
-  EXPECT_EQ(str(var), R"(Variable{
-  Decorations{
-    BindingDecoration{2}
-    GroupDecoration{1}
-  }
-  my_var
-  function
-  read
-  __f32
-  {
-    Identifier[not set]{expr}
-  }
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/vector.cc b/src/ast/vector.cc
index 23619d7..b3d6d89 100644
--- a/src/ast/vector.cc
+++ b/src/ast/vector.cc
@@ -35,10 +35,6 @@
 
 Vector::~Vector() = default;
 
-std::string Vector::type_name() const {
-  return "__vec_" + std::to_string(size_) + subtype_->type_name();
-}
-
 std::string Vector::FriendlyName(const SymbolTable& symbols) const {
   std::ostringstream out;
   out << "vec" << size_ << "<" << subtype_->FriendlyName(symbols) << ">";
diff --git a/src/ast/vector.h b/src/ast/vector.h
index 640c512..9a6f402 100644
--- a/src/ast/vector.h
+++ b/src/ast/vector.h
@@ -43,9 +43,6 @@
   /// @returns the size of the vector
   uint32_t size() const { return size_; }
 
-  /// @returns the name for th type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/vector_test.cc b/src/ast/vector_test.cc
index 4f4d0ed..4cbbc71 100644
--- a/src/ast/vector_test.cc
+++ b/src/ast/vector_test.cc
@@ -30,12 +30,6 @@
   EXPECT_EQ(v->size(), 2u);
 }
 
-TEST_F(AstVectorTest, TypeName) {
-  auto* i32 = create<I32>();
-  auto* v = create<Vector>(i32, 3);
-  EXPECT_EQ(v->type_name(), "__vec_3__i32");
-}
-
 TEST_F(AstVectorTest, FriendlyName) {
   auto* f32 = create<F32>();
   auto* v = create<Vector>(f32, 3);
diff --git a/src/ast/void.cc b/src/ast/void.cc
index 5591ceb..a96d5f0 100644
--- a/src/ast/void.cc
+++ b/src/ast/void.cc
@@ -28,10 +28,6 @@
 
 Void::~Void() = default;
 
-std::string Void::type_name() const {
-  return "__void";
-}
-
 std::string Void::FriendlyName(const SymbolTable&) const {
   return "void";
 }
diff --git a/src/ast/void.h b/src/ast/void.h
index 3ed79b0..94e0fda 100644
--- a/src/ast/void.h
+++ b/src/ast/void.h
@@ -33,9 +33,6 @@
   Void(Void&&);
   ~Void() override;
 
-  /// @returns the name for this type
-  std::string type_name() const override;
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
diff --git a/src/ast/workgroup_decoration.cc b/src/ast/workgroup_decoration.cc
index 33e94a6..9b346aa 100644
--- a/src/ast/workgroup_decoration.cc
+++ b/src/ast/workgroup_decoration.cc
@@ -36,22 +36,6 @@
   return "workgroup_size";
 }
 
-void WorkgroupDecoration::to_str(const sem::Info& sem,
-                                 std::ostream& out,
-                                 size_t indent) const {
-  make_indent(out, indent);
-  out << "WorkgroupDecoration{" << std::endl;
-  x_->to_str(sem, out, indent + 2);
-  if (y_) {
-    y_->to_str(sem, out, indent + 2);
-    if (z_) {
-      z_->to_str(sem, out, indent + 2);
-    }
-  }
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
 WorkgroupDecoration* WorkgroupDecoration::Clone(CloneContext* ctx) const {
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
diff --git a/src/ast/workgroup_decoration.h b/src/ast/workgroup_decoration.h
index aaf9df9..f9d8e56 100644
--- a/src/ast/workgroup_decoration.h
+++ b/src/ast/workgroup_decoration.h
@@ -49,14 +49,6 @@
   /// @returns the WGSL name for the decoration
   std::string name() const override;
 
-  /// Outputs the decoration to the given stream
-  /// @param sem the semantic info for the program
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
-
   /// Clones this node and all transitive child nodes using the `CloneContext`
   /// `ctx`.
   /// @param ctx the clone context
diff --git a/src/ast/workgroup_decoration_test.cc b/src/ast/workgroup_decoration_test.cc
index 1f0756d..f437c63 100644
--- a/src/ast/workgroup_decoration_test.cc
+++ b/src/ast/workgroup_decoration_test.cc
@@ -96,15 +96,6 @@
   EXPECT_EQ(Symbols().NameFor(z_ident->symbol()), "depth");
 }
 
-TEST_F(WorkgroupDecorationTest, ToStr) {
-  auto* d = WorkgroupSize(2, "height");
-  EXPECT_EQ(str(d), R"(WorkgroupDecoration{
-  ScalarConstructor[not set]{2}
-  Identifier[not set]{height}
-}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/program.cc b/src/program.cc
index c9814b3..c3cd9c1 100644
--- a/src/program.cc
+++ b/src/program.cc
@@ -127,19 +127,6 @@
   return Sem().Get(type_decl);
 }
 
-std::string Program::to_str(bool demangle) const {
-  AssertNotMoved();
-  auto str = ast_->to_str(Sem());
-  if (demangle) {
-    str = Demangler().Demangle(Symbols(), str);
-  }
-  return str;
-}
-
-std::string Program::str(const ast::Node* node) const {
-  return Demangler().Demangle(Symbols(), node->str(Sem()));
-}
-
 void Program::AssertNotMoved() const {
   TINT_ASSERT(Program, !moved_);
 }
diff --git a/src/program.h b/src/program.h
index d2ffe11..ad42704 100644
--- a/src/program.h
+++ b/src/program.h
@@ -164,28 +164,6 @@
   /// the type declaration has no resolved type.
   const sem::Type* TypeOf(const ast::TypeDecl* type_decl) const;
 
-  /// @param demangle whether to automatically demangle the symbols in the
-  /// returned string
-  /// @returns a string describing this program.
-  std::string to_str(bool demangle) const;
-
-  /// @returns a demangled string describing this program.
-  std::string to_str() const { return to_str(true); }
-
-  /// Writes a representation of the node to the output stream
-  /// @note unlike str(), to_str() does not automatically demangle the string.
-  /// @param node the AST node
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const ast::Node* node, std::ostream& out, size_t indent) const {
-    node->to_str(Sem(), out, indent);
-  }
-
-  /// Returns a demangled, string representation of `node`.
-  /// @param node the AST node
-  /// @returns a string representation of the node
-  std::string str(const ast::Node* node) const;
-
   /// A function that can be used to print a program
   using Printer = std::string (*)(const Program*);
 
diff --git a/src/program_builder.cc b/src/program_builder.cc
index a7f5db7..287175c 100644
--- a/src/program_builder.cc
+++ b/src/program_builder.cc
@@ -77,10 +77,6 @@
   return !diagnostics_.contains_errors();
 }
 
-std::string ProgramBuilder::str(const ast::Node* node) const {
-  return Demangler().Demangle(Symbols(), node->str(Sem()));
-}
-
 void ProgramBuilder::MarkAsMoved() {
   AssertNotMoved();
   moved_ = true;
diff --git a/src/program_builder.h b/src/program_builder.h
index 1bfa21f..bc529e8 100644
--- a/src/program_builder.h
+++ b/src/program_builder.h
@@ -294,20 +294,6 @@
   /// information
   bool IsValid() const;
 
-  /// Writes a representation of the node to the output stream
-  /// @note unlike str(), to_str() does not automatically demangle the string.
-  /// @param node the AST node
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(const ast::Node* node, std::ostream& out, size_t indent) const {
-    node->to_str(Sem(), out, indent);
-  }
-
-  /// Returns a demangled, string representation of `node`.
-  /// @param node the AST node
-  /// @returns a string representation of the node
-  std::string str(const ast::Node* node) const;
-
   /// Creates a new ast::Node owned by the ProgramBuilder. When the
   /// ProgramBuilder is destructed, the ast::Node will also be destructed.
   /// @param source the Source of the node
diff --git a/src/program_test.cc b/src/program_test.cc
index 3ec52c4..afedbf5 100644
--- a/src/program_test.cc
+++ b/src/program_test.cc
@@ -31,13 +31,6 @@
   EXPECT_EQ(program.AST().Functions().size(), 0u);
 }
 
-TEST_F(ProgramTest, ToStrEmitsPreambleAndPostamble) {
-  Program program(std::move(*this));
-  const auto str = program.to_str();
-  auto* const expected = "Module{\n}\n";
-  EXPECT_EQ(str, expected);
-}
-
 TEST_F(ProgramTest, EmptyIsValid) {
   Program program(std::move(*this));
   EXPECT_TRUE(program.IsValid());
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 25b2f3b..07d904f 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -725,12 +725,6 @@
 ast::Node* StatementBuilder::Clone(CloneContext*) const {
   return nullptr;
 }
-void StatementBuilder::to_str(const sem::Info&,
-                              std::ostream& out,
-                              size_t indent) const {
-  make_indent(out, indent);
-  out << "StatementBuilder" << std::endl;
-}
 
 FunctionEmitter::FunctionEmitter(ParserImpl* pi,
                                  const spvtools::opt::Function& function,
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index f128b41..edf5d48 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -356,9 +356,6 @@
 
  private:
   Node* Clone(CloneContext*) const override;
-  void to_str(const sem::Info& sem,
-              std::ostream& out,
-              size_t indent) const override;
 };
 
 /// A FunctionEmitter emits a SPIR-V function onto a Tint AST module.
diff --git a/src/reader/wgsl/parser_impl_switch_body_test.cc b/src/reader/wgsl/parser_impl_switch_body_test.cc
index 99d3899..e9b7809 100644
--- a/src/reader/wgsl/parser_impl_switch_body_test.cc
+++ b/src/reader/wgsl/parser_impl_switch_body_test.cc
@@ -131,8 +131,8 @@
   EXPECT_FALSE(e->IsDefault());
   ASSERT_EQ(e->body()->size(), 0u);
   ASSERT_EQ(e->selectors().size(), 2u);
-  ASSERT_EQ(e->selectors()[0]->to_str(Sem()), "1");
-  ASSERT_EQ(e->selectors()[1]->to_str(Sem()), "2");
+  ASSERT_EQ(e->selectors()[0]->value_as_i32(), 1);
+  ASSERT_EQ(e->selectors()[1]->value_as_i32(), 2);
 }
 
 TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectorsMissingColon) {
diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc
index 1a95b03..8a1511f 100644
--- a/src/resolver/validation_test.cc
+++ b/src/resolver/validation_test.cc
@@ -51,16 +51,12 @@
  public:
   FakeStmt(ProgramID program_id, Source source) : Base(program_id, source) {}
   FakeStmt* Clone(CloneContext*) const override { return nullptr; }
-  void to_str(const sem::Info&, std::ostream& out, size_t) const override {
-    out << "Fake";
-  }
 };
 
 class FakeExpr : public Castable<FakeExpr, ast::Expression> {
  public:
   FakeExpr(ProgramID program_id, Source source) : Base(program_id, source) {}
   FakeExpr* Clone(CloneContext*) const override { return nullptr; }
-  void to_str(const sem::Info&, std::ostream&, size_t) const override {}
 };
 
 TEST_F(ResolverValidationTest, WorkgroupMemoryUsedInVertexStage) {