Extend the source manager with the ability to override the contents of
files with the contents of an arbitrary memory buffer. Use this new
functionality to drastically clean up the way in which we handle file
truncation for code-completion: all of the truncation/completion logic
is now encapsulated in the preprocessor where it belongs
(<rdar://problem/7434737>).



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90300 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h
index 6cf92e6..3b170a6 100644
--- a/include/clang/Basic/SourceManager.h
+++ b/include/clang/Basic/SourceManager.h
@@ -54,9 +54,6 @@
     /// file.  This is owned by the ContentCache object.
     mutable const llvm::MemoryBuffer *Buffer;
 
-    /// The line and column at which we should truncate the file.
-    unsigned TruncateAtLine, TruncateAtColumn;
-    
   public:
     /// Reference to the file entry.  This reference does not own
     /// the FileEntry object.  It is possible for this to be NULL if
@@ -93,28 +90,19 @@
       Buffer = B;
     }
 
-    /// \brief Truncate this file at the given line and column.
-    ///
-    /// \param Line the line on which to truncate the current file (1-based).
-    /// \param Column the column at which to truncate the current file.
-    /// (1-based).
-    void truncateAt(unsigned Line, unsigned Column);
-    
-    /// \brief Determines whether the file was artificially truncated with
-    /// truncateAt().
-    bool isTruncated() const { return TruncateAtLine && TruncateAtColumn; }
-      
+    /// \brief Replace the existing buffer (which will be deleted)
+    /// with the given buffer.
+    void replaceBuffer(const llvm::MemoryBuffer *B);
+
     ContentCache(const FileEntry *Ent = 0)
-      : Buffer(0), TruncateAtLine(0), TruncateAtColumn(0), Entry(Ent), 
-        SourceLineCache(0), NumLines(0) {}
+      : Buffer(0), Entry(Ent), SourceLineCache(0), NumLines(0) {}
 
     ~ContentCache();
 
     /// The copy ctor does not allow copies where source object has either
     ///  a non-NULL Buffer or SourceLineCache.  Ownership of allocated memory
     ///  is not transfered, so this is a logical error.
