diff --git a/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/include/clang/Frontend/ASTUnit.h @@ -369,7 +369,8 @@ bool Parse(std::shared_ptr PCHContainerOps, std::unique_ptr OverrideMainBuffer, - IntrusiveRefCntPtr VFS); + IntrusiveRefCntPtr VFS, + bool KeepSourceMgr = false); std::unique_ptr getMainBufferWithPrecompiledPreamble( std::shared_ptr PCHContainerOps, @@ -848,16 +849,23 @@ /// this(i.e. be an overlay over RealFileSystem). /// FileMgr->getVirtualFileSystem() will be used if \p VFS is nullptr. /// + /// \param KeepSourceMgr - Keep the current SourceMgr object instead of + /// creating a new one. This is useful if you replaced the file buffers with + /// new code and want to reparse the source with this buffer. + /// /// \returns True if a failure occurred that causes the ASTUnit not to /// contain any translation-unit information, false otherwise. bool Reparse(std::shared_ptr PCHContainerOps, ArrayRef RemappedFiles = None, - IntrusiveRefCntPtr VFS = nullptr); + IntrusiveRefCntPtr VFS = nullptr, + bool KeepSourceMgr = false); /// Free data that will be re-generated on the next parse. /// /// Preamble-related data is not affected. - void ResetForParse(); + /// + /// \param KeepSourceMgr - Keep the SourceManager object of this class. + void ResetForParse(bool KeepSourceMgr = false); /// Perform code completion at the given file, line, and /// column within this translation unit. diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -1094,7 +1094,9 @@ /// contain any translation-unit information, false otherwise. bool ASTUnit::Parse(std::shared_ptr PCHContainerOps, std::unique_ptr OverrideMainBuffer, - IntrusiveRefCntPtr VFS) { + IntrusiveRefCntPtr VFS, + bool KeepSourceMgr) { + if (!Invocation) return true; @@ -1130,7 +1132,8 @@ // Ensure that Clang has a FileManager with the right VFS, which may have // changed above in AddImplicitPreamble. If VFS is nullptr, rely on // createFileManager to create one. - if (VFS && FileMgr && &FileMgr->getVirtualFileSystem() == VFS) + if (FileMgr && + (KeepSourceMgr || (VFS && &FileMgr->getVirtualFileSystem() == VFS))) Clang->setFileManager(&*FileMgr); else FileMgr = Clang->createFileManager(std::move(VFS)); @@ -1164,10 +1167,12 @@ LangOpts = Clang->getInvocation().LangOpts; FileSystemOpts = Clang->getFileSystemOpts(); - ResetForParse(); + ResetForParse(KeepSourceMgr); + + if (!KeepSourceMgr && !SourceMgr) + SourceMgr = + new SourceManager(getDiagnostics(), *FileMgr, UserFilesAreVolatile); - SourceMgr = new SourceManager(getDiagnostics(), *FileMgr, - UserFilesAreVolatile); if (!OverrideMainBuffer) { checkAndRemoveNonDriverDiags(StoredDiagnostics); TopLevelDeclsInPreamble.clear(); @@ -1807,7 +1812,8 @@ bool ASTUnit::Reparse(std::shared_ptr PCHContainerOps, ArrayRef RemappedFiles, - IntrusiveRefCntPtr VFS) { + IntrusiveRefCntPtr VFS, + bool KeepSourceMgr) { if (!Invocation) return true; @@ -1835,20 +1841,22 @@ // If we have a preamble file lying around, or if we might try to // build a precompiled preamble, do so now. std::unique_ptr OverrideMainBuffer; - if (Preamble || PreambleRebuildCountdown > 0) + if (!KeepSourceMgr && (Preamble || PreambleRebuildCountdown > 0)) OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); // Clear out the diagnostics state. - FileMgr.reset(); + if (!KeepSourceMgr) + FileMgr.reset(); + getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); if (OverrideMainBuffer) getDiagnostics().setNumWarnings(NumWarningsInPreamble); // Parse the sources - bool Result = - Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), VFS); + bool Result = Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), + VFS, KeepSourceMgr); // If we're caching global code-completion results, and the top-level // declarations have changed, clear out the code-completion cache. @@ -1863,10 +1871,12 @@ return Result; } -void ASTUnit::ResetForParse() { +void ASTUnit::ResetForParse(bool KeepSourceMgr) { SavedMainFileBuffer.reset(); - SourceMgr.reset(); + if (!KeepSourceMgr) + SourceMgr.reset(); + TheSema.reset(); Ctx.reset(); PP.reset(); diff --git a/clang/unittests/Frontend/ASTUnitTest.cpp b/clang/unittests/Frontend/ASTUnitTest.cpp --- a/clang/unittests/Frontend/ASTUnitTest.cpp +++ b/clang/unittests/Frontend/ASTUnitTest.cpp @@ -178,4 +178,43 @@ ASSERT_NE(ErrUnit->stored_diag_size(), 0U); } +TEST_F(ASTUnitTest, ReparseWithOldSourceMgr) { + llvm::IntrusiveRefCntPtr InMemoryFs = + new llvm::vfs::InMemoryFileSystem(); + InMemoryFs->addFile("test.cpp", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( + void f() {} + )cpp")); + + const char *Args[] = {"clang", "test.cpp"}; + Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); + CreateInvocationOptions CIOpts; + CIOpts.Diags = Diags; + CInvok = createInvocation(Args, std::move(CIOpts)); + ASSERT_TRUE(CInvok); + + FileManager *FileMgr = new FileManager(FileSystemOptions(), InMemoryFs); + PCHContainerOps = std::make_shared(); + + auto AU = ASTUnit::LoadFromCompilerInvocation( + CInvok, PCHContainerOps, Diags, FileMgr, false, CaptureDiagsKind::None, 1, + TU_Complete, false, false, false); + + ASSERT_TRUE(AU); + + const FileEntry *FEntry = AU->getSourceManager().getFileEntryForID( + AU->getSourceManager().getMainFileID()); + std::string NewCode = "void g() {}"; + + AU->getSourceManager().overrideFileContents( + FEntry, llvm::MemoryBuffer::getMemBuffer(NewCode)); + + AU->Reparse(std::make_shared(), None, nullptr, + /*KeepFileMgr=*/true); + + auto FileMemBuf = AU->getSourceManager().getMemoryBufferForFileOrFake(FEntry); + + // Verify that we still get the same memory buffer after Reparse. + EXPECT_TRUE(FileMemBuf.getBuffer() == NewCode); +} + } // anonymous namespace