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>
This commit is contained in:
Jon Ross-Perkins
2025-01-18 01:11:44 +00:00
committed by GitHub
co-authored by Geoff Romer
parent ef6e035e7d
commit 4c4c4a4d2c
47 changed files with 318 additions and 275 deletions
+1 -22
View File
@@ -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
View File
@@ -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,
-42
View File
@@ -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_
-63
View File
@@ -1,63 +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
#include "testing/base/test_raw_ostream.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace Carbon::Testing {
namespace {
using ::testing::HasSubstr;
using ::testing::StrEq;
TEST(TestRawOstreamTest, Basics) {
TestRawOstream os;
os << "test 1";
EXPECT_THAT(os.TakeStr(), StrEq("test 1"));
os << "test"
<< " "
<< "2";
EXPECT_THAT(os.TakeStr(), StrEq("test 2"));
os << "test";
os << " ";
os << "3";
EXPECT_THAT(os.TakeStr(), StrEq("test 3"));
}
TEST(TestRawOstreamTest, MultipleStreams) {
TestRawOstream os1;
TestRawOstream os2;
os1 << "test ";
os2 << "test stream 2";
os1 << "stream 1";
EXPECT_THAT(os1.TakeStr(), StrEq("test stream 1"));
EXPECT_THAT(os2.TakeStr(), StrEq("test stream 2"));
}
TEST(TestRawOstreamTest, MultipleLines) {
TestRawOstream os;
os << "test line 1\n";
os << "test line 2\n";
os << "test line 3\n";
EXPECT_THAT(os.TakeStr(), StrEq("test line 1\ntest line 2\ntest line 3\n"));
}
TEST(TestRawOstreamTest, Substring) {
TestRawOstream os;
os << "test line 1\n";
os << "test line 2\n";
os << "test line 3\n";
EXPECT_THAT(os.TakeStr(), HasSubstr("test line 2"));
}
} // namespace
} // namespace Carbon::Testing