]> granicus.if.org Git - clang/commitdiff
Extend the source manager with the ability to override the contents of
authorDouglas Gregor <dgregor@apple.com>
Wed, 2 Dec 2009 06:49:09 +0000 (06:49 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 2 Dec 2009 06:49:09 +0000 (06:49 +0000)
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

include/clang/Basic/SourceManager.h
include/clang/Lex/Lexer.h
include/clang/Lex/Preprocessor.h
lib/Basic/SourceManager.cpp
lib/Frontend/CompilerInstance.cpp
lib/Lex/Lexer.cpp
lib/Lex/Preprocessor.cpp

index 6cf92e674b19ff7c320fb74bb91d6a2cd7e93d88..3b170a6a74f9184815b6cb86d064372e393aade6 100644 (file)
@@ -54,9 +54,6 @@ namespace SrcMgr {
     /// 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 @@ namespace SrcMgr {
       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 @@ class SourceManager {
   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 @@ public:
                                         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 @@ public:
   /// \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;
index 52bf194883df3c8446bee41ae8ad2c638b70c761..eac197afd6531f0c25080c8d6a91ee3543aacf75 100644 (file)
@@ -39,7 +39,6 @@ class Lexer : public PreprocessorLexer {
   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 @@ public:
     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
index c8e4a0f2097338b6d7f7e23933414da2ab1fab32..939444167bb31f493162c947cdad17d06de5a10c 100644 (file)
@@ -121,6 +121,9 @@ class Preprocessor {
   /// 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 @@ public:
       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.
index c27675f38bc696831642e9f1fe028952faea3fb0..8cc7a8438d2171b027cf6a1aa64b7ca428a83013 100644 (file)
@@ -41,66 +41,29 @@ unsigned ContentCache::getSizeBytesMapped() const {
 /// 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 @@ SourceManager::getOrCreateContentCache(const FileEntry *FileEnt) {
   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 @@ SourceLocation SourceManager::createInstantiationLoc(SourceLocation SpellingLoc,
   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 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
   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 {
index 1083d5ef1c02957c9091e517b4954c2392d67995..2d58beead8b57f53d4d7b892ec8064ac625e6c02 100644 (file)
@@ -287,7 +287,7 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP,
   }
 
   // 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)
index 52a7a04567a6f50289dbeff46a409dd5b51a0e44..451e1cebfff697d92f380f59b133e5dafb30af7e 100644 (file)
@@ -70,7 +70,6 @@ void Lexer::InitLexer(const char *BufStart, const char *BufPtr,
          " to simplify lexing!");
 
   Is_PragmaLexer = false;
-  IsEofCodeCompletion = false;
   
   // Start of the file is a start of line.
   IsAtStartOfLine = true;
@@ -105,10 +104,6 @@ Lexer::Lexer(FileID FID, const llvm::MemoryBuffer *InputFile, Preprocessor &PP)
 
   // 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 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
   // 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.
index 066909475fe35c4c3c20f419eb0a85ba36c5463a..c75782049449dc18323ab522c539843f50f99335 100644 (file)
@@ -50,7 +50,8 @@ Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts,
                            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 @@ void Preprocessor::PrintStats() {
              << 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
 //===----------------------------------------------------------------------===//