-    ContentCache(const ContentCache &RHS) 
-      : Buffer(0), TruncateAtLine(0), TruncateAtColumn(0), SourceLineCache(0) {
+    ContentCache(const ContentCache &RHS) : Buffer(0), SourceLineCache(0) {
       Entry = RHS.Entry;
 
       assert (RHS.Buffer == 0 && RHS.SourceLineCache == 0
@@ -344,19 +332,13 @@
   mutable FileID LastRFIDForBeforeTUCheck;
   mutable bool   LastResForBeforeTUCheck;
 
-  // Keep track of the file/line/column that we should truncate.
-  const FileEntry *TruncateFile;
-  unsigned TruncateAtLine;
-  unsigned TruncateAtColumn;
-  
   // SourceManager doesn't support copy construction.
   explicit SourceManager(const SourceManager&);
   void operator=(const SourceManager&);
 public:
   SourceManager()
-    : ExternalSLocEntries(0), LineTable(0), NumLinearScans(0),
-      NumBinaryProbes(0), TruncateFile(0), TruncateAtLine(0),
-      TruncateAtColumn(0) {
+    : ExternalSLocEntries(0), LineTable(0), NumLinearScans(0), 
+      NumBinaryProbes(0) {
     clearIDTables();
   }
   ~SourceManager();
@@ -425,6 +407,21 @@
                                         unsigned PreallocatedID = 0,
                                         unsigned Offset = 0);
 
+  /// \brief Retrieve the memory buffer associated with the given file.
+  const llvm::MemoryBuffer *getMemoryBufferForFile(const FileEntry *File);
+
+  /// \brief Override the contents of the given source file by providing an
+  /// already-allocated buffer.
+  ///
+  /// \param SourceFile the source file whose contents will be override.
+  ///
+  /// \param Buffer the memory buffer whose contents will be used as the
+  /// data in the given source file.
+  ///
+  /// \returns true if an error occurred, false otherwise.
+  bool overrideFileContents(const FileEntry *SourceFile,
+                            const llvm::MemoryBuffer *Buffer);
+
   //===--------------------------------------------------------------------===//
   // FileID manipulation methods.
   //===--------------------------------------------------------------------===//
@@ -668,12 +665,6 @@
   /// \returns true if LHS source location comes before RHS, false otherwise.
   bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const;
 
-  /// \brief Truncate the given file at the specified line/column.
-  void truncateFileAt(const FileEntry *Entry, unsigned Line, unsigned Column);
-  
-  /// \brief Determine whether this file was truncated.
-  bool isTruncatedFile(FileID FID) const;
-  
   // Iterators over FileInfos.
   typedef llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*>
       ::const_iterator fileinfo_iterator;
diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h
index 52bf194..eac197a 100644
--- a/include/clang/Lex/Lexer.h
+++ b/include/clang/Lex/Lexer.h
@@ -39,7 +39,6 @@
   SourceLocation FileLoc;        // Location for start of file.
   LangOptions Features;          // Features enabled by this language (cache).
   bool Is_PragmaLexer;           // True if lexer for _Pragma handling.
-  bool IsEofCodeCompletion;      // True if EOF is treated as a code-completion.
   
   //===--------------------------------------------------------------------===//
   // Context-specific lexing flags set by the preprocessor.
@@ -180,15 +179,6 @@
     ExtendedTokenMode = Mode ? 1 : 0;
   }
 
-  /// \brief Specify that end-of-file is to be considered a code-completion
-  /// token.
-  ///
-  /// When in this mode, the end-of-file token will be immediately preceded
-  /// by a code-completion token.
-  void SetEofIsCodeCompletion(bool Val = true) {
-    IsEofCodeCompletion = Val;
-  }
-  
   const char *getBufferStart() const { return BufferStart; }
 
   /// ReadToEndOfLine - Read the rest of the current preprocessor line as an
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index c8e4a0f..9394441 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -121,6 +121,9 @@
   /// with this preprocessor.
   std::vector<CommentHandler *> CommentHandlers;
 
+  /// \brief The file that we're performing code-completion for, if any.
+  const FileEntry *CodeCompletionFile;
+
   /// CurLexer - This is the current top of the stack that we're lexing from if
   /// not expanding a macro and we are lexing directly from source code.
   ///  Only one of CurLexer, CurPTHLexer, or CurTokenLexer will be non-null.
@@ -485,6 +488,27 @@
       CachedTokens[CachedLexPos-1] = Tok;
   }
 
+  /// \brief Specify the point at which code-completion will be performed.
+  ///
+  /// \param File the file in which code completion should occur. If
+  /// this file is included multiple times, code-completion will
+  /// perform completion the first time it is included. If NULL, this
+  /// function clears out the code-completion point.
+  ///
+  /// \param Line the line at which code completion should occur
+  /// (1-based).
+  ///
+  /// \param Column the column at which code completion should occur
+  /// (1-based).
+  ///
+  /// \returns true if an error occurred, false otherwise.
+  bool SetCodeCompletionPoint(const FileEntry *File, 
+                              unsigned Line, unsigned Column);
+
+  /// \brief Determine if this source location refers into the file
+  /// for which we are performing code completion.
+  bool isCodeCompletionFile(SourceLocation FileLoc);
+
   /// Diag - Forwarding function for diagnostics.  This emits a diagnostic at
   /// the specified Token's location, translating the token's start
   /// position in the current buffer into a SourcePosition object for rendering.
diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp
index c27675f..8cc7a84 100644
--- a/lib/Basic/SourceManager.cpp
+++ b/lib/Basic/SourceManager.cpp
@@ -41,66 +41,29 @@
 /// getSize - Returns the size of the content encapsulated by this ContentCache.
 ///  This can be the size of the source file or the size of an arbitrary
 ///  scratch buffer.  If the ContentCache encapsulates a source file, that
-///  file is not lazily brought in from disk to satisfy this query unless it
-///  needs to be truncated due to a truncateAt() call.
+///  file is not lazily brought in from disk to satisfy this query.
 unsigned ContentCache::getSize() const {
   return Buffer ? Buffer->getBufferSize() : Entry->getSize();
 }
 
+void ContentCache::replaceBuffer(const llvm::MemoryBuffer *B) {
+  if (B == Buffer)
+    return;
+  
+  delete Buffer;
+  Buffer = B;
+}
+
 const llvm::MemoryBuffer *ContentCache::getBuffer(std::string *ErrorStr) const {
   // Lazily create the Buffer for ContentCaches that wrap files.
   if (!Buffer && Entry) {
     // FIXME: Should we support a way to not have to do this check over
     //   and over if we cannot open the file?
     Buffer = MemoryBuffer::getFile(Entry->getName(), ErrorStr,Entry->getSize());
-    if (isTruncated())
-      const_cast<ContentCache *>(this)->truncateAt(TruncateAtLine, 
-                                                   TruncateAtColumn);
   }
   return Buffer;
 }
 
-void ContentCache::truncateAt(unsigned Line, unsigned Column) {
-  TruncateAtLine = Line;
-  TruncateAtColumn = Column;
-  
-  if (!isTruncated() || !Buffer)
-    return;
-  
-  // Find the byte position of the truncation point.
-  const char *Position = Buffer->getBufferStart();
-  for (unsigned Line = 1; Line < TruncateAtLine; ++Line) {
-    for (; *Position; ++Position) {
-      if (*Position != '\r' && *Position != '\n')
-        continue;
-      
-      // Eat \r\n or \n\r as a single line.
-      if ((Position[1] == '\r' || Position[1] == '\n') &&
-          Position[0] != Position[1])
-        ++Position;
-      ++Position;
-      break;
-    }
-  }
-  
-  for (unsigned Column = 1; Column < TruncateAtColumn; ++Column, ++Position) {
-    if (!*Position)
-      break;
-    
-    if (*Position == '\t')
-      Column += 7;
-  }
-  
-  // Truncate the buffer.
-  if (Position != Buffer->getBufferEnd()) {
-    MemoryBuffer *TruncatedBuffer 
-      = MemoryBuffer::getMemBufferCopy(Buffer->getBufferStart(), Position, 
-                                       Buffer->getBufferIdentifier());
-    delete Buffer;
-    Buffer = TruncatedBuffer;
-  }
-}
-
 unsigned LineTableInfo::getLineTableFilenameID(const char *Ptr, unsigned Len) {
   // Look up the filename in the string table, returning the pre-existing value
   // if it exists.
@@ -332,16 +295,6 @@
   EntryAlign = std::max(8U, EntryAlign);
   Entry = ContentCacheAlloc.Allocate<ContentCache>(1, EntryAlign);
   new (Entry) ContentCache(FileEnt);
-  
-  if (FileEnt == TruncateFile) {
-    // If we had queued up a file truncation request, perform the truncation
-    // now.
-    Entry->truncateAt(TruncateAtLine, TruncateAtColumn);
-    TruncateFile = 0;
-    TruncateAtLine = 0;
-    TruncateAtColumn = 0;
-  }
-  
   return Entry;
 }
 
@@ -457,6 +410,25 @@
   return SourceLocation::getMacroLoc(NextOffset-(TokLength+1));
 }
 
