Selaa lähdekoodia

Add RawStringOstream for slightly simpler streaming to strings (#4817)

This adds a RawStringOstream. Versus TestRawOstream, which is
consolidated over to RawStringOstream, it uses a string for storage
instead of a vector, mainly to support move-to-string semantics. Versus
llvm::raw_string_ostream, it owns the string and supports pwrite (which
is needed for driver and its fd_ostream compatibility requirement).

This converts most uses of llvm::raw_string_ostream, leaving behind a
few in InstNamer that explicitly cannot own the string, such as:

```
     llvm::raw_string_ostream(name)
          << "_" << tree.tokens().GetColumnNumber(token);
```

I have this as its own library so that it can use CHECK.

Yes this doesn't save much code, but it's code we repeatedly write.

---------

Co-authored-by: Geoff Romer <gromer@google.com>
Jon Ross-Perkins 1 vuosi sitten
vanhempi
sitoutus
4c4c4a4d2c
47 muutettua tiedostoa jossa 318 lisäystä ja 275 poistoa
  1. 27 7
      common/BUILD
  2. 17 19
      common/command_line.cpp
  3. 12 13
      common/command_line_test.cpp
  4. 2 2
      common/enum_base_test.cpp
  5. 8 6
      common/error.h
  6. 2 2
      common/error_test.cpp
  7. 6 6
      common/hashing_test.cpp
  8. 64 0
      common/raw_string_ostream.h
  9. 18 10
      common/raw_string_ostream_test.cpp
  10. 3 3
      common/vlog_test.cpp
  11. 1 1
      explorer/BUILD
  12. 1 1
      explorer/base/BUILD
  13. 2 2
      explorer/base/error_builders_test.cpp
  14. 2 2
      explorer/file_test.cpp
  15. 1 22
      testing/base/BUILD
  16. 15 13
      testing/base/source_gen.cpp
  17. 0 42
      testing/base/test_raw_ostream.h
  18. 2 0
      testing/file_test/BUILD
  19. 3 2
      testing/file_test/autoupdate.cpp
  20. 3 3
      testing/file_test/file_test_base.cpp
  21. 3 3
      toolchain/base/BUILD
  22. 3 3
      toolchain/base/shared_value_stores_test.cpp
  23. 2 0
      toolchain/check/BUILD
  24. 4 4
      toolchain/check/import_cpp.cpp
  25. 8 5
      toolchain/check/sem_ir_diagnostic_converter.cpp
  26. 7 3
      toolchain/driver/BUILD
  27. 10 11
      toolchain/driver/clang_runner_test.cpp
  28. 2 2
      toolchain/driver/driver_fuzzer.cpp
  29. 3 4
      toolchain/driver/driver_test.cpp
  30. 4 4
      toolchain/driver/format_subcommand.cpp
  31. 1 0
      toolchain/language_server/BUILD
  32. 3 3
      toolchain/language_server/language_server.cpp
  33. 2 1
      toolchain/lex/BUILD
  34. 13 11
      toolchain/lex/tokenized_buffer_benchmark.cpp
  35. 17 17
      toolchain/lex/tokenized_buffer_test.cpp
  36. 1 0
      toolchain/lower/BUILD
  37. 3 3
      toolchain/lower/mangler.cpp
  38. 1 1
      toolchain/parse/BUILD
  39. 3 4
      toolchain/parse/tree_test.cpp
  40. 5 1
      toolchain/sem_ir/BUILD
  41. 3 3
      toolchain/sem_ir/inst.h
  42. 19 25
      toolchain/sem_ir/inst_namer.cpp
  43. 3 3
      toolchain/sem_ir/stringify_type.cpp
  44. 3 3
      toolchain/sem_ir/type_info.cpp
  45. 2 3
      toolchain/sem_ir/yaml_test.cpp
  46. 1 0
      toolchain/testing/BUILD
  47. 3 2
      toolchain/testing/yaml_test_helpers.cpp

+ 27 - 7
common/BUILD

@@ -43,6 +43,7 @@ cc_library(
         ":check",
         ":error",
         ":ostream",
+        ":raw_string_ostream",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -54,8 +55,8 @@ cc_test(
     deps = [
         ":command_line",
         ":error_test_helpers",
+        ":raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
         "@llvm-project//llvm:Support",
     ],
@@ -108,8 +109,8 @@ cc_test(
     deps = [
         ":enum_base",
         ":enum_base_test_def",
+        ":raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
     ],
 )
@@ -120,6 +121,7 @@ cc_library(
     deps = [
         ":check",
         ":ostream",
+        ":raw_string_ostream",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -141,8 +143,8 @@ cc_test(
     deps = [
         ":error",
         ":error_test_helpers",
+        ":raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
     ],
 )
@@ -185,8 +187,8 @@ cc_test(
     srcs = ["hashing_test.cpp"],
     deps = [
         ":hashing",
+        ":raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
         "@llvm-project//llvm:Support",
     ],
@@ -289,7 +291,6 @@ cc_test(
         ":map",
         ":raw_hashtable_test_helpers",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
     ],
 )
@@ -411,6 +412,26 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "raw_string_ostream",
+    hdrs = ["raw_string_ostream.h"],
+    deps = [
+        ":check",
+        ":ostream",
+    ],
+)
+
+cc_test(
+    name = "raw_string_ostream_test",
+    size = "small",
+    srcs = ["raw_string_ostream_test.cpp"],
+    deps = [
+        ":raw_string_ostream",
+        "//testing/base:gtest_main",
+        "@googletest//:gtest",
+    ],
+)
+
 cc_library(
     name = "set",
     hdrs = ["set.h"],
@@ -430,7 +451,6 @@ cc_test(
         ":raw_hashtable_test_helpers",
         ":set",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
     ],
 )
@@ -616,9 +636,9 @@ cc_test(
     size = "small",
     srcs = ["vlog_test.cpp"],
     deps = [
+        ":raw_string_ostream",
         ":vlog",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
     ],
 )

+ 17 - 19
common/command_line.cpp

@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/PointerIntPair.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -550,12 +551,11 @@ void MetaPrinter::PrintRawUsageCommandAndOptions(const Command& command,
   *out_ << command.info.name;
 
   // Buffer the options rendering so we can limit its length.
-  std::string buffer_str;
-  llvm::raw_string_ostream buffer_out(buffer_str);
+  RawStringOstream buffer_out;
   MetaPrinter buffer_printer(&buffer_out);
   bool have_short_flags = false;
   for (const auto& arg : command.options) {
-    if (static_cast<int>(buffer_str.size()) > max_option_width) {
+    if (static_cast<int>(buffer_out.size()) > max_option_width) {
       break;
     }
     // We can summarize positive boolean flags with a short name using a
@@ -571,7 +571,7 @@ void MetaPrinter::PrintRawUsageCommandAndOptions(const Command& command,
   }
   llvm::StringRef space = have_short_flags ? " " : "";
   for (const auto& option : command.options) {
-    if (static_cast<int>(buffer_str.size()) > max_option_width) {
+    if (static_cast<int>(buffer_out.size()) > max_option_width) {
       break;
     }
     if (option->is_help_hidden || option->meta_action) {
@@ -587,10 +587,11 @@ void MetaPrinter::PrintRawUsageCommandAndOptions(const Command& command,
     buffer_printer.PrintOptionUsage(*option);
     space = " ";
   }
-  if (!buffer_str.empty()) {
-    if (static_cast<int>(buffer_str.size()) <= max_option_width) {
-      *out_ << " [" << buffer_str << "]";
+  if (!buffer_out.empty()) {
+    if (static_cast<int>(buffer_out.size()) <= max_option_width) {
+      *out_ << " [" << buffer_out.TakeStr() << "]";
     } else {
+      buffer_out.clear();
       *out_ << " [OPTIONS]";
     }
   }
@@ -911,8 +912,7 @@ auto Parser::ParseOneOfArgValue(const Arg& arg, llvm::StringRef value)
     -> ErrorOr<Success> {
   CARBON_CHECK(arg.kind == Arg::Kind::OneOf, "Incorrect kind: {0}", arg.kind);
   if (!arg.value_action(arg, value)) {
-    std::string error_str;
-    llvm::raw_string_ostream error(error_str);
+    RawStringOstream error;
     error << "option `--" << arg.info.name << "=";
     llvm::printEscapedString(value, error);
     error << "` has an invalid value `";
@@ -920,7 +920,7 @@ auto Parser::ParseOneOfArgValue(const Arg& arg, llvm::StringRef value)
     error << "`; valid values are: ";
     PrintListOfAlternatives(error, arg.value_strings,
                             [](llvm::StringRef x) { return x; });
-    return Error(error_str);
+    return Error(error.TakeStr());
   }
   return Success();
 }
@@ -1079,14 +1079,14 @@ auto Parser::FinalizeParsedOptions() -> ErrorOr<Success> {
     return lhs->info.name < rhs->info.name;
   });
 
-  std::string error_str = "required options not provided: ";
-  llvm::raw_string_ostream error(error_str);
+  RawStringOstream error;
+  error << "required options not provided: ";
   llvm::ListSeparator sep;
   for (const Arg* option : missing_options) {
     error << sep << "--" << option->info.name;
   }
 
-  return Error(error_str);
+  return Error(error.TakeStr());
 }
 
 auto Parser::ParsePositionalArg(llvm::StringRef unparsed_arg)
@@ -1117,12 +1117,11 @@ auto Parser::ParsePositionalArg(llvm::StringRef unparsed_arg)
 auto Parser::ParseSubcommand(llvm::StringRef unparsed_arg) -> ErrorOr<Success> {
   auto subcommand_it = subcommand_map_.find(unparsed_arg);
   if (subcommand_it == subcommand_map_.end()) {
-    std::string error_str;
-    llvm::raw_string_ostream error(error_str);
+    RawStringOstream error;
     error << "invalid subcommand `" << unparsed_arg
           << "`; available subcommands: ";
     MetaPrinter(&error).PrintSubcommands(*command_);
-    return Error(error_str);
+    return Error(error.TakeStr());
   }
 
   // Before we recurse into the subcommand, verify that all the required
@@ -1179,11 +1178,10 @@ auto Parser::FinalizeParse() -> ErrorOr<ParseResult> {
     case Command::Kind::Invalid:
       CARBON_FATAL("Should never have a parser with an invalid command!");
     case Command::Kind::RequiresSubcommand: {
-      std::string error_str;
-      llvm::raw_string_ostream error(error_str);
+      RawStringOstream error;
       error << "no subcommand specified; available subcommands: ";
       MetaPrinter(&error).PrintSubcommands(*command_);
-      return Error(error_str);
+      return Error(error.TakeStr());
     }
     case Command::Kind::Action:
       // All arguments have been successfully parsed, run any action for the

+ 12 - 13
common/command_line_test.cpp

@@ -8,15 +8,14 @@
 #include <gtest/gtest.h>
 
 #include "common/error_test_helpers.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/Support/FormatVariadic.h"
-#include "testing/base/test_raw_ostream.h"
 
 namespace Carbon::CommandLine {
 namespace {
 
 using ::Carbon::Testing::IsError;
 using ::Carbon::Testing::IsSuccess;
-using ::Carbon::Testing::TestRawOstream;
 using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::StrEq;
@@ -92,7 +91,7 @@ TEST(ArgParserTest, BooleanFlags) {
               IsSuccess(Eq(ParseResult::Success)));
   EXPECT_FALSE(flag);
 
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(
       parse({"--no-flag=true"}, os),
       IsError(StrEq("cannot specify a value when using a flag name prefixed "
@@ -187,7 +186,7 @@ TEST(ArgParserTest, ShortArgs) {
   EXPECT_TRUE(example);
   EXPECT_THAT(integer_option, Eq(123));
 
-  TestRawOstream os;
+  RawStringOstream os;
 
   EXPECT_THAT(parse({"-v"}, os), IsError(StrEq("unknown short option `-v`")));
   EXPECT_THAT(parse({"-xvx"}, os), IsError(StrEq("unknown short option `-v`")));
@@ -231,7 +230,7 @@ TEST(ArgParserTest, PositionalArgs) {
     });
   };
 
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(parse({"--flag", "--option=x"}, os),
               IsError(StrEq(
                   "not all required positional arguments were provided; first "
@@ -277,7 +276,7 @@ TEST(ArgParserTest, PositionalAppendArgs) {
     });
   };
 
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(parse({"--flag", "--option=x"}, os),
               IsError(StrEq(
                   "not all required positional arguments were provided; first "
@@ -339,7 +338,7 @@ TEST(ArgParserTest, BasicSubcommands) {
     });
   };
 
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(parse({}, os),
               IsError(StrEq("no subcommand specified; available subcommands: "
                             "`sub1`, `sub2`, or `help`")));
@@ -474,7 +473,7 @@ TEST(ArgParserTest, OneOfOption) {
               IsSuccess(Eq(ParseResult::Success)));
   EXPECT_THAT(value, Eq(3));
 
-  TestRawOstream os;
+  RawStringOstream os;
 
   EXPECT_THAT(
       parse({"--option"}, os),
@@ -502,7 +501,7 @@ TEST(ArgParserTest, OneOfOption) {
 
 TEST(ArgParserTest, OneOfOptionWithTwoOptions) {
   int value = 0;
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(ParseOneOfOption({"--option=z"}, os,
                                [&](auto& arg_b) {
                                  arg_b.SetOneOf(
@@ -518,7 +517,7 @@ TEST(ArgParserTest, OneOfOptionWithTwoOptions) {
 
 TEST(ArgParserTest, OneOfOptionWithOneOption) {
   int value = 0;
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(ParseOneOfOption({"--option=z"}, os,
                                [&](auto& arg_b) {
                                  arg_b.SetOneOf(
@@ -611,7 +610,7 @@ TEST(ArgParserTest, RequiredArgs) {
     });
   };
 
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(parse({}, os),
               IsError(StrEq("required options not provided: --opt1, --opt2")));
 
@@ -771,7 +770,7 @@ A hidden subcommand.
         });
   };
 
-  TestRawOstream os;
+  RawStringOstream os;
 
   EXPECT_THAT(parse({"--flag", "--help"}, os),
               IsSuccess(Eq(ParseResult::MetaSuccess)));
@@ -936,7 +935,7 @@ x
         });
   };
 
-  TestRawOstream os;
+  RawStringOstream os;
   EXPECT_THAT(parse({"--help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess)));
   EXPECT_THAT(os.TakeStr(), StrEq(llvm::StringRef(R"""(
 Usage:

+ 2 - 2
common/enum_base_test.cpp

@@ -6,7 +6,7 @@
 
 #include <gtest/gtest.h>
 
-#include "testing/base/test_raw_ostream.h"
+#include "common/raw_string_ostream.h"
 
 namespace Carbon {
 namespace {
@@ -45,7 +45,7 @@ TEST(EnumBaseTest, NamesAndConstants) {
 }
 
 TEST(EnumBaseTest, Printing) {
-  Testing::TestRawOstream stream;
+  RawStringOstream stream;
 
   TestKind kind = TestKind::Beep;
   stream << kind << " " << TestKind::Beep;

+ 8 - 6
common/error.h

@@ -10,6 +10,7 @@
 
 #include "common/check.h"
 #include "common/ostream.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/Twine.h"
 
 namespace Carbon {
@@ -138,7 +139,10 @@ class ErrorBuilder {
  public:
   explicit ErrorBuilder(std::string location = "")
       : location_(std::move(location)),
-        out_(std::make_unique<llvm::raw_string_ostream>(message_)) {}
+        out_(std::make_unique<RawStringOstream>()) {}
+
+  ErrorBuilder(ErrorBuilder&&) = default;
+  auto operator=(ErrorBuilder&&) -> ErrorBuilder& = default;
 
   // Accumulates string message to a temporary `ErrorBuilder`. After streaming,
   // the builder must be converted to an `Error` or `ErrorOr`.
@@ -156,19 +160,17 @@ class ErrorBuilder {
   }
 
   // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns.
-  operator Error() { return Error(location_, message_); }
+  operator Error() { return Error(location_, out_->TakeStr()); }
 
   template <typename T>
   // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns.
   operator ErrorOr<T>() {
-    return Error(location_, message_);
+    return Error(location_, out_->TakeStr());
   }
 
  private:
   std::string location_;
-  std::string message_;
-  // Use a pointer to allow move construction.
-  std::unique_ptr<llvm::raw_string_ostream> out_;
+  std::unique_ptr<RawStringOstream> out_;
 };
 
 }  // namespace Carbon

+ 2 - 2
common/error_test.cpp

@@ -7,7 +7,7 @@
 #include <gtest/gtest.h>
 
 #include "common/error_test_helpers.h"
-#include "testing/base/test_raw_ostream.h"
+#include "common/raw_string_ostream.h"
 
 namespace Carbon {
 namespace {
@@ -108,7 +108,7 @@ TEST(ErrorTest, ErrorBuilderOperatorImplicitCast) {
 
 TEST(ErrorTest, StreamError) {
   Error result = ErrorBuilder("TestFunc") << "msg";
-  Testing::TestRawOstream result_stream;
+  RawStringOstream result_stream;
   result_stream << result;
   EXPECT_EQ(result_stream.TakeStr(), "TestFunc: msg");
 }

+ 6 - 6
common/hashing_test.cpp

@@ -10,6 +10,7 @@
 #include <concepts>
 #include <type_traits>
 
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -469,15 +470,14 @@ TEST(HashingTest, TupleRecursion) {
 constexpr uint64_t TestSeed = 42ULL * 1024;
 
 auto ToHexBytes(llvm::StringRef s) -> std::string {
-  std::string rendered;
-  llvm::raw_string_ostream os(rendered);
-  os << "{";
+  RawStringOstream rendered;
+  rendered << "{";
   llvm::ListSeparator sep(", ");
   for (const char c : s) {
-    os << sep << llvm::formatv("{0:x2}", static_cast<uint8_t>(c));
+    rendered << sep << llvm::formatv("{0:x2}", static_cast<uint8_t>(c));
   }
-  os << "}";
-  return rendered;
+  rendered << "}";
+  return rendered.TakeStr();
 }
 
 template <typename T>

+ 64 - 0
common/raw_string_ostream.h

@@ -0,0 +1,64 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_COMMON_RAW_STRING_OSTREAM_H_
+#define CARBON_COMMON_RAW_STRING_OSTREAM_H_
+
+#include "common/check.h"
+#include "common/ostream.h"
+
+namespace Carbon {
+
+// Implements streaming output with an underlying string. The string must always
+// be taken prior to destruction.
+//
+// Versus `llvm::raw_string_ostream`:
+// - Owns the underlying string.
+// - Supports `pwrite` for compatibility with more stream uses in tests.
+class RawStringOstream : public llvm::raw_pwrite_stream {
+ public:
+  explicit RawStringOstream() : llvm::raw_pwrite_stream(/*Unbuffered=*/true) {}
+
+  ~RawStringOstream() override {
+    CARBON_CHECK(str_.empty(), "Expected to be emptied by TakeStr, have: {0}",
+                 str_);
+  }
+
+  // Returns the streamed contents, clearing the stream back to empty.
+  auto TakeStr() -> std::string {
+    std::string result = std::move(str_);
+    clear();
+    return result;
+  }
+
+  // Clears the buffer, which can be helpful when destructing without
+  // necessarily calling `TakeStr()`.
+  auto clear() -> void { str_.clear(); }
+
+  auto empty() -> bool { return str_.empty(); }
+  auto size() -> size_t { return str_.size(); }
+
+ private:
+  auto current_pos() const -> uint64_t override { return str_.size(); }
+
+  auto pwrite_impl(const char* ptr, size_t size, uint64_t offset)
+      -> void override {
+    str_.replace(offset, size, ptr, size);
+  }
+
+  auto write_impl(const char* ptr, size_t size) -> void override {
+    str_.append(ptr, size);
+  }
+
+  void reserveExtraSpace(uint64_t extra_size) override {
+    str_.reserve(str_.size() + extra_size);
+  }
+
+  // The actual buffer.
+  std::string str_;
+};
+
+}  // namespace Carbon
+
+#endif  // CARBON_COMMON_RAW_STRING_OSTREAM_H_

+ 18 - 10
testing/base/test_raw_ostream_test.cpp → common/raw_string_ostream_test.cpp

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "testing/base/test_raw_ostream.h"
+#include "common/raw_string_ostream.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -13,8 +13,8 @@ namespace {
 using ::testing::HasSubstr;
 using ::testing::StrEq;
 
-TEST(TestRawOstreamTest, Basics) {
-  TestRawOstream os;
+TEST(RawStringOstreamTest, Basics) {
+  RawStringOstream os;
 
   os << "test 1";
   EXPECT_THAT(os.TakeStr(), StrEq("test 1"));
@@ -30,9 +30,9 @@ TEST(TestRawOstreamTest, Basics) {
   EXPECT_THAT(os.TakeStr(), StrEq("test 3"));
 }
 
-TEST(TestRawOstreamTest, MultipleStreams) {
-  TestRawOstream os1;
-  TestRawOstream os2;
+TEST(RawStringOstreamTest, MultipleStreams) {
+  RawStringOstream os1;
+  RawStringOstream os2;
 
   os1 << "test ";
   os2 << "test stream 2";
@@ -41,8 +41,8 @@ TEST(TestRawOstreamTest, MultipleStreams) {
   EXPECT_THAT(os2.TakeStr(), StrEq("test stream 2"));
 }
 
-TEST(TestRawOstreamTest, MultipleLines) {
-  TestRawOstream os;
+TEST(RawStringOstreamTest, MultipleLines) {
+  RawStringOstream os;
 
   os << "test line 1\n";
   os << "test line 2\n";
@@ -50,8 +50,8 @@ TEST(TestRawOstreamTest, MultipleLines) {
   EXPECT_THAT(os.TakeStr(), StrEq("test line 1\ntest line 2\ntest line 3\n"));
 }
 
-TEST(TestRawOstreamTest, Substring) {
-  TestRawOstream os;
+TEST(RawStringOstreamTest, Substring) {
+  RawStringOstream os;
 
   os << "test line 1\n";
   os << "test line 2\n";
@@ -59,5 +59,13 @@ TEST(TestRawOstreamTest, Substring) {
   EXPECT_THAT(os.TakeStr(), HasSubstr("test line 2"));
 }
 
+TEST(RawStringOstreamTest, Pwrite) {
+  RawStringOstream os;
+  os << "test line 1\n";
+  os.pwrite("splat", 5, 1);
+  os << "test line 2\n";
+  EXPECT_THAT(os.TakeStr(), HasSubstr("tsplatine 1\ntest line 2\n"));
+}
+
 }  // namespace
 }  // namespace Carbon::Testing

+ 3 - 3
common/vlog_test.cpp

@@ -7,7 +7,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "testing/base/test_raw_ostream.h"
+#include "common/raw_string_ostream.h"
 
 namespace Carbon::Testing {
 namespace {
@@ -30,7 +30,7 @@ class VLogger {
   auto TakeStr() -> std::string { return buffer_.TakeStr(); }
 
  private:
-  TestRawOstream buffer_;
+  RawStringOstream buffer_;
 
   llvm::raw_ostream* vlog_stream_ = nullptr;
 };
@@ -50,7 +50,7 @@ TEST(VLogTest, Disabled) {
 }
 
 TEST(VLogTest, To) {
-  TestRawOstream buffer;
+  RawStringOstream buffer;
   CARBON_VLOG_TO(&buffer, "Test");
   EXPECT_THAT(buffer.TakeStr(), "Test");
 }

+ 1 - 1
explorer/BUILD

@@ -57,7 +57,7 @@ cc_binary(
     deps = [
         ":main",
         "//common:check",
-        "//testing/base:test_raw_ostream",
+        "//common:raw_string_ostream",
         "//testing/file_test:file_test_base",
         "@abseil-cpp//absl/flags:flag",
         "@re2",

+ 1 - 1
explorer/base/BUILD

@@ -79,8 +79,8 @@ cc_test(
     deps = [
         ":error_builders",
         ":source_location",
+        "//common:raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
     ],
 )

+ 2 - 2
explorer/base/error_builders_test.cpp

@@ -6,8 +6,8 @@
 
 #include <gtest/gtest.h>
 
+#include "common/raw_string_ostream.h"
 #include "explorer/base/source_location.h"
-#include "testing/base/test_raw_ostream.h"
 
 namespace Carbon {
 namespace {
@@ -17,7 +17,7 @@ TEST(ErrorBuildersTest, ProgramError) {
   EXPECT_EQ(err.location(), "x:1");
   EXPECT_EQ(err.message(), "test");
 
-  Testing::TestRawOstream out;
+  RawStringOstream out;
   out << err;
   EXPECT_EQ(out.TakeStr(), "x:1: test");
 }

+ 2 - 2
explorer/file_test.cpp

@@ -3,9 +3,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "absl/flags/flag.h"
+#include "common/raw_string_ostream.h"
 #include "explorer/main.h"
 #include "re2/re2.h"
-#include "testing/base/test_raw_ostream.h"
 #include "testing/file_test/file_test_base.h"
 
 ABSL_FLAG(bool, trace, false,
@@ -103,7 +103,7 @@ class ExplorerFileTest : public FileTestBase {
     return test_name().find("/trace/") != std::string::npos;
   }
 
-  TestRawOstream trace_stream_;
+  RawStringOstream trace_stream_;
   RE2 prelude_line_re_;
   RE2 timing_re_;
 };

+ 1 - 22
testing/base/BUILD

@@ -53,6 +53,7 @@ cc_library(
     deps = [
         "//common:check",
         "//common:map",
+        "//common:raw_string_ostream",
         "//common:set",
         "//toolchain/lex:token_kind",
         "@abseil-cpp//absl/random",
@@ -90,28 +91,6 @@ cc_binary(
     ],
 )
 
-cc_library(
-    name = "test_raw_ostream",
-    testonly = 1,
-    hdrs = ["test_raw_ostream.h"],
-    deps = [
-        "//common:ostream",
-        "@googletest//:gtest",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_test(
-    name = "test_raw_ostream_test",
-    size = "small",
-    srcs = ["test_raw_ostream_test.cpp"],
-    deps = [
-        ":test_raw_ostream",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-    ],
-)
-
 cc_library(
     name = "global_exe_path",
     testonly = 1,

+ 15 - 13
testing/base/source_gen.cpp

@@ -6,6 +6,7 @@
 
 #include <numeric>
 
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/SmallVector.h"
@@ -311,8 +312,7 @@ static auto EstimateAvgClassDefLines(SourceGen::ClassParams params) -> double {
 auto SourceGen::GenAPIFileDenseDecls(int target_lines,
                                      const DenseDeclParams& params)
     -> std::string {
-  std::string source;
-  llvm::raw_string_ostream os(source);
+  RawStringOstream source;
 
   // Figure out how many classes fit in our target lines, each separated by a
   // blank line. We need to account the comment lines below to start the file.
@@ -327,27 +327,29 @@ auto SourceGen::GenAPIFileDenseDecls(int target_lines,
   int expected_lines =
       NumFileCommentLines + num_classes * (avg_class_lines + 1);
 
-  os << "// Generated " << (!IsCpp() ? "Carbon" : "C++") << " source file.\n";
-  os << llvm::formatv("// {0} target lines: {1} classes, {2} expected lines",
-                      target_lines, num_classes, expected_lines)
-     << "\n";
-  os << "//\n// Generating as an API file with dense declarations.\n";
+  source << "// Generated " << (!IsCpp() ? "Carbon" : "C++")
+         << " source file.\n";
+  source << llvm::formatv(
+                "// {0} target lines: {1} classes, {2} expected lines",
+                target_lines, num_classes, expected_lines)
+         << "\n";
+  source << "//\n// Generating as an API file with dense declarations.\n";
 
   // Carbon uses an implicitly imported prelude to get builtin types, but C++
   // requires header files so include those.
   if (IsCpp()) {
-    os << "\n";
+    source << "\n";
     // Header for specific integer types like `std::int64_t`.
-    os << "#include <cstdint>\n";
+    source << "#include <cstdint>\n";
     // Header for `std::pair`.
-    os << "#include <utility>\n";
+    source << "#include <utility>\n";
   }
 
   auto class_gen_state = ClassGenState(*this, num_classes, params.class_params,
                                        params.type_use_params);
   for ([[maybe_unused]] int _ : llvm::seq(num_classes)) {
-    os << "\n";
-    GenerateClassDef(params.class_params, class_gen_state, os);
+    source << "\n";
+    GenerateClassDef(params.class_params, class_gen_state, source);
   }
 
   // Make sure we consumed all the state.
@@ -358,7 +360,7 @@ auto SourceGen::GenAPIFileDenseDecls(int target_lines,
   CARBON_CHECK(class_gen_state.class_names().empty());
   CARBON_CHECK(class_gen_state.type_names().empty());
 
-  return source;
+  return source.TakeStr();
 }
 
 auto SourceGen::GetShuffledIdentifiers(int number, int min_length,

+ 0 - 42
testing/base/test_raw_ostream.h

@@ -1,42 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#ifndef CARBON_TESTING_BASE_TEST_RAW_OSTREAM_H_
-#define CARBON_TESTING_BASE_TEST_RAW_OSTREAM_H_
-
-#include <gtest/gtest.h>
-
-#include <string>
-
-#include "common/ostream.h"
-#include "llvm/ADT/SmallString.h"
-
-namespace Carbon::Testing {
-
-// A raw_ostream that makes it easy to repeatedly check streamed output.
-class TestRawOstream : public llvm::raw_svector_ostream {
- public:
-  explicit TestRawOstream() : llvm::raw_svector_ostream(buffer_) {}
-
-  ~TestRawOstream() override {
-    if (!buffer_.empty()) {
-      ADD_FAILURE() << "Unchecked output:\n" << buffer_;
-    }
-  }
-
-  // Flushes the stream and returns the contents so far, clearing the stream
-  // back to empty.
-  auto TakeStr() -> std::string {
-    std::string result = buffer_.str().str();
-    buffer_.clear();
-    return result;
-  }
-
- private:
-  llvm::SmallString<16> buffer_;
-};
-
-}  // namespace Carbon::Testing
-
-#endif  // CARBON_TESTING_BASE_TEST_RAW_OSTREAM_H_

+ 2 - 0
testing/file_test/BUILD

@@ -18,6 +18,7 @@ cc_library(
     deps = [
         "//common:check",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "@abseil-cpp//absl/strings:string_view",
         "@llvm-project//llvm:Support",
         "@re2",
@@ -36,6 +37,7 @@ cc_library(
         "//common:exe_path",
         "//common:init_llvm",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "@abseil-cpp//absl/flags:flag",
         "@abseil-cpp//absl/flags:parse",
         "@googletest//:gtest",

+ 3 - 2
testing/file_test/autoupdate.cpp

@@ -9,6 +9,7 @@
 #include "absl/strings/string_view.h"
 #include "common/check.h"
 #include "common/ostream.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -363,11 +364,11 @@ auto FileTestAutoupdater::Run(bool dry_run) -> bool {
   }
 
   // Generate the autoupdated file.
-  std::string new_content;
-  llvm::raw_string_ostream new_content_stream(new_content);
+  RawStringOstream new_content_stream;
   for (const auto& line : new_lines_) {
     new_content_stream << *line << '\n';
   }
+  std::string new_content = new_content_stream.TakeStr();
 
   // Update the file on disk if needed.
   if (new_content == input_content_) {

+ 3 - 3
testing/file_test/file_test_base.cpp

@@ -18,6 +18,7 @@
 #include "common/error.h"
 #include "common/exe_path.h"
 #include "common/init_llvm.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
@@ -108,8 +109,7 @@ enum class BazelMode : uint8_t {
 // Returns the requested bazel command string for the given execution mode.
 static auto GetBazelCommand(BazelMode mode, llvm::StringRef test_name)
     -> std::string {
-  std::string args_str;
-  llvm::raw_string_ostream args(args_str);
+  RawStringOstream args;
 
   const char* target = getenv("TEST_TARGET");
   args << "bazel " << ((mode == BazelMode::Test) ? "test" : "run") << " "
@@ -131,7 +131,7 @@ static auto GetBazelCommand(BazelMode mode, llvm::StringRef test_name)
 
   args << "--file_tests=";
   args << test_name;
-  return args_str;
+  return args.TakeStr();
 }
 
 // Runs a test and compares output. This keeps output split by line so that

+ 3 - 3
toolchain/base/BUILD

@@ -83,8 +83,8 @@ cc_test(
     deps = [
         ":value_ids",
         ":value_store",
+        "//common:raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "//toolchain/testing:yaml_test_helpers",
         "@googletest//:gtest",
     ],
@@ -113,8 +113,8 @@ cc_test(
     srcs = ["int_test.cpp"],
     deps = [
         ":int",
+        "//common:raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "//toolchain/testing:yaml_test_helpers",
         "@googletest//:gtest",
     ],
@@ -138,8 +138,8 @@ cc_test(
     srcs = ["shared_value_stores_test.cpp"],
     deps = [
         ":shared_value_stores",
+        "//common:raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "//toolchain/testing:yaml_test_helpers",
         "@googletest//:gtest",
     ],

+ 3 - 3
toolchain/base/shared_value_stores_test.cpp

@@ -7,7 +7,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "testing/base/test_raw_ostream.h"
+#include "common/raw_string_ostream.h"
 #include "toolchain/testing/yaml_test_helpers.h"
 
 namespace Carbon::Testing {
@@ -31,7 +31,7 @@ auto MatchSharedValues(testing::Matcher<Yaml::MappingValue> ints,
 
 TEST(SharedValueStores, PrintEmpty) {
   SharedValueStores value_stores;
-  TestRawOstream out;
+  RawStringOstream out;
   value_stores.Print(out);
   EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()),
               MatchSharedValues(IsEmpty(), IsEmpty(), IsEmpty(), IsEmpty()));
@@ -46,7 +46,7 @@ TEST(SharedValueStores, PrintVals) {
       Real{.mantissa = apint, .exponent = apint, .is_decimal = true});
   value_stores.identifiers().Add("a");
   value_stores.string_literal_values().Add("foo'\"baz");
-  TestRawOstream out;
+  RawStringOstream out;
   value_stores.Print(out);
 
   EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()),

+ 2 - 0
toolchain/check/BUILD

@@ -74,6 +74,7 @@ cc_library(
         "//common:array_stack",
         "//common:check",
         "//common:map",
+        "//common:raw_string_ostream",
         "//common:set",
         "//common:vlog",
         "//toolchain/base:index_base",
@@ -281,6 +282,7 @@ cc_library(
     hdrs = ["sem_ir_diagnostic_converter.h"],
     deps = [
         ":context",
+        "//common:raw_string_ostream",
         "//toolchain/diagnostics:diagnostic_emitter",
         "//toolchain/lex:token_index",
         "//toolchain/parse:tree_node_diagnostic_converter",

+ 4 - 4
toolchain/check/import_cpp.cpp

@@ -9,6 +9,7 @@
 
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Tooling/Tooling.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
@@ -22,8 +23,7 @@ namespace Carbon::Check {
 auto ImportCppFile(Context& context, SemIRLoc loc,
                    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
                    llvm::StringRef file_path, llvm::StringRef code) -> void {
-  std::string diagnostics_str;
-  llvm::raw_string_ostream diagnostics_stream(diagnostics_str);
+  RawStringOstream diagnostics_stream;
 
   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnostic_options(
       new clang::DiagnosticOptions());
@@ -45,13 +45,13 @@ auto ImportCppFile(Context& context, SemIRLoc loc,
         "{0} error{0:s} and {1} warning{1:s} in `Cpp` import `{2}`:\n{3}",
         IntAsSelect, IntAsSelect, std::string, std::string);
     context.emitter().Emit(loc, CppInteropParseError, num_errors, num_warnings,
-                           file_path.str(), diagnostics_str);
+                           file_path.str(), diagnostics_stream.TakeStr());
   } else if (num_warnings > 0) {
     CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning,
                       "{0} warning{0:s} in `Cpp` import `{1}`:\n{2}",
                       IntAsSelect, std::string, std::string);
     context.emitter().Emit(loc, CppInteropParseWarning, num_warnings,
-                           file_path.str(), diagnostics_str);
+                           file_path.str(), diagnostics_stream.TakeStr());
   }
 }
 

+ 8 - 5
toolchain/check/sem_ir_diagnostic_converter.cpp

@@ -4,7 +4,9 @@
 
 #include "toolchain/check/sem_ir_diagnostic_converter.h"
 
+#include "common/raw_string_ostream.h"
 #include "toolchain/sem_ir/stringify_type.h"
+
 namespace Carbon::Check {
 
 auto SemIRDiagnosticConverter::ConvertLoc(SemIRLoc loc,
@@ -146,11 +148,12 @@ auto SemIRDiagnosticConverter::ConvertArg(llvm::Any arg) const -> llvm::Any {
     } else if (!library_name_id->is_valid()) {
       library_name = "library <invalid>";
     } else {
-      llvm::raw_string_ostream stream(library_name);
-      stream << "library \"";
-      stream << sem_ir_->string_literal_values().Get(
-          library_name_id->AsStringLiteralValueId());
-      stream << "\"";
+      RawStringOstream stream;
+      stream << "library \""
+             << sem_ir_->string_literal_values().Get(
+                    library_name_id->AsStringLiteralValueId())
+             << "\"";
+      library_name = stream.TakeStr();
     }
     return library_name;
   }

+ 7 - 3
toolchain/driver/BUILD

@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
+load("//bazel/cc_toolchains:defs.bzl", "cc_env")
 load("//testing/fuzzing:rules.bzl", "cc_fuzz_test")
 
 package(default_visibility = ["//visibility:public"])
@@ -41,14 +42,15 @@ cc_test(
     name = "clang_runner_test",
     size = "small",
     srcs = ["clang_runner_test.cpp"],
+    env = cc_env(),
     deps = [
         ":clang_runner",
         "//common:all_llvm_targets",
         "//common:check",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//testing/base:global_exe_path",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "@googletest//:gtest",
         "@llvm-project//llvm:Object",
         "@llvm-project//llvm:Support",
@@ -113,6 +115,7 @@ cc_library(
         ":clang_runner",
         "//common:command_line",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//common:version",
         "//common:vlog",
         "//toolchain/base:pretty_stack_trace_function",
@@ -144,12 +147,13 @@ cc_test(
     name = "driver_test",
     size = "small",
     srcs = ["driver_test.cpp"],
+    env = cc_env(),
     deps = [
         ":driver",
         "//common:all_llvm_targets",
+        "//common:raw_string_ostream",
         "//testing/base:global_exe_path",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "//toolchain/diagnostics:diagnostic_emitter",
         "//toolchain/install:install_paths",
         "//toolchain/lex:tokenized_buffer_test_helpers",
@@ -168,7 +172,7 @@ cc_fuzz_test(
     deps = [
         ":driver",
         "//common:exe_path",
-        "//testing/base:test_raw_ostream",
+        "//common:raw_string_ostream",
         "//testing/fuzzing:libfuzzer_header",
         "//toolchain/install:install_paths",
         "@llvm-project//llvm:Support",

+ 10 - 11
toolchain/driver/clang_runner_test.cpp

@@ -13,18 +13,17 @@
 
 #include "common/check.h"
 #include "common/ostream.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Program.h"
 #include "llvm/TargetParser/Host.h"
 #include "testing/base/global_exe_path.h"
-#include "testing/base/test_raw_ostream.h"
 
 namespace Carbon {
 namespace {
 
-using ::Carbon::Testing::TestRawOstream;
 using ::testing::HasSubstr;
 using ::testing::StrEq;
 
@@ -54,7 +53,7 @@ static auto RunWithCapturedOutput(std::string& out, std::string& err,
 }
 
 TEST(ClangRunnerTest, Version) {
-  TestRawOstream test_os;
+  RawStringOstream test_os;
   const auto install_paths =
       InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
   std::string target = llvm::sys::getDefaultTargetTriple();
@@ -127,11 +126,10 @@ TEST(ClangRunnerTest, LinkCommandEcho) {
 
   const auto install_paths =
       InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
-  std::string verbose_out;
-  llvm::raw_string_ostream verbose_os(verbose_out);
+  RawStringOstream verbose_out;
   std::string target = llvm::sys::getDefaultTargetTriple();
   auto vfs = llvm::vfs::getRealFileSystem();
-  ClangRunner runner(&install_paths, target, vfs, &verbose_os);
+  ClangRunner runner(&install_paths, target, vfs, &verbose_out);
   std::string out;
   std::string err;
   EXPECT_TRUE(RunWithCapturedOutput(out, err,
@@ -141,7 +139,8 @@ TEST(ClangRunnerTest, LinkCommandEcho) {
                                                          bar_file.string()});
                                     }))
       << "Verbose output from runner:\n"
-      << verbose_out << "\n";
+      << verbose_out.TakeStr() << "\n";
+  verbose_out.clear();
 
   // Because we use `-###' above, we should just see the command that the Clang
   // driver would have run in a subprocess. This will be very architecture
@@ -161,11 +160,10 @@ TEST(ClangRunnerTest, DashC) {
 
   const auto install_paths =
       InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
-  std::string verbose_out;
-  llvm::raw_string_ostream verbose_os(verbose_out);
+  RawStringOstream verbose_out;
   std::string target = llvm::sys::getDefaultTargetTriple();
   auto vfs = llvm::vfs::getRealFileSystem();
-  ClangRunner runner(&install_paths, target, vfs, &verbose_os);
+  ClangRunner runner(&install_paths, target, vfs, &verbose_out);
   std::string out;
   std::string err;
   EXPECT_TRUE(RunWithCapturedOutput(out, err,
@@ -175,7 +173,8 @@ TEST(ClangRunnerTest, DashC) {
                                            test_output.string()});
                                     }))
       << "Verbose output from runner:\n"
-      << verbose_out << "\n";
+      << verbose_out.TakeStr() << "\n";
+  verbose_out.clear();
 
   // No output should be produced.
   EXPECT_THAT(out, StrEq(""));

+ 2 - 2
toolchain/driver/driver_fuzzer.cpp

@@ -6,10 +6,10 @@
 #include <string>
 
 #include "common/exe_path.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
-#include "testing/base/test_raw_ostream.h"
 #include "testing/fuzzing/libfuzzer.h"
 #include "toolchain/driver/driver.h"
 #include "toolchain/install/install_paths.h"
@@ -80,7 +80,7 @@ extern "C" auto LLVMFuzzerTestOneInput(const unsigned char* data, size_t size)
 
   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs =
       new llvm::vfs::InMemoryFileSystem;
-  TestRawOstream error_stream;
+  RawStringOstream error_stream;
   llvm::raw_null_ostream dest;
   Driver d(fs, install_paths, dest, error_stream);
   d.SetFuzzing();

+ 3 - 4
toolchain/driver/driver_test.cpp

@@ -11,17 +11,16 @@
 #include <fstream>
 #include <utility>
 
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "testing/base/global_exe_path.h"
-#include "testing/base/test_raw_ostream.h"
 #include "toolchain/testing/yaml_test_helpers.h"
 
 namespace Carbon {
 namespace {
 
-using ::Carbon::Testing::TestRawOstream;
 using ::testing::_;
 using ::testing::ContainsRegex;
 using ::testing::HasSubstr;
@@ -94,8 +93,8 @@ class DriverTest : public testing::Test {
   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs_ =
       new llvm::vfs::InMemoryFileSystem;
   const InstallPaths installation_;
-  TestRawOstream test_output_stream_;
-  TestRawOstream test_error_stream_;
+  RawStringOstream test_output_stream_;
+  RawStringOstream test_error_stream_;
 
   // Some tests work directly with files in the test temporary directory.
   std::filesystem::path test_tmpdir_;

+ 4 - 4
toolchain/driver/format_subcommand.cpp

@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "common/raw_string_ostream.h"
 #include "toolchain/base/shared_value_stores.h"
 #include "toolchain/diagnostics/diagnostic_consumer.h"
 #include "toolchain/format/format.h"
@@ -83,15 +84,14 @@ auto FormatSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
     SharedValueStores value_stores;
     auto tokens = Lex::Lex(value_stores, *source, consumer);
 
-    std::string buffer_str;
-    llvm::raw_string_ostream buffer(buffer_str);
-
+    RawStringOstream buffer;
     if (Format::Format(tokens, buffer)) {
       // TODO: Figure out a multi-file output setup that supports good
       // multi-file testing.
       // TODO: Use --output values (and default to overwrite).
-      driver_env.output_stream << buffer_str;
+      driver_env.output_stream << buffer.TakeStr();
     } else {
+      buffer.clear();
       mark_per_file_error();
       driver_env.output_stream << source->text();
     }

+ 1 - 0
toolchain/language_server/BUILD

@@ -16,6 +16,7 @@ cc_library(
         ":outgoing_messages",
         "//common:error",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "@llvm-project//clang-tools-extra/clangd:ClangDaemon",
     ],
 )

+ 3 - 3
toolchain/language_server/language_server.cpp

@@ -6,6 +6,7 @@
 
 #include "clang-tools-extra/clangd/LSPBinder.h"
 #include "clang-tools-extra/clangd/Transport.h"
+#include "common/raw_string_ostream.h"
 #include "toolchain/language_server/context.h"
 #include "toolchain/language_server/incoming_messages.h"
 #include "toolchain/language_server/outgoing_messages.h"
@@ -26,10 +27,9 @@ auto Run(std::FILE* input_stream, llvm::raw_ostream& output_stream)
   // Run the server loop.
   llvm::Error err = transport->loop(incoming);
   if (err) {
-    std::string str;
-    llvm::raw_string_ostream out(str);
+    RawStringOstream out;
     out << err;
-    return Error(str);
+    return Error(out.TakeStr());
   } else {
     return Success();
   }

+ 2 - 1
toolchain/lex/BUILD

@@ -266,8 +266,8 @@ cc_test(
         ":token_kind",
         ":tokenized_buffer",
         ":tokenized_buffer_test_helpers",
+        "//common:raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "//toolchain/base:shared_value_stores",
         "//toolchain/diagnostics:diagnostic_emitter",
         "//toolchain/diagnostics:mocks",
@@ -303,6 +303,7 @@ cc_binary(
         ":token_kind",
         ":tokenized_buffer",
         "//common:check",
+        "//common:raw_string_ostream",
         "//testing/base:benchmark_main",
         "//testing/base:source_gen_lib",
         "//toolchain/base:shared_value_stores",

+ 13 - 11
toolchain/lex/tokenized_buffer_benchmark.cpp

@@ -9,6 +9,7 @@
 
 #include "absl/random/random.h"
 #include "common/check.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/StringExtras.h"
 #include "testing/base/source_gen.h"
@@ -173,13 +174,14 @@ auto RandomSource(RandomSourceOptions options) -> std::string {
   // First place tokens onto each line.
   for (auto i : llvm::seq(NumTokens / options.tokens_per_line)) {
     lines.push_back("");
-    llvm::raw_string_ostream os(lines.back());
+    RawStringOstream os;
     // Arbitrarily indent each line by two spaces.
     os << "  ";
     llvm::ListSeparator sep(" ");
     for (int j : llvm::seq(options.tokens_per_line)) {
       os << sep << tokens[i * options.tokens_per_line + j];
     }
+    lines.push_back(os.TakeStr());
   }
 
   // Next, synthesize blank and comment lines with the correct distribution.
@@ -217,14 +219,14 @@ class LexerBenchHelper {
   }
 
   auto DiagnoseErrors() -> std::string {
-    std::string result;
-    llvm::raw_string_ostream out(result);
-    StreamDiagnosticConsumer consumer(out, /*include_diagnostic_kind=*/false);
+    RawStringOstream result;
+    StreamDiagnosticConsumer consumer(result,
+                                      /*include_diagnostic_kind=*/false);
     auto buffer = Lex::Lex(value_stores_, source_, consumer);
     consumer.Flush();
     CARBON_CHECK(buffer.has_errors(),
                  "Asked to diagnose errors but none found!");
-    return result;
+    return result.TakeStr();
   }
 
   auto source_text() -> llvm::StringRef { return source_.text(); }
@@ -419,8 +421,7 @@ void BM_GroupingSymbols(benchmark::State& state) {
   // are also active and have some reasonable icache pressure.
   llvm::SmallVector<llvm::StringRef> ids =
       Testing::SourceGen::Global().GetShuffledIdentifiers(NumTokens);
-  std::string source;
-  llvm::raw_string_ostream os(source);
+  RawStringOstream os;
   int num_tokens_per_nest =
       curly_brace_depth * 2 + paren_depth * 2 + square_bracket_depth * 2 + 2;
   int num_nests = NumTokens / num_tokens_per_nest;
@@ -449,7 +450,8 @@ void BM_GroupingSymbols(benchmark::State& state) {
     os << ids[(i * 2 + 1) % NumTokens] << "\n";
   }
 
-  LexerBenchHelper helper(os.str());
+  std::string source = os.TakeStr();
+  LexerBenchHelper helper(source);
   for (auto _ : state) {
     TokenizedBuffer buffer = helper.Lex();
 
@@ -524,15 +526,15 @@ void BM_CommentLines(benchmark::State& state) {
   int num_comment_lines = state.range(0);
   int comment_length = state.range(1);
   int comment_indent = state.range(2);
-  std::string separator;
-  llvm::raw_string_ostream os(separator);
+  RawStringOstream os;
   os << "\n";
   for (int i : llvm::seq(num_comment_lines)) {
     static_cast<void>(i);
     os << std::string(comment_indent, ' ') << "//"
        << std::string(comment_length, ' ') << "\n";
   }
-  std::string source = RandomIdentifierSeq(3, 5, /*uniform=*/true, separator);
+  std::string source =
+      RandomIdentifierSeq(3, 5, /*uniform=*/true, os.TakeStr());
 
   LexerBenchHelper helper(source);
   for (auto _ : state) {

+ 17 - 17
toolchain/lex/tokenized_buffer_test.cpp

@@ -11,9 +11,9 @@
 #include <forward_list>
 #include <iterator>
 
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/FormatVariadic.h"
-#include "testing/base/test_raw_ostream.h"
 #include "toolchain/base/shared_value_stores.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/diagnostics/mocks.h"
@@ -27,7 +27,6 @@ namespace {
 
 using ::Carbon::Testing::ExpectedToken;
 using ::Carbon::Testing::IsSingleDiagnostic;
-using ::Carbon::Testing::TestRawOstream;
 using ::testing::_;
 using ::testing::ElementsAre;
 using ::testing::Eq;
@@ -1168,10 +1167,9 @@ TEST_F(LexerTest, DiagnosticFileTooLarge) {
   compile_helper_.GetTokenizedBuffer(input, &consumer);
 }
 
-// Appends comment lines to the string, to create a comment block.
-static auto AppendCommentLines(std::string& str, int count, llvm::StringRef tag)
-    -> void {
-  llvm::raw_string_ostream out(str);
+// Outputs comments to the stream to create a comment block.
+static auto AppendCommentLines(RawStringOstream& out, int count,
+                               llvm::StringRef tag) -> void {
   for (int i : llvm::seq(count)) {
     out << "// " << tag << i << "\n";
   }
@@ -1179,13 +1177,15 @@ static auto AppendCommentLines(std::string& str, int count, llvm::StringRef tag)
 
 TEST_F(LexerTest, CommentBlock) {
   for (int comments_before = 0; comments_before < 5; ++comments_before) {
-    std::string prefix;
+    RawStringOstream prefix;
     AppendCommentLines(prefix, comments_before, "B");
+    std::string prefix_out = prefix.TakeStr();
 
     for (int comments_after = 1; comments_after < 5; ++comments_after) {
-      std::string source = prefix;
+      RawStringOstream source;
+      source << prefix_out;
       if (comments_before > 0) {
-        source += "//\n";
+        source << "//\n";
       }
       AppendCommentLines(source, comments_after, "C");
 
@@ -1193,7 +1193,7 @@ TEST_F(LexerTest, CommentBlock) {
           "{0} comment lines before the empty comment line, {1} after",
           comments_before, comments_after));
 
-      auto& buffer = compile_helper_.GetTokenizedBuffer(source);
+      auto& buffer = compile_helper_.GetTokenizedBuffer(source.TakeStr());
       ASSERT_FALSE(buffer.has_errors());
 
       EXPECT_THAT(buffer.comments_size(), Eq(1));
@@ -1205,17 +1205,17 @@ TEST_F(LexerTest, IndentedComments) {
   for (int indent = 0; indent < 40; ++indent) {
     SCOPED_TRACE(llvm::formatv("Indent: {0}", indent));
 
-    std::string source;
-    llvm::raw_string_ostream source_stream(source);
-    source_stream.indent(indent);
-    source_stream << "// Comment\n";
+    RawStringOstream source;
+    source.indent(indent);
+    source << "// Comment\n";
+    std::string source_str = source.TakeStr();
 
-    auto& buffer = compile_helper_.GetTokenizedBuffer(source);
+    auto& buffer = compile_helper_.GetTokenizedBuffer(source_str);
     ASSERT_FALSE(buffer.has_errors());
     EXPECT_THAT(buffer.comments_size(), Eq(1));
 
     std::string simd_source =
-        source +
+        source_str +
         "\"Add a bunch of padding so that SIMD logic shouldn't hit EOF\"";
     auto& simd_buffer = compile_helper_.GetTokenizedBuffer(simd_source);
     ASSERT_FALSE(simd_buffer.has_errors());
@@ -1282,7 +1282,7 @@ TEST_F(LexerTest, PrintingOutputYaml) {
   auto& buffer =
       compile_helper_.GetTokenizedBuffer("\n ;\n\n\n; ;\n\n\n\n\n\n\n\n\n\n\n");
   ASSERT_FALSE(buffer.has_errors());
-  TestRawOstream print_stream;
+  RawStringOstream print_stream;
   buffer.Print(print_stream);
 
   EXPECT_THAT(

+ 1 - 0
toolchain/lower/BUILD

@@ -46,6 +46,7 @@ cc_library(
     deps = [
         "//common:check",
         "//common:map",
+        "//common:raw_string_ostream",
         "//common:vlog",
         "//toolchain/base:kind_switch",
         "//toolchain/check:sem_ir_diagnostic_converter",

+ 3 - 3
toolchain/lower/mangler.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/lower/mangler.h"
 
+#include "common/raw_string_ostream.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/sem_ir/entry_point.h"
 #include "toolchain/sem_ir/ids.h"
@@ -131,8 +132,7 @@ auto Mangler::Mangle(SemIR::FunctionId function_id,
     CARBON_CHECK(!specific_id.is_valid(), "entry point should not be generic");
     return "main";
   }
-  std::string result;
-  llvm::raw_string_ostream os(result);
+  RawStringOstream os;
   os << "_C";
 
   os << names().GetAsStringIfIdentifier(function.name_id);
@@ -151,7 +151,7 @@ auto Mangler::Mangle(SemIR::FunctionId function_id,
         llvm::HexPrintStyle::Lower, 16);
   }
 
-  return os.str();
+  return os.TakeStr();
 }
 
 }  // namespace Carbon::Lower

+ 1 - 1
toolchain/parse/BUILD

@@ -157,8 +157,8 @@ cc_test(
         ":parse",
         ":tree",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "//toolchain/base:shared_value_stores",
         "//toolchain/diagnostics:diagnostic_emitter",
         "//toolchain/diagnostics:mocks",

+ 3 - 4
toolchain/parse/tree_test.cpp

@@ -9,7 +9,7 @@
 
 #include <forward_list>
 
-#include "testing/base/test_raw_ostream.h"
+#include "common/raw_string_ostream.h"
 #include "toolchain/base/shared_value_stores.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/diagnostics/mocks.h"
@@ -23,7 +23,6 @@
 namespace Carbon::Parse {
 namespace {
 
-using ::Carbon::Testing::TestRawOstream;
 using ::testing::ElementsAre;
 using ::testing::Pair;
 
@@ -90,7 +89,7 @@ TEST_F(TreeTest, PrintPostorderAsYAML) {
   auto [tokens, tree_and_subtrees] =
       compile_helper_.GetTokenizedBufferWithTreeAndSubtrees("fn F();");
   EXPECT_FALSE(tree_and_subtrees.tree().has_errors());
-  TestRawOstream print_stream;
+  RawStringOstream print_stream;
   tree_and_subtrees.tree().Print(print_stream);
 
   auto file = Yaml::Sequence(ElementsAre(
@@ -119,7 +118,7 @@ TEST_F(TreeTest, PrintPreorderAsYAML) {
   auto [tokens, tree_and_subtrees] =
       compile_helper_.GetTokenizedBufferWithTreeAndSubtrees("fn F();");
   EXPECT_FALSE(tree_and_subtrees.tree().has_errors());
-  TestRawOstream print_stream;
+  RawStringOstream print_stream;
   tree_and_subtrees.PrintPreorder(print_stream);
 
   auto param_list = Yaml::Sequence(ElementsAre(Yaml::Mapping(

+ 5 - 1
toolchain/sem_ir/BUILD

@@ -55,6 +55,7 @@ cc_library(
         "//common:check",
         "//common:hashing",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//common:struct_reflection",
         "//toolchain/base:index_base",
         "//toolchain/base:int",
@@ -112,6 +113,7 @@ cc_library(
         "//common:hashing",
         "//common:map",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//common:set",
         "//toolchain/base:int",
         "//toolchain/base:kind_switch",
@@ -134,6 +136,7 @@ cc_library(
         ":file",
         ":typed_insts",
         "//common:check",
+        "//common:raw_string_ostream",
         "//toolchain/base:kind_switch",
         "@llvm-project//llvm:Support",
     ],
@@ -153,6 +156,7 @@ cc_library(
         ":file",
         ":typed_insts",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//toolchain/base:kind_switch",
         "//toolchain/base:shared_value_stores",
         "//toolchain/base:value_ids",
@@ -232,9 +236,9 @@ cc_test(
     srcs = ["yaml_test.cpp"],
     deps = [
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//testing/base:global_exe_path",
         "//testing/base:gtest_main",
-        "//testing/base:test_raw_ostream",
         "//toolchain/driver",
         "//toolchain/testing:yaml_test_helpers",
         "@googletest//:gtest",

+ 3 - 3
toolchain/sem_ir/inst.h

@@ -11,6 +11,7 @@
 #include "common/check.h"
 #include "common/hashing.h"
 #include "common/ostream.h"
+#include "common/raw_string_ostream.h"
 #include "common/struct_reflection.h"
 #include "toolchain/base/index_base.h"
 #include "toolchain/base/int.h"
@@ -88,15 +89,14 @@ struct InstLikeTypeInfo<InstCat> : InstLikeTypeInfoBase<InstCat> {
   }
   // A name that can be streamed to an llvm::raw_ostream.
   static auto DebugName() -> std::string {
-    std::string str;
-    llvm::raw_string_ostream out(str);
+    RawStringOstream out;
     out << "{";
     llvm::ListSeparator sep;
     for (auto kind : InstCat::Kinds) {
       out << sep << kind;
     }
     out << "}";
-    return out.str();
+    return out.TakeStr();
   }
 };
 

+ 19 - 25
toolchain/sem_ir/inst_namer.cpp

@@ -5,6 +5,7 @@
 #include "toolchain/sem_ir/inst_namer.h"
 
 #include "common/ostream.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StableHashing.h"
@@ -152,18 +153,17 @@ auto InstNamer::GetNameFor(ScopeId scope_id, InstId inst_id) const
   const auto& [inst_scope, inst_name] = insts_[inst_id.index];
   if (!inst_name) {
     // This should not happen in valid IR.
-    std::string str;
-    llvm::raw_string_ostream str_stream(str);
-    str_stream << "<unexpected>." << inst_id;
+    RawStringOstream out;
+    out << "<unexpected>." << inst_id;
     auto loc_id = sem_ir_->insts().GetLocId(inst_id);
     // TODO: Consider handling inst_id cases.
     if (loc_id.is_node_id()) {
       const auto& tree = sem_ir_->parse_tree();
       auto token = tree.node_token(loc_id.node_id());
-      str_stream << ".loc" << tree.tokens().GetLineNumber(token) << "_"
-                 << tree.tokens().GetColumnNumber(token);
+      out << ".loc" << tree.tokens().GetLineNumber(token) << "_"
+          << tree.tokens().GetColumnNumber(token);
     }
-    return str;
+    return out.TakeStr();
   }
   if (inst_scope == scope_id) {
     return ("%" + inst_name.str()).str();
@@ -190,10 +190,9 @@ auto InstNamer::GetLabelFor(ScopeId scope_id, InstBlockId block_id) const
   const auto& [label_scope, label_name] = labels_[block_id.index];
   if (!label_name) {
     // This should not happen in valid IR.
-    std::string str;
-    llvm::raw_string_ostream(str)
-        << "<unexpected instblockref " << block_id << ">";
-    return str;
+    RawStringOstream out;
+    out << "<unexpected instblockref " << block_id << ">";
+    return out.TakeStr();
   }
   if (label_scope == scope_id) {
     return ("!" + label_name.str()).str();
@@ -443,8 +442,7 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id,
     auto add_int_or_float_type_name = [&](char type_literal_prefix,
                                           SemIR::InstId bit_width_id,
                                           llvm::StringRef suffix = "") {
-      std::string name;
-      llvm::raw_string_ostream out(name);
+      RawStringOstream out;
       out << type_literal_prefix;
       if (auto bit_width = sem_ir_->insts().TryGetAs<IntValue>(bit_width_id)) {
         out << sem_ir_->ints().Get(bit_width->int_id);
@@ -452,7 +450,7 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id,
         out << "N";
       }
       out << suffix;
-      add_inst_name(std::move(name));
+      add_inst_name(out.TakeStr());
     };
     auto facet_access_name_id = [&](InstId facet_value_inst_id) -> NameId {
       if (auto name = sem_ir_->insts().TryGetAs<NameRef>(facet_value_inst_id)) {
@@ -483,10 +481,9 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id,
         continue;
       }
       case CARBON_KIND(AssociatedEntity inst): {
-        std::string name;
-        llvm::raw_string_ostream out(name);
+        RawStringOstream out;
         out << "assoc" << inst.index.index;
-        add_inst_name(std::move(name));
+        add_inst_name(out.TakeStr());
         continue;
       }
       case CARBON_KIND(AssociatedEntityType inst): {
@@ -679,10 +676,9 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id,
       }
       case CARBON_KIND(ImplWitnessAccess inst): {
         // TODO: Include information about the impl?
-        std::string name;
-        llvm::raw_string_ostream out(name);
+        RawStringOstream out;
         out << "impl.elem" << inst.index.index;
-        add_inst_name(std::move(name));
+        add_inst_name(out.TakeStr());
         continue;
       }
       case CARBON_KIND(ImportDecl inst): {
@@ -724,10 +720,9 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id,
         continue;
       }
       case CARBON_KIND(IntValue inst): {
-        std::string name;
-        llvm::raw_string_ostream out(name);
+        RawStringOstream out;
         out << "int_" << sem_ir_->ints().Get(inst.int_id);
-        add_inst_name(std::move(name));
+        add_inst_name(out.TakeStr());
         continue;
       }
       case CARBON_KIND(NameBindingDecl inst): {
@@ -840,10 +835,9 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id,
         continue;
       }
       case CARBON_KIND(TupleAccess inst): {
-        std::string name;
-        llvm::raw_string_ostream out(name);
+        RawStringOstream out;
         out << "tuple.elem" << inst.index.index;
-        add_inst_name(std::move(name));
+        add_inst_name(out.TakeStr());
         continue;
       }
       case CARBON_KIND(TupleType inst): {

+ 3 - 3
toolchain/sem_ir/stringify_type.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/sem_ir/stringify_type.h"
 
+#include "common/raw_string_ostream.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/sem_ir/entity_with_params_base.h"
 #include "toolchain/sem_ir/ids.h"
@@ -147,8 +148,7 @@ class StepStack {
 
 auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id)
     -> std::string {
-  std::string str;
-  llvm::raw_string_ostream out(str);
+  RawStringOstream out;
 
   // Note: Since this is a stack, work is resolved in the reverse order from the
   // order pushed.
@@ -617,7 +617,7 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id)
     }
   }
 
-  return str;
+  return out.TakeStr();
 }
 
 }  // namespace Carbon::SemIR

+ 3 - 3
toolchain/sem_ir/type_info.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/sem_ir/type_info.h"
 
+#include "common/raw_string_ostream.h"
 #include "toolchain/sem_ir/file.h"
 
 namespace Carbon::SemIR {
@@ -121,10 +122,9 @@ auto NumericTypeLiteralInfo::PrintLiteral(const File& file,
 
 auto NumericTypeLiteralInfo::GetLiteralAsString(const File& file) const
     -> std::string {
-  std::string result;
-  llvm::raw_string_ostream out(result);
+  RawStringOstream out;
   PrintLiteral(file, out);
-  return result;
+  return out.TakeStr();
 }
 
 }  // namespace Carbon::SemIR

+ 2 - 3
toolchain/sem_ir/yaml_test.cpp

@@ -6,17 +6,16 @@
 #include <gtest/gtest.h>
 
 #include "common/ostream.h"
+#include "common/raw_string_ostream.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/VirtualFileSystem.h"
 #include "testing/base/global_exe_path.h"
-#include "testing/base/test_raw_ostream.h"
 #include "toolchain/driver/driver.h"
 #include "toolchain/testing/yaml_test_helpers.h"
 
 namespace Carbon::SemIR {
 namespace {
 
-using ::Carbon::Testing::TestRawOstream;
 using ::testing::_;
 using ::testing::AllOf;
 using ::testing::Contains;
@@ -38,7 +37,7 @@ TEST(SemIRTest, YAML) {
       llvm::MemoryBuffer::getMemBuffer("fn F() { var x: () = (); return; }")));
   const auto install_paths =
       InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
-  TestRawOstream print_stream;
+  RawStringOstream print_stream;
   Driver d(fs, &install_paths, print_stream, llvm::errs());
   auto run_result =
       d.RunCommand({"compile", "--no-prelude-import", "--phase=check",

+ 1 - 0
toolchain/testing/BUILD

@@ -73,6 +73,7 @@ cc_library(
     deps = [
         "//common:error",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "@abseil-cpp//absl/strings",
         "@googletest//:gtest",
         "@llvm-project//llvm:Support",

+ 3 - 2
toolchain/testing/yaml_test_helpers.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/testing/yaml_test_helpers.h"
 
+#include "common/raw_string_ostream.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/YAMLParser.h"
 
@@ -63,9 +64,9 @@ auto Value::FromText(llvm::StringRef text) -> ErrorOr<SequenceValue> {
   sm.setDiagHandler(
       [](const llvm::SMDiagnostic& diag, void* context) -> void {
         auto* error_message = static_cast<std::optional<std::string>*>(context);
-        *error_message = std::string();
-        llvm::raw_string_ostream stream(**error_message);
+        RawStringOstream stream;
         diag.print(/*ProgName=*/nullptr, stream, /*ShowColors=*/false);
+        *error_message = stream.TakeStr();
       },
       &error_message);
   llvm::yaml::Stream yaml_stream(text, sm);