diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index e0f1ea435d54e..ec5803ff46290 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -2031,6 +2031,7 @@ class SourceManagerForFile { // The order of these fields are important - they should be in the same order // as they are created in `createSourceManagerForFile` so that they can be // deleted in the reverse order as they are created. + std::string ContentBuffer; std::unique_ptr FileMgr; std::unique_ptr Diagnostics; std::unique_ptr SourceMgr; diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp index b1f2180c1d462..4e351ec9089a9 100644 --- a/clang/lib/Basic/SourceManager.cpp +++ b/clang/lib/Basic/SourceManager.cpp @@ -2382,14 +2382,20 @@ size_t SourceManager::getDataStructureSizes() const { SourceManagerForFile::SourceManagerForFile(StringRef FileName, StringRef Content) { + // We copy to `std::string` for Context instead of StringRef because the + // SourceManager::getBufferData() works only with null-terminated buffers. + // And we still want to keep the API convenient. + ContentBuffer = Content.str(); + // This is referenced by `FileMgr` and will be released by `FileMgr` when it // is deleted. IntrusiveRefCntPtr InMemoryFileSystem( new llvm::vfs::InMemoryFileSystem); + InMemoryFileSystem->addFile( FileName, 0, - llvm::MemoryBuffer::getMemBuffer(Content, FileName, - /*RequiresNullTerminator=*/false)); + llvm::MemoryBuffer::getMemBuffer(ContentBuffer, FileName, + /*RequiresNullTerminator=*/true)); // This is passed to `SM` as reference, so the pointer has to be referenced // in `Environment` so that `FileMgr` can out-live this function scope. FileMgr = diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 9864e7ec1b2ec..af9107d0e5bf9 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -29096,6 +29096,16 @@ TEST_F(FormatTest, BreakBeforeClassName) { " ArenaSafeUniquePtr {};"); } +TEST_F(FormatTest, DoesNotCrashOnNonNullTerminatedStringRefs) { + StringRef TwoLines = "namespace foo {}\n" + "namespace bar {}"; + StringRef FirstLine = TwoLines.take_until([](char c) { return c == '\n'; }); + + // The internal API used to crash when passed a non-null-terminated StringRef. + // Check this does not happen anymore. + verifyNoCrash(FirstLine); +} + } // namespace } // namespace test } // namespace format