+const llvm::MemoryBuffer *
+SourceManager::getMemoryBufferForFile(const FileEntry *File) {
+  const SrcMgr::ContentCache *IR = getOrCreateContentCache(File);
+  if (IR == 0)
+    return 0;
+
+  return IR->getBuffer();
+}
+
+bool SourceManager::overrideFileContents(const FileEntry *SourceFile,
+                                         const llvm::MemoryBuffer *Buffer) {
+  const SrcMgr::ContentCache *IR = getOrCreateContentCache(SourceFile);
+  if (IR == 0)
+    return true;
+
+  const_cast<SrcMgr::ContentCache *>(IR)->replaceBuffer(Buffer);
+  return false;
+}
+
 /// getBufferData - Return a pointer to the start and end of the source buffer
 /// data for the specified FileID.
 std::pair<const char*, const char*>
@@ -1124,28 +1096,6 @@
   return LastResForBeforeTUCheck = (LOffs.first < ROffs.first);
 }
 
-void SourceManager::truncateFileAt(const FileEntry *Entry, unsigned Line, 
-                                   unsigned Column) {
-  llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*>::iterator FI
-     = FileInfos.find(Entry);
-  if (FI != FileInfos.end()) {
-    FI->second->truncateAt(Line, Column);
-    return;
-  }
-  
-  // We cannot perform the truncation until we actually see the file, so
-  // save the truncation information.
-  assert(TruncateFile == 0 && "Can't queue up multiple file truncations!");
-  TruncateFile = Entry;
-  TruncateAtLine = Line;
-  TruncateAtColumn = Column;
-}
-
-/// \brief Determine whether this file was truncated.
-bool SourceManager::isTruncatedFile(FileID FID) const {
-  return getSLocEntry(FID).getFile().getContentCache()->isTruncated();
-}
-
 /// PrintStats - Print statistics to stderr.
 ///
 void SourceManager::PrintStats() const {
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index 1083d5e..2d58bee 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -287,7 +287,7 @@
   }
 
   // Truncate the named file at the given line/column.
-  PP.getSourceManager().truncateFileAt(Entry, Line, Column);
+  PP.SetCodeCompletionPoint(Entry, Line, Column);
 
   // Set up the creation routine for code-completion.
   if (UseDebugPrinter)
diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp
index 52a7a04..451e1ce 100644
--- a/lib/Lex/Lexer.cpp
+++ b/lib/Lex/Lexer.cpp
@@ -70,7 +70,6 @@
          " to simplify lexing!");
 
   Is_PragmaLexer = false;
-  IsEofCodeCompletion = false;
   
   // Start of the file is a start of line.
   IsAtStartOfLine = true;
@@ -105,10 +104,6 @@
 
   // Default to keeping comments if the preprocessor wants them.
   SetCommentRetentionState(PP.getCommentRetentionState());
-      
-  // If the input file is truncated, the EOF is a code-completion token.
-  if (PP.getSourceManager().isTruncatedFile(FID))
-    IsEofCodeCompletion = true;
 }
 
 /// Lexer constructor - Create a new raw lexer object.  This object is only
@@ -1326,24 +1321,16 @@
   // Otherwise, check if we are code-completing, then issue diagnostics for 
   // unterminated #if and missing newline.
 
-  if (IsEofCodeCompletion) {
-    bool isIntendedFile = true;
-    if (PP && FileLoc.isFileID()) {
-      SourceManager &SM = PP->getSourceManager();
-      isIntendedFile = SM.isTruncatedFile(SM.getFileID(FileLoc));
-    }
+  if (PP && PP->isCodeCompletionFile(FileLoc)) {
+    // We're at the end of the file, but we've been asked to consider the
+    // end of the file to be a code-completion token. Return the
+    // code-completion token.
+    Result.startToken();
+    FormTokenWithChars(Result, CurPtr, tok::code_completion);
     
-    if (isIntendedFile) {
-      // We're at the end of the file, but we've been asked to consider the
-      // end of the file to be a code-completion token. Return the
-      // code-completion token.
-      Result.startToken();
-      FormTokenWithChars(Result, CurPtr, tok::code_completion);
-      
-      // Only do the eof -> code_completion translation once.
-      IsEofCodeCompletion = false;
-      return true;
-    }
+    // Only do the eof -> code_completion translation once.
+    PP->SetCodeCompletionPoint(0, 0, 0);
+    return true;
   }
   
   // If we are in a #if directive, emit an error.
diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index 0669094..c757820 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -50,7 +50,8 @@
                            bool OwnsHeaders)
   : Diags(&diags), Features(opts), Target(target),FileMgr(Headers.getFileMgr()),
     SourceMgr(SM), HeaderInfo(Headers), Identifiers(opts, IILookup),
-    BuiltinInfo(Target), CurPPLexer(0), CurDirLookup(0), Callbacks(0) {
+    BuiltinInfo(Target), CodeCompletionFile(0), CurPPLexer(0), CurDirLookup(0),
+    Callbacks(0) {
   ScratchBuf = new ScratchBuffer(SourceMgr);
   CounterValue = 0; // __COUNTER__ starts at 0.
   OwnsHeaderSearch = OwnsHeaders;
@@ -188,6 +189,63 @@
              << NumFastTokenPaste << " on the fast path.\n";
 }
 
+bool Preprocessor::SetCodeCompletionPoint(const FileEntry *File, 
+                                          unsigned TruncateAtLine, 
+                                          unsigned TruncateAtColumn) {
+  using llvm::MemoryBuffer;
+
+  CodeCompletionFile = File;
+
+  // Okay to clear out the code-completion point by passing NULL.
+  if (!CodeCompletionFile)
+    return false;
+
+  // Load the actual file's contents.
+  const MemoryBuffer *Buffer = SourceMgr.getMemoryBufferForFile(File);
+  if (!Buffer)
+    return true;
+
+  // Find the byte position of the truncation point.
+  const char *Position = Buffer->getBufferStart();
+  for (unsigned Line = 1; Line < TruncateAtLine; ++Line) {
+    for (; *Position; ++Position) {
+      if (*Position != '\r' && *Position != '\n')
+        continue;
+      
+      // Eat \r\n or \n\r as a single line.
+      if ((Position[1] == '\r' || Position[1] == '\n') &&
+          Position[0] != Position[1])
+        ++Position;
+      ++Position;
+      break;
+    }
+  }
+  
+  for (unsigned Column = 1; Column < TruncateAtColumn; ++Column, ++Position) {
+    if (!*Position)
+      break;
+    
+    if (*Position == '\t')
+      Column += 7;
+  }
+  
+  // Truncate the buffer.
+  if (Position != Buffer->getBufferEnd()) {
+    MemoryBuffer *TruncatedBuffer 
+      = MemoryBuffer::getMemBufferCopy(Buffer->getBufferStart(), Position, 
+                                       Buffer->getBufferIdentifier());
+    SourceMgr.overrideFileContents(File, TruncatedBuffer);
+  }
+
+  return false;
+}
+
+bool Preprocessor::isCodeCompletionFile(SourceLocation FileLoc) {
+  return CodeCompletionFile && FileLoc.isFileID() &&
+    SourceMgr.getFileEntryForID(SourceMgr.getFileID(FileLoc))
+      == CodeCompletionFile;
+}
+
 //===----------------------------------------------------------------------===//
 // Token Spelling
 //===----------------------------------------------------------------------===//