https://github.com/Evianaive updated 
https://github.com/llvm/llvm-project/pull/182750

>From 38699993550ad9bb91f00145ec5244b82d5ff4f2 Mon Sep 17 00:00:00 2001
From: Evianaive <[email protected]>
Date: Wed, 25 Feb 2026 12:51:19 +0800
Subject: [PATCH 1/5] [clang-repl] Migrate the file content cache invalidation
 and macro cleanup logic within the Cling implementation.

---
 clang/include/clang/Basic/FileManager.h   |  9 +++++
 clang/include/clang/Basic/SourceManager.h |  4 ++-
 clang/include/clang/Lex/Preprocessor.h    |  5 +++
 clang/lib/Basic/FileManager.cpp           | 41 +++++++++++++++++++++--
 clang/lib/Basic/SourceManager.cpp         | 11 ++++++
 clang/lib/Lex/PPMacroExpansion.cpp        | 13 +++++++
 6 files changed, 79 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/FileManager.h 
b/clang/include/clang/Basic/FileManager.h
index 01ce243e1b5fc..9de7f27167e40 100644
--- a/clang/include/clang/Basic/FileManager.h
+++ b/clang/include/clang/Basic/FileManager.h
@@ -31,6 +31,7 @@
 #include <ctime>
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 
 namespace llvm {
@@ -107,6 +108,11 @@ class FileManager : public RefCountedBase<FileManager> {
   /// The canonical names of files and directories .
   llvm::DenseMap<const void *, llvm::StringRef> CanonicalNames;
 
+  std::set<const FileEntry *> FileEntriesToReread;
+
+  /// The canonical names of directories.
+  llvm::DenseMap<const DirectoryEntry *, llvm::StringRef> CanonicalDirNames;
+
   /// Storage for canonical names that we have computed.
   llvm::BumpPtrAllocator CanonicalNameStorage;
 
@@ -284,6 +290,9 @@ class FileManager : public RefCountedBase<FileManager> {
   std::error_code getNoncachedStatValue(StringRef Path,
                                         llvm::vfs::Status &Result);
 
+  /// Remove the real file \p Entry from the cache.
+  void invalidateCache(FileEntryRef Entry);
+
   /// If path is not absolute and FileSystemOptions set the working
   /// directory, the path is modified to be relative to the given
   /// working directory.
diff --git a/clang/include/clang/Basic/SourceManager.h 
b/clang/include/clang/Basic/SourceManager.h
index bc9e97863556d..334854c5ba798 100644
--- a/clang/include/clang/Basic/SourceManager.h
+++ b/clang/include/clang/Basic/SourceManager.h
@@ -256,7 +256,7 @@ class alignas(8) ContentCache {
 
   /// Set the buffer.
   void setBuffer(std::unique_ptr<llvm::MemoryBuffer> B) {
-    IsBufferInvalid = false;
+    IsBufferInvalid = !B;
     Buffer = std::move(B);
   }
 
@@ -849,6 +849,8 @@ class SourceManager : public RefCountedBase<SourceManager> {
 
   void clearIDTables();
 
+  void invalidateCache(FileID FID);
+
   /// Initialize this source manager suitably to replay the compilation
   /// described by \p Old. Requires that \p Old outlive \p *this.
   void initializeForReplay(const SourceManager &Old);
diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index b6e42a6151ac3..4b43ba810ac97 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1493,6 +1493,11 @@ class Preprocessor {
     return appendDefMacroDirective(II, MI, MI->getDefinitionLoc());
   }
 
+  /// Remove a IdentifierInfo and MacroDirective from the history.
+  /// Given an IdentifierInfo and a MacroDirective we can remove them from
+  /// the macros vector.
+  void removeMacro(IdentifierInfo *II, MacroDirective *MD);
+
   /// Set a MacroDirective that was loaded from a PCH file.
   void setLoadedMacroDirective(IdentifierInfo *II, MacroDirective *ED,
                                MacroDirective *MD);
diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp
index 76f732865d6ac..278bb243e5092 100644
--- a/clang/lib/Basic/FileManager.cpp
+++ b/clang/lib/Basic/FileManager.cpp
@@ -217,7 +217,25 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
   // See if there is already an entry in the map.
   auto SeenFileInsertResult =
       SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});
-  if (!SeenFileInsertResult.second) {
+
+  auto *NamedFileEnt = &*SeenFileInsertResult.first;
+
+  const FileEntry *StaleFileEntry = 0;
+  bool needsRereading = false;
+  if (NamedFileEnt && NamedFileEnt->getValue()) {
+    FileEntryRef::MapValue Value = *NamedFileEnt->getValue();
+    if (isa<FileEntry *>(Value.V)) {
+      auto found = FileEntriesToReread.find(cast<FileEntry *>(Value.V));
+      if (found != FileEntriesToReread.end()) {
+        needsRereading = true;
+        StaleFileEntry = *found;
+        FileEntriesToReread.erase(found);
+      }
+    }
+  }
+
+  // See if there is already an entry in the map.
+  if (!SeenFileInsertResult.second && !needsRereading) {
     if (!SeenFileInsertResult.first->second)
       return llvm::errorCodeToError(
           SeenFileInsertResult.first->second.getError());
@@ -226,8 +244,6 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
 
   // We've not seen this before. Fill it in.
   ++NumFileCacheMisses;
-  auto *NamedFileEnt = &*SeenFileInsertResult.first;
-  assert(!NamedFileEnt->second && "should be newly-created");
 
   // Get the null-terminated file name as stored as the key of the
   // SeenFileEntries map.
@@ -353,6 +369,20 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
     // We should still fill the path even if we aren't opening the file.
     fillRealPathName(UFE, InterndFileName);
   }
+
+  if (StaleFileEntry) {
+    // Find occurrences of old FileEntry; update with new one:
+    for (auto &fe : SeenFileEntries) {
+      if (fe.getValue()) {
+        FileEntryRef::MapValue Value = *fe.getValue();
+        if (isa<FileEntry *>(Value.V) &&
+            cast<FileEntry *>(Value.V) == StaleFileEntry) {
+          fe.setValue(FileEntryRef::MapValue(*UFE, DirInfo));
+        }
+      }
+    }
+  }
+
   return ReturnedRef;
 }
 
@@ -614,6 +644,11 @@ FileManager::getNoncachedStatValue(StringRef Path,
   return std::error_code();
 }
 
+void FileManager::invalidateCache(FileEntryRef Entry) {
+  assert(Entry && "Cannot invalidate a NULL FileEntry");
+  FileEntriesToReread.insert(Entry);
+}
+
 void FileManager::GetUniqueIDMapping(
     SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const {
   UIDToFiles.clear();
diff --git a/clang/lib/Basic/SourceManager.cpp 
b/clang/lib/Basic/SourceManager.cpp
index b6cc6ec9365f5..2ec21f6157b82 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -358,6 +358,17 @@ bool SourceManager::isMainFile(const FileEntry 
&SourceFile) {
   return false;
 }
 
+void SourceManager::invalidateCache(FileID FID) {
+  OptionalFileEntryRef Entry = getFileEntryRefForID(FID);
+  if (!Entry)
+    return;
+  if (ContentCache *&E = FileInfos[*Entry]) {
+    E->setBuffer(nullptr);
+    E = 0;
+  }
+  getFileManager().invalidateCache(*Entry);
+}
+
 void SourceManager::initializeForReplay(const SourceManager &Old) {
   assert(MainFileID.isInvalid() && "expected uninitialized SourceManager");
 
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp 
b/clang/lib/Lex/PPMacroExpansion.cpp
index 20b8fe585a007..77e8fca63c3fc 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -91,6 +91,19 @@ void Preprocessor::appendMacroDirective(IdentifierInfo *II, 
MacroDirective *MD){
     II->setChangedSinceDeserialization();
 }
 
+void Preprocessor::removeMacro(IdentifierInfo *II, MacroDirective *MD) {
+  assert(II && MD);
+  II->setHasMacroDefinition(false);
+  CurSubmoduleState->Macros.erase(II);
+  if (MacroDirective *prevMD = MD->getPrevious()) {
+    // Avoid assertion in appendMacroDirective.
+    MacroDirective *prevPrevMD = prevMD->getPrevious();
+    prevMD->setPrevious(0);
+    appendMacroDirective(II, prevMD);
+    prevMD->setPrevious(prevPrevMD);
+  }
+}
+
 void Preprocessor::setLoadedMacroDirective(IdentifierInfo *II,
                                            MacroDirective *ED,
                                            MacroDirective *MD) {

>From b6d684bdc619bae2aea9ee77c3896adc2c96d538 Mon Sep 17 00:00:00 2001
From: Evianaive <[email protected]>
Date: Wed, 25 Feb 2026 12:57:53 +0800
Subject: [PATCH 2/5] [clang-repl] Add PreProcessor Track to implement undo of
 include and macro

Add PreProcessor tracker. And IncludedFiles and MacroDirectiveInfos in 
PartialTranslationUnit. Wrap CleanUpPTU with proper semantics and rename the 
original function to CleanUpTU

fix move
---
 .../Interpreter/PartialTranslationUnit.h      |  35 ++++++
 clang/lib/Basic/FileManager.cpp               |   4 +-
 clang/lib/Basic/SourceManager.cpp             |   8 ++
 clang/lib/Interpreter/IncrementalParser.cpp   | 118 +++++++++++++++++-
 clang/lib/Interpreter/IncrementalParser.h     |   7 +-
 clang/lib/Interpreter/Interpreter.cpp         |   2 +-
 6 files changed, 169 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Interpreter/PartialTranslationUnit.h 
b/clang/include/clang/Interpreter/PartialTranslationUnit.h
index c878e139fe70d..18eb21e5aa281 100644
--- a/clang/include/clang/Interpreter/PartialTranslationUnit.h
+++ b/clang/include/clang/Interpreter/PartialTranslationUnit.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H
 #define LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H
 
+#include "clang/Basic/FileEntry.h"
 #include <memory>
 
 namespace llvm {
@@ -22,6 +23,8 @@ class Module;
 
 namespace clang {
 
+class IdentifierInfo;
+class MacroDirective;
 class TranslationUnitDecl;
 
 /// The class keeps track of various objects created as part of processing
@@ -34,6 +37,38 @@ struct PartialTranslationUnit {
   bool operator==(const PartialTranslationUnit &other) {
     return other.TUPart == TUPart && other.TheModule == TheModule;
   }
+
+  ///\brief Each macro pair (is this the same as for decls?)came
+  /// through different interface at
+  /// different time. We are being conservative and we want to keep all the
+  /// call sequence that originally occurred in clang.
+  ///
+  struct MacroDirectiveInfo {
+    // We need to store both the IdentifierInfo and the MacroDirective
+    // because the Preprocessor stores the macros in a DenseMap<II, MD>.
+    IdentifierInfo *II;
+    const MacroDirective *MD;
+    MacroDirectiveInfo(IdentifierInfo *II, const MacroDirective *MD)
+        : II(II), MD(MD) {}
+    inline bool operator==(const MacroDirectiveInfo &rhs) const {
+      return II == rhs.II && MD == rhs.MD;
+    }
+    inline bool operator!=(const MacroDirectiveInfo &rhs) const {
+      return !operator==(rhs);
+    }
+  };
+  // Intentionally use struct instead of pair because we don't need default
+  // init.
+  // Add macro decls to be able to revert them for error recovery.
+  typedef llvm::SmallVector<MacroDirectiveInfo, 2> MacroDirectiveInfoQueue;
+
+  ///\brief All seen macros.
+  ///
+  MacroDirectiveInfoQueue MacroDirectiveInfos;
+
+  /// Files that were #included during this PTU's parsing.
+  /// Used to reset HeaderFileInfo on Undo.
+  std::vector<FileEntryRef> IncludedFiles;
 };
 } // namespace clang
 
diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp
index 278bb243e5092..e81d85c5a0765 100644
--- a/clang/lib/Basic/FileManager.cpp
+++ b/clang/lib/Basic/FileManager.cpp
@@ -347,7 +347,9 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
   }
 
   FileEntryRef ReturnedRef(*NamedFileEnt);
-  if (ReusingEntry) { // Already have an entry with this inode, return it.
+  if (ReusingEntry &&
+      llvm::sys::toTimeT(Status.getLastModificationTime()) == UFE->ModTime) {
+    // Already have an entry with this inode, return it.
     return ReturnedRef;
   }
 
diff --git a/clang/lib/Basic/SourceManager.cpp 
b/clang/lib/Basic/SourceManager.cpp
index 2ec21f6157b82..1c1a0b665d6de 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -366,6 +366,14 @@ void SourceManager::invalidateCache(FileID FID) {
     E->setBuffer(nullptr);
     E = 0;
   }
+  if (!FID.isInvalid()) {
+    const SrcMgr::SLocEntry &SLocE = getSLocEntry(FID);
+    if (SLocE.isFile()) {
+      SrcMgr::ContentCache &CC =
+        const_cast<SrcMgr::ContentCache &>(SLocE.getFile().getContentCache());
+      CC.setBuffer(nullptr);
+    }
+  }
   getFileManager().invalidateCache(*Entry);
 }
 
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp 
b/clang/lib/Interpreter/IncrementalParser.cpp
index bf08911e23533..e08c820950adc 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/DeclContextInternals.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
+#include "clang/Lex/PPCallbacks.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Sema/Sema.h"
 #include "llvm/IR/Module.h"
@@ -28,6 +29,61 @@
 
 namespace clang {
 
+class IncrementalPreProcessorTracker : public PPCallbacks {
+  std::vector<FileEntryRef> IncludedFiles;
+  PartialTranslationUnit::MacroDirectiveInfoQueue MacroDirectives;
+  void MacroDirective(const clang::Token &MacroNameTok,
+                      const clang::MacroDirective *MD) {
+    PartialTranslationUnit::MacroDirectiveInfo MDE(
+        MacroNameTok.getIdentifierInfo(), MD);
+    MacroDirectives.push_back(MDE);
+  }
+
+public:
+  explicit IncrementalPreProcessorTracker() {}
+  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+                          StringRef FileName, bool IsAngled,
+                          CharSourceRange FilenameRange,
+                          OptionalFileEntryRef File, StringRef SearchPath,
+                          StringRef RelativePath, const Module 
*SuggestedModule,
+                          bool ModuleImported,
+                          SrcMgr::CharacteristicKind FileType) override {
+
+    if (File) {
+      IncludedFiles.push_back(*File);
+    }
+  }
+
+  /// \name PPCallbacks overrides
+  /// Macro support
+  void MacroDefined(const clang::Token &MacroNameTok,
+                    const clang::MacroDirective *MD) final {
+    MacroDirective(MacroNameTok, MD);
+  }
+
+  /// \name PPCallbacks overrides
+  /// Macro support
+  void MacroUndefined(const clang::Token &MacroNameTok,
+                      const clang::MacroDefinition & /*MD*/,
+                      const clang::MacroDirective *Undef) final {
+    if (Undef)
+      MacroDirective(MacroNameTok, Undef);
+  }
+
+  /// Move out tracked files after Parse completes
+  std::vector<FileEntryRef> takeFiles() { return std::move(IncludedFiles); }
+
+  /// Move out tracked macros after Parse completes
+  PartialTranslationUnit::MacroDirectiveInfoQueue takeMacros() {
+    return std::move(MacroDirectives);
+  }
+
+  void reset() {
+    IncludedFiles.clear();
+    MacroDirectives.clear();
+  }
+};
+
 // IncrementalParser::IncrementalParser() {}
 
 IncrementalParser::IncrementalParser(CompilerInstance &Instance,
@@ -42,6 +98,11 @@ IncrementalParser::IncrementalParser(CompilerInstance 
&Instance,
     External->StartTranslationUnit(Consumer);
 
   P->Initialize();
+
+  // track files that are included when parse to support undo
+  auto Tracker = std::make_unique<IncrementalPreProcessorTracker>();
+  PreProcessorTracker = Tracker.get();
+  S.getPreprocessor().addPPCallbacks(std::move(Tracker));
 }
 
 IncrementalParser::~IncrementalParser() { P.reset(); }
@@ -82,7 +143,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
 
   DiagnosticsEngine &Diags = S.getDiagnostics();
   if (Diags.hasErrorOccurred()) {
-    CleanUpPTU(C.getTranslationUnitDecl());
+    CleanUpTU(C.getTranslationUnitDecl());
 
     Diags.Reset(/*soft=*/true);
     Diags.getClient()->clear();
@@ -109,6 +170,9 @@ IncrementalParser::Parse(llvm::StringRef input) {
   Preprocessor &PP = S.getPreprocessor();
   assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
 
+  if (PreProcessorTracker)
+    PreProcessorTracker->reset();
+
   std::ostringstream SourceName;
   SourceName << "input_line_" << InputCount++;
 
@@ -161,7 +225,7 @@ IncrementalParser::Parse(llvm::StringRef input) {
   return PTU;
 }
 
-void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
+void IncrementalParser::CleanUpTU(TranslationUnitDecl *MostRecentTU) {
   if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) 
{
     for (auto &&[Key, List] : *Map) {
       DeclContextLookupResult R = List.getLookupResult();
@@ -194,6 +258,51 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl 
*MostRecentTU) {
   }
 }
 
+void IncrementalParser::CleanUpPTU(
+    const PartialTranslationUnit &MostRecentPTU) {
+  CleanUpTU(MostRecentPTU.TUPart);
+
+  auto &PP = P->getPreprocessor();
+  auto &HS = PP.getHeaderSearchInfo();
+  auto &SM = PP.getSourceManager();
+
+  auto &MacroDirectives = PTUs.back().MacroDirectiveInfos;
+  bool Successful = true;
+  if (!MacroDirectives.empty()) {
+    for (auto MI = MacroDirectives.rbegin(), ME = MacroDirectives.rend();
+         MI != ME; ++MI) {
+      // Get rid of the macro definition
+      auto UnloadMacro =
+          [&PP](PartialTranslationUnit::MacroDirectiveInfo MacroD) {
+            const MacroDirective *MD = MacroD.MD;
+            // Undef the definition
+            const MacroInfo *MI = MD->getMacroInfo();
+
+            // If the macro is not defined, this is a noop undef, just return.
+            if (!MI)
+              return false;
+
+            // Remove the pair from the macros
+            PP.removeMacro(MacroD.II, const_cast<MacroDirective *>(MacroD.MD));
+
+            return true;
+          };
+      Successful = UnloadMacro(*MI) && Successful;
+    }
+  }
+  if (!PTUs.back().IncludedFiles.empty()) {
+    for (FileEntryRef FE : PTUs.back().IncludedFiles) {
+      HeaderFileInfo &HFI = HS.getFileInfo(FE);
+      HFI.IsLocallyIncluded = false;
+      HFI.isPragmaOnce = false;
+      HFI.LazyControllingMacro = LazyIdentifierInfoPtr();
+      HFI.IsValid = false;
+
+      SM.invalidateCache(SM.translateFile(FE));
+    }
+  }
+}
+
 PartialTranslationUnit &
 IncrementalParser::RegisterPTU(TranslationUnitDecl *TU,
                                std::unique_ptr<llvm::Module> M /*={}*/) {
@@ -213,6 +322,11 @@ IncrementalParser::RegisterPTU(TranslationUnitDecl *TU,
     LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " ("
                             << LastPTU.TheModule->getName() << ")");
   LLVM_DEBUG(llvm::dbgs() << "]\n");
+
+  if (PreProcessorTracker) {
+    LastPTU.IncludedFiles = PreProcessorTracker->takeFiles();
+    LastPTU.MacroDirectiveInfos = PreProcessorTracker->takeMacros();
+  }
   return LastPTU;
 }
 } // end namespace clang
diff --git a/clang/lib/Interpreter/IncrementalParser.h 
b/clang/lib/Interpreter/IncrementalParser.h
index 9b042bc494efb..df508eb24a6f5 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -30,6 +30,7 @@ class Parser;
 class Sema;
 class TranslationUnitDecl;
 class IncrementalAction;
+class IncrementalPreProcessorTracker;
 struct PartialTranslationUnit;
 
 /// Provides support for incremental compilation. Keeps track of the state
@@ -54,6 +55,9 @@ class IncrementalParser {
 
   std::list<PartialTranslationUnit> &PTUs;
 
+  /// Tracks the include files during parsing
+  IncrementalPreProcessorTracker *PreProcessorTracker;
+
 public:
   IncrementalParser(CompilerInstance &Instance, IncrementalAction *Act,
                     llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs);
@@ -64,8 +68,9 @@ class IncrementalParser {
   /// \c TranslationUnitDecl.
   virtual llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input);
 
-  void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
+  void CleanUpTU(TranslationUnitDecl *MostRecentTU);
 
+  void CleanUpPTU(const PartialTranslationUnit &MostRecentPTU);
   /// Register a PTU produced by Parse.
   PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
                                       std::unique_ptr<llvm::Module> M = {});
diff --git a/clang/lib/Interpreter/Interpreter.cpp 
b/clang/lib/Interpreter/Interpreter.cpp
index 9c94cfa5ee381..32a5cbc398e95 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -592,7 +592,7 @@ llvm::Error Interpreter::Undo(unsigned N) {
         return Err;
     }
 
-    IncrParser->CleanUpPTU(PTUs.back().TUPart);
+    IncrParser->CleanUpPTU(PTUs.back());
     PTUs.pop_back();
   }
   return llvm::Error::success();

>From f33f94a8f92ccd29bb500b09ea7fdcaa6e2d4993 Mon Sep 17 00:00:00 2001
From: Evianaive <[email protected]>
Date: Sat, 28 Feb 2026 00:34:43 +0800
Subject: [PATCH 3/5] [clang-repl] Add Test for undo include and macro

---
 .../test/Interpreter/Inputs/dynamic-header.h  |  9 +++++++
 clang/test/Interpreter/code-undo.cpp          | 24 +++++++++++++++++--
 2 files changed, 31 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Interpreter/Inputs/dynamic-header.h

diff --git a/clang/test/Interpreter/Inputs/dynamic-header.h 
b/clang/test/Interpreter/Inputs/dynamic-header.h
new file mode 100644
index 0000000000000..9abc4ef5f0629
--- /dev/null
+++ b/clang/test/Interpreter/Inputs/dynamic-header.h
@@ -0,0 +1,9 @@
+#ifndef DYNAMIC_HEADER_H
+#define DYNAMIC_HEADER_H
+
+#define MACRO_FROM_HEADER 100
+inline int getDynamicValue() { 
+    return MACRO_FROM_HEADER;
+}
+
+#endif
diff --git a/clang/test/Interpreter/code-undo.cpp 
b/clang/test/Interpreter/code-undo.cpp
index 4516910ca3b4f..9b2839deabb8a 100644
--- a/clang/test/Interpreter/code-undo.cpp
+++ b/clang/test/Interpreter/code-undo.cpp
@@ -1,10 +1,10 @@
-// RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | sed 's|@TESTDIR@|%/S|g' | clang-repl -Xcc -I%S/Inputs | 
FileCheck %s
 extern "C" int printf(const char *, ...);
 int x1 = 0;
 int x2 = 42;
 %undo
 int x2 = 24;
-auto r1 = printf("x1 = %d\n", x1);
+auto r1 = printf("x1 = %d\n", x1); 
 // CHECK: x1 = 0
 auto r2 = printf("x2 = %d\n", x2);
 // CHECK-NEXT: x2 = 24
@@ -20,4 +20,24 @@ auto r4 = bar();
 %undo
 auto r5 = bar();
 
+#include <cstdio>
+
+#include "dynamic-header.h"
+auto r6 = printf("getDynamicValue() = %d\n", getDynamicValue());
+%undo
+%undo
+// CHECK-NEXT: getDynamicValue() = 100
+
+FILE *f = fopen("@TESTDIR@/Inputs/dynamic-header.h", "w");
+
+fprintf(f, "#ifndef DYNAMIC_HEADER_H\n");
+fprintf(f, "#define DYNAMIC_HEADER_H\n");
+fprintf(f, "inline int getDynamicValue() { return 200; }\n");
+fprintf(f, "#endif\n");
+fclose(f); 
+
+#include "dynamic-header.h"
+auto r8 = printf("getDynamicValue() = %d\n", getDynamicValue());
+// CHECK-NEXT: getDynamicValue() = 200
+
 %quit

>From 0d1472548b84174d379fc02948692f256ff1bf9e Mon Sep 17 00:00:00 2001
From: Evianaive <[email protected]>
Date: Sun, 1 Mar 2026 01:52:10 +0800
Subject: [PATCH 4/5] [clang-repl] Fix Test

---
 clang/lib/Basic/FileManager.cpp            | 28 +++++++++++++----
 clang/lib/Frontend/PrecompiledPreamble.cpp | 35 +++++++++++++++++++++-
 2 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp
index e81d85c5a0765..1dbcea91445dd 100644
--- a/clang/lib/Basic/FileManager.cpp
+++ b/clang/lib/Basic/FileManager.cpp
@@ -269,11 +269,16 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
   // FIXME: Use the directory info to prune this, before doing the stat 
syscall.
   // FIXME: This will reduce the # syscalls.
 
-  // Check to see if the file exists.
+  // Check to see if the file exists.  Stat without opening first; we only
+  // open the file after confirming that the entry needs to be populated (i.e.
+  // the inode is new or the modtime has changed).  This avoids calling
+  // openFileForRead() on an aliased path whose inode is already cached and
+  // up-to-date, which would waste a VFS call and produce a misleading read
+  // count in tests that instrument openFileForRead().
   std::unique_ptr<llvm::vfs::File> F;
   llvm::vfs::Status Status;
-  auto statError = getStatValue(InterndFileName, Status, true,
-                                openFile ? &F : nullptr, IsText);
+  auto statError =
+      getStatValue(InterndFileName, Status, true, nullptr, IsText);
   if (statError) {
     // There's no real file at the given path.
     if (CacheFailure)
@@ -284,7 +289,7 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
     return llvm::errorCodeToError(statError);
   }
 
-  assert((openFile || !F) && "undesired open file");
+  assert(!F && "stat-only call must not open a file");
 
   // It exists.  See if we have already opened a file with the same inode.
   // This occurs when one dir is symlinked to another, for example.
@@ -349,10 +354,23 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
   FileEntryRef ReturnedRef(*NamedFileEnt);
   if (ReusingEntry &&
       llvm::sys::toTimeT(Status.getLastModificationTime()) == UFE->ModTime) {
-    // Already have an entry with this inode, return it.
+    // Already have an entry with this inode and the modtime is unchanged;
+    // return it without opening the file.
     return ReturnedRef;
   }
 
+  // The entry needs to be (re-)populated.  Open the file now if requested.
+  if (openFile) {
+    if (auto openErr =
+            getStatValue(InterndFileName, Status, true, &F, IsText)) {
+      if (CacheFailure)
+        NamedFileEnt->second = openErr;
+      else
+        SeenFileEntries.erase(Filename);
+      return llvm::errorCodeToError(openErr);
+    }
+  }
+
   // Otherwise, we don't have this file yet, add it.
   UFE->Size = Status.getSize();
   UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
diff --git a/clang/lib/Frontend/PrecompiledPreamble.cpp 
b/clang/lib/Frontend/PrecompiledPreamble.cpp
index 9bf18b4d897ce..9844b93c18b17 100644
--- a/clang/lib/Frontend/PrecompiledPreamble.cpp
+++ b/clang/lib/Frontend/PrecompiledPreamble.cpp
@@ -544,6 +544,33 @@ llvm::ErrorOr<PrecompiledPreamble> 
PrecompiledPreamble::Build(
   // so we can verify whether they have changed or not.
   llvm::StringMap<PrecompiledPreamble::PreambleFileHash> FilesInPreamble;
 
+  // Build a map of overridden file UniqueIDs to their expected hashes,
+  // mirroring exactly what CanReuse computes from PreprocessorOpts.  This
+  // ensures that a file remapped via a buffer — which may also exist in VFS
+  // with a non-zero modtime — is recorded with a buffer-content hash rather
+  // than VFS file metadata, so the CanReuse comparison succeeds on the next
+  // reparse instead of always returning false.
+  std::map<llvm::sys::fs::UniqueID, PrecompiledPreamble::PreambleFileHash>
+      OverriddenByUID;
+  {
+    FileManager &FileMgr = Clang->getFileManager();
+    llvm::vfs::FileSystem &BVFS = FileMgr.getVirtualFileSystem();
+    const PreprocessorOptions &PPOpts = Clang->getPreprocessorOpts();
+    for (const auto &R : PPOpts.RemappedFiles) {
+      if (auto S = BVFS.status(R.second))
+        OverriddenByUID[S->getUniqueID()] =
+            PrecompiledPreamble::PreambleFileHash::createForFile(
+                S->getSize(),
+                llvm::sys::toTimeT(S->getLastModificationTime()));
+    }
+    for (const auto &RB : PPOpts.RemappedFileBuffers) {
+      if (auto MaybeFile = FileMgr.getOptionalFileRef(RB.first))
+        OverriddenByUID[MaybeFile->getUniqueID()] =
+            PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(
+                RB.second->getMemBufferRef());
+    }
+  }
+
   SourceManager &SourceMgr = Clang->getSourceManager();
   for (auto &Filename : PreambleDepCollector->getDependencies()) {
     auto MaybeFile = Clang->getFileManager().getOptionalFileRef(Filename);
@@ -551,7 +578,13 @@ llvm::ErrorOr<PrecompiledPreamble> 
PrecompiledPreamble::Build(
         MaybeFile == SourceMgr.getFileEntryRefForID(SourceMgr.getMainFileID()))
       continue;
     auto File = *MaybeFile;
-    if (time_t ModTime = File.getModificationTime()) {
+    // If this file was remapped (buffer or file override), record the same
+    // hash that CanReuse will compute from OverriddenFiles so the two sides
+    // of the comparison always use the same format.
+    auto OverriddenIt = OverriddenByUID.find(File.getUniqueID());
+    if (OverriddenIt != OverriddenByUID.end()) {
+      FilesInPreamble[File.getName()] = OverriddenIt->second;
+    } else if (time_t ModTime = File.getModificationTime()) {
       FilesInPreamble[File.getName()] =
           PrecompiledPreamble::PreambleFileHash::createForFile(File.getSize(),
                                                                ModTime);

>From 8622daa4b3c2bd9f82cc9eccdcab7b2e8e27a3b9 Mon Sep 17 00:00:00 2001
From: Evianaive <[email protected]>
Date: Sun, 1 Mar 2026 14:53:56 +0800
Subject: [PATCH 5/5] [clang-repl] check time only for need rereading file

---
 clang/lib/Basic/FileManager.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp
index 1dbcea91445dd..cfe1c5854d79b 100644
--- a/clang/lib/Basic/FileManager.cpp
+++ b/clang/lib/Basic/FileManager.cpp
@@ -353,9 +353,13 @@ llvm::Expected<FileEntryRef> 
FileManager::getFileRef(StringRef Filename,
 
   FileEntryRef ReturnedRef(*NamedFileEnt);
   if (ReusingEntry &&
-      llvm::sys::toTimeT(Status.getLastModificationTime()) == UFE->ModTime) {
-    // Already have an entry with this inode and the modtime is unchanged;
-    // return it without opening the file.
+      (!needsRereading ||
+       llvm::sys::toTimeT(Status.getLastModificationTime()) == UFE->ModTime)) {
+    // Already have an entry with this inode.  Take the early return when:
+    // - no re-read was requested (covers alias paths and virtual files whose
+    //   modtime may differ from the stat cache), or
+    // - a re-read was requested but the file's modtime is unchanged, meaning
+    //   the on-disk content is the same and re-populating would be wasteful.
     return ReturnedRef;
   }
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to