]> granicus.if.org Git - clang/commitdiff
Replace the -code-completion-dump option with
authorDouglas Gregor <dgregor@apple.com>
Tue, 22 Sep 2009 21:11:38 +0000 (21:11 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 22 Sep 2009 21:11:38 +0000 (21:11 +0000)
  -code-completion-at=filename:line:column

which performs code completion at the specified location by truncating
the file at that position and enabling code completion. This approach
makes it possible to run multiple tests from a single test file, and
gives a more natural command-line interface.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82571 91177308-0d34-0410-b5e6-96231b3b80d8

27 files changed:
include/clang/Basic/DiagnosticFrontendKinds.td
include/clang/Basic/SourceManager.h
include/clang/Lex/Preprocessor.h
lib/Basic/SourceManager.cpp
lib/Lex/Lexer.cpp
lib/Lex/Preprocessor.cpp
test/CodeCompletion/call.cpp
test/CodeCompletion/enum-switch-case-qualified.cpp
test/CodeCompletion/enum-switch-case.c
test/CodeCompletion/enum-switch-case.cpp
test/CodeCompletion/function-templates.cpp
test/CodeCompletion/functions.cpp
test/CodeCompletion/member-access.c
test/CodeCompletion/member-access.cpp
test/CodeCompletion/namespace-alias.cpp
test/CodeCompletion/namespace.cpp
test/CodeCompletion/nested-name-specifier.cpp
test/CodeCompletion/operator.cpp
test/CodeCompletion/ordinary-name.c
test/CodeCompletion/tag.c
test/CodeCompletion/tag.cpp
test/CodeCompletion/templates.cpp
test/CodeCompletion/truncation.c [new file with mode: 0644]
test/CodeCompletion/truncation.c.h [new file with mode: 0644]
test/CodeCompletion/using-namespace.cpp
test/CodeCompletion/using.cpp
tools/clang-cc/clang-cc.cpp

index b4e7f1093b581c7ebc7fc7134a2d0abfe8404b8f..385b1c2a0b8153ed2d7afd59ac7f9e86401bdcb6 100644 (file)
@@ -17,6 +17,8 @@ def err_fe_error_reading_stdin : Error<"error reading stdin">;
 def err_fe_error_backend : Error<"error in backend: %0">, DefaultFatal;
 def err_fe_invalid_ast_file : Error<"invalid AST file: '%0'">, DefaultFatal;
 def err_fe_invalid_ast_action : Error<"invalid action for AST input">, DefaultFatal;
+def err_fe_invalid_code_complete_file 
+  : Error<"cannot locate code-completion file %0">, DefaultFatal;
 
 def note_fixit_applied : Note<"FIX-IT applied suggested code changes">;
 def note_fixit_in_macro : Note<
index e0eb2197ce06c4d653cf7983ef0d64e447b38915..7eb988f005ec0996bfce87fbd7a7591ae00a70f4 100644 (file)
@@ -54,6 +54,9 @@ 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,15 +96,28 @@ 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; }
+      
     ContentCache(const FileEntry *Ent = 0)
-      : Buffer(0), Entry(Ent), SourceLineCache(0), NumLines(0) {}
+      : Buffer(0), TruncateAtLine(0), TruncateAtColumn(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), SourceLineCache(0) {
+    ContentCache(const ContentCache &RHS) 
+      : Buffer(0), TruncateAtLine(0), TruncateAtColumn(0), SourceLineCache(0) {
       Entry = RHS.Entry;
 
       assert (RHS.Buffer == 0 && RHS.SourceLineCache == 0
@@ -331,13 +347,19 @@ 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) {
+      NumBinaryProbes(0), TruncateFile(0), TruncateAtLine(0),
+      TruncateAtColumn(0) {
     clearIDTables();
   }
   ~SourceManager();
@@ -647,6 +669,12 @@ 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 17823cd25774d110515619d058440f54046588c3..0765ac391be3f3a53c5d8f1267c80eab0cd46e71 100644 (file)
@@ -92,10 +92,6 @@ class Preprocessor {
   bool DisableMacroExpansion : 1;  // True if macro expansion is disabled.
   bool InMacroArgs : 1;            // True if parsing fn macro invocation args.
 
-  /// \brief True if the end-of-file of the main file should be treated as
-  /// a code-completion token.
-  bool IsMainFileEofCodeCompletion : 1;
-
   /// Identifiers - This is mapping/lookup information for all identifiers in
   /// the program, including program keywords.
   IdentifierTable Identifiers;
@@ -263,12 +259,6 @@ public:
     Callbacks = C;
   }
 
-  /// \brief Note that, for the main source file, the end-of-file should be
-  /// treated as a code-completion token.
-  void SetMainFileEofCodeCompletion() {
-    IsMainFileEofCodeCompletion = true;
-  }
-  
   /// getMacroInfo - Given an identifier, return the MacroInfo it is #defined to
   /// or null if it isn't #define'd.
   MacroInfo *getMacroInfo(IdentifierInfo *II) const {
index d1c47095a24d19d54ef9e3645dfaea110e0c9bef..962cb4c42a8ab0e030581801daf89f79eba53e58 100644 (file)
@@ -41,9 +41,10 @@ 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.
+///  file is not lazily brought in from disk to satisfy this query unless it
+///  needs to be truncated due to a truncateAt() call.
 unsigned ContentCache::getSize() const {
-  return Entry ? Entry->getSize() : Buffer->getBufferSize();
+  return Buffer ? Buffer->getBufferSize() : Entry->getSize();
 }
 
 const llvm::MemoryBuffer *ContentCache::getBuffer() const {
@@ -52,10 +53,54 @@ const llvm::MemoryBuffer *ContentCache::getBuffer() const {
     // 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(), 0, 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.
@@ -287,6 +332,16 @@ 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;
 }
 
@@ -1058,6 +1113,28 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
   }
 }
 
+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 74ac55cf2eb451860e73ad0a123dca0571ef5675..0f01155a8f2c836df8434f33638b81b276c3d9ae 100644 (file)
@@ -101,12 +101,16 @@ Lexer::Lexer(FileID FID, Preprocessor &PP)
     Features(PP.getLangOptions()) {
 
   const llvm::MemoryBuffer *InputFile = PP.getSourceManager().getBuffer(FID);
-
+  
   InitLexer(InputFile->getBufferStart(), InputFile->getBufferStart(),
             InputFile->getBufferEnd());
 
   // 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
@@ -1323,15 +1327,23 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
   // unterminated #if and missing newline.
 
   if (IsEofCodeCompletion) {
-    // We're at the end of the file, but we've been asked to conside the
-    // end of the file to be a code-completion token. Return the
-    // code-completion token.
-    Result.startToken();
-    FormTokenWithChars(Result, CurPtr, tok::code_completion);
+    bool isIntendedFile = true;
+    if (PP && FileLoc.isFileID()) {
+      SourceManager &SM = PP->getSourceManager();
+      isIntendedFile = SM.isTruncatedFile(SM.getFileID(FileLoc));
+    }
     
-    // Only do the eof -> code_completion translation once.
-    IsEofCodeCompletion = false;
-    return true;
+    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;
+    }
   }
   
   // If we are in a #if directive, emit an error.
index 4e522cbb8a4ada8f61a0f6c497f28a766703e0d0..bfa090a09e870d2d1a0f58fa6e4cf057d3441a95 100644 (file)
@@ -71,7 +71,6 @@ Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts,
   // Macro expansion is enabled.
   DisableMacroExpansion = false;
   InMacroArgs = false;
-  IsMainFileEofCodeCompletion = false;
   NumCachedTokenLexers = 0;
 
   CachedLexPos = 0;
@@ -369,13 +368,6 @@ void Preprocessor::EnterMainSourceFile() {
   // Enter the main file source buffer.
   EnterSourceFile(MainFileID, 0);
 
-  if (IsMainFileEofCodeCompletion) {
-    // Tell our newly-created lexer that it should treat its end-of-file as
-    // a code-completion token.
-    IsMainFileEofCodeCompletion = false;
-    static_cast<Lexer *>(getCurrentFileLexer())->SetEofIsCodeCompletion();
-  }
-  
   // Tell the header info that the main file was entered.  If the file is later
   // #imported, it won't be re-entered.
   if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID))
index 4faff15c05a74b7ce98d5e1ad492882862fd6cec..dd90083874c9ff0d8d3b660222df8d43ed3a2a30 100644 (file)
@@ -1,5 +1,5 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test.
 void f(float x, float y);
 void f(int i, int j, int k);
 struct X { };
@@ -10,13 +10,20 @@ namespace N {
     
     operator int() const;
   };
-  void f(Y y);
+  void f(Y y, int);
 }
 typedef N::Y Y;
 void f();
 
 void test() {
-  // CHECK-CC1: f : 0 : f(<#struct N::Y y#>)
+  f(Y(), 0, 0);
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:19:9 %s -o - | FileCheck -check-prefix=CC1 %s &&
+  // CHECK-CC1: f : 0 : f(<#struct N::Y y#>, <#int#>)
   // CHECK-NEXT-CC1: f : 0 : f(<#int i#>, <#int j#>, <#int k#>)
   // CHECK-NEXT-CC1: f : 0 : f(<#float x#>, <#float y#>)
-  f(Y(),
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:19:13 %s -o - | FileCheck -check-prefix=CC2 %s &&
+  // CHECK-NOT-CC2: f : 0 : f(<#struct N::Y y#>, <#int#>)
+  // CHECK-CC2: f : 0 : f(<#int i#>, <#int j#>, <#int k#>)
+  // CHECK-NEXT-CC2: f : 0 : f(<#float x#>, <#float y#>)
+  // RUN: true
+}
index fc1b0ea07578efce1fee5b4b6688136533928fd3..468a4f979f7c8d4ff8d47262812f822ed400d292 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace M {
   
 namespace N {
@@ -23,11 +20,14 @@ namespace M {
   
 void test(enum N::C::Color color) {
   switch (color) {
-      // CHECK-NEXT-CC1: Blue : 0 : N::C::Blue
-      // CHECK-NEXT-CC1: Green : 0 : N::C::Green
-      // CHECK-NEXT-CC1: Indigo : 0 : N::C::Indigo
-      // CHECK-NEXT-CC1: Orange : 0 : N::C::Orange
-      // CHECK-NEXT-CC1: Red : 0 : N::C::Red
-      // CHECK-NEXT-CC1: Violet : 0 : N::C::Violet
-      // CHECK-NEXT-CC1: Yellow : 0 : N::C::Yellow
-    case 
+  case 
+    // RUN: clang-cc -fsyntax-only -code-completion-at=%s:23:8 %s -o - | FileCheck -check-prefix=CC1 %s &&
+    // RUN: true
+    // CHECK-NEXT-CC1: Blue : 0 : N::C::Blue
+    // CHECK-NEXT-CC1: Green : 0 : N::C::Green
+    // CHECK-NEXT-CC1: Indigo : 0 : N::C::Indigo
+    // CHECK-NEXT-CC1: Orange : 0 : N::C::Orange
+    // CHECK-NEXT-CC1: Red : 0 : N::C::Red
+    // CHECK-NEXT-CC1: Violet : 0 : N::C::Violet
+    // CHECK-NEXT-CC1: Yellow : 0 : N::C::Yellow
+      
index 08488f75c1f8f8a9b255ffacd83124959743c710..255fbbce2de94e4a5fe9b5ef0402eb5de66528ec 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 enum Color {
   Red,
   Orange,
@@ -19,9 +16,14 @@ void test(enum Color color) {
     case Yellow:
       break;
 
+    case Green:
+      break;
+      
+    // RUN: clang-cc -fsyntax-only -code-completion-at=%s:19:10 %s -o - | FileCheck -check-prefix=CC1 %s &&
     // CHECK-CC1: Blue : 0
     // CHECK-NEXT-CC1: Green : 0
     // CHECK-NEXT-CC1: Indigo : 0
     // CHECK-NEXT-CC1: Orange : 0
     // CHECK-NEXT-CC1: Violet : 0
-    case 
+    // RUN: true
+      
index 49b33c830af8752e23b5d2d6cfa282a96e2c4b39..15e50fdf484353b43597c71be466f84823dde422 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace N {
   enum Color {
     Red,
@@ -15,15 +12,18 @@ namespace N {
 
 void test(enum N::Color color) {
   switch (color) {
-    case N::Red:
-      break;
-      
-    case N::Yellow:
-      break;
-      
-      // CHECK-CC1: Blue : 0 : N::Blue
-      // CHECK-NEXT-CC1: Green : 0 : N::Green
-      // CHECK-NEXT-CC1: Indigo : 0 : N::Indigo
-      // CHECK-NEXT-CC1: Orange : 0 : N::Orange
-      // CHECK-NEXT-CC1: Violet : 0 : N::Violet
-    case 
+  case N::Red:
+    break;
+    
+  case N::Yellow:
+    break;
+    
+  case 
+    // RUN: clang-cc -fsyntax-only -code-completion-at=%s:21:8 %s -o - | FileCheck -check-prefix=CC1 %s &&
+    // CHECK-CC1: Blue : 0 : N::Blue
+    // CHECK-NEXT-CC1: Green : 0 : N::Green
+    // CHECK-NEXT-CC1: Indigo : 0 : N::Indigo
+    // CHECK-NEXT-CC1: Orange : 0 : N::Orange
+    // CHECK-NEXT-CC1: Violet : 0 : N::Violet
+    
+    // RUN: true
index c9a893ec9c238b935d6620561a96e8cfd5c01892..52cba71bd2f920c7e2779a98b58874015fc0bff8 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace std {
   template<typename RandomAccessIterator>
   void sort(RandomAccessIterator first, RandomAccessIterator last);
@@ -10,7 +7,9 @@ namespace std {
 }
 
 void f() {
+  std::  
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:10:8 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: dyn_cast<<#class X#>>(<#Y *Val#>)
   // CHECK-CC1: sort(<#RandomAccessIterator first#>, <#RandomAccessIterator last#>)
-  std::
+  // RUN: true
   
index f722e9a07e33bfa878387db12fb0e19d7304f250..f04ef01d705409556997eae930fe01aa2667d943 100644 (file)
@@ -1,9 +1,9 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
 void f(int i, int j = 2, int k = 5);
 void f(float x, float y);
        
 void test() {
+  ::
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:5:5 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: f(<#int i#>{#, <#int j#>{#, <#int k#>#}#})
   // CHECK-CC1: f(<#float x#>, <#float y#>)
-  ::
+  // RUN: true
index 25b2b9ce22f16fae17e94cf610c4afddd01a4fea..1e8e5630981faf07e024dd506a8c0275f5ea448f 100644 (file)
@@ -1,5 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
 struct Point {
   float x;
   float y;
@@ -7,7 +5,9 @@ struct Point {
 };
 
 void test(struct Point *p) {
+  p->
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:8:6 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: x
   // CHECK-CC1: y
   // CHECK-CC1: z
-  p->
\ No newline at end of file
+  // RUN: true
index c2dfee44dd0e0afc48e939adcb48e8e38dc0a071..cbd19db1a5cd3dacfd68c794fc8405592ac92a84 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 struct Base1 {
   int member1;
   float member2;
@@ -29,6 +26,8 @@ public:
 };
 
 void test(const Proxy &p) {
+  p->
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:29:6 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: member4 : 0
   // CHECK-CC1: memfun3 : 0
   // CHECK-CC1: memfun1 : 1
@@ -39,4 +38,5 @@ void test(const Proxy &p) {
   // CHECK-CC1: member2 : 2
   // CHECK-CC1: member3 : 2
   // CHECK-CC1: memfun1 : 2 (Hidden) : Base2::memfun1(<#int#>)
-  p->
\ No newline at end of file
+  // RUN: true
+  
index 8d70c4517d816b3278a85d174453a58a2cdf04e1..cae3d561d113f8329ffb9757b04c8d1b4fa775d2 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace N4 {
   namespace N3 { }
 }
@@ -13,10 +10,12 @@ namespace N2 {
   namespace I5 { }
   namespace I1 { }
   
+  namespace New =
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:13:18 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: I1 : 1
   // CHECK-CC1: I4 : 1
   // CHECK-CC1: I5 : 1
   // CHECK-CC1: N2 : 2
   // CHECK-NEXT-CC1: N4 : 2
-  namespace New =
-
+  // RUN: true
+  
\ No newline at end of file
index db841248ab7667d8794423c3f46edb689d10f3c7..5563698e934622b8b34b31b9761be5a62860d13b 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace N3 {
 }
 
@@ -10,6 +7,9 @@ namespace N2 {
   namespace I5 { }
   namespace I1 { }
   
+  namespace
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:10:12 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: I1 : 0
   // CHECK-NEXT-CC1: I5 : 0
-  namespace
+  // RUN: true
+  
index 4d6a75f8cbe5d8ad72045a5f322c99e2c14b8db4..0cc5a19421c78938d0068dbdc184d6600f59cce3 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace N {
   struct A { };
   namespace M { 
@@ -12,7 +9,10 @@ namespace N {
   struct B { };
 }
 
+N::
+// RUN: clang-cc -fsyntax-only -code-completion-at=%s:12:4 %s -o - | FileCheck -check-prefix=CC1 %s &&
 // CHECK-CC1: A : 0
 // CHECK-CC1: B : 0
 // CHECK-CC1: M : 0
-N::
\ No newline at end of file
+// RUN: true
+
index 808940526fbd480ae01fb8011741619dafdb5da7..72a3f6bb719435df73b190598cb320a1e1522ea8 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 class T { };
 
 typedef int Integer;
@@ -10,10 +7,12 @@ namespace N { }
 void f() {
   typedef float Float;
   
+  operator
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:10:11 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: Float : 0
   // CHECK-CC1: + : 0
   // CHECK-CC1: short : 0
   // CHECK-CC1: Integer : 2
   // CHECK-CC1: T : 2
   // CHECK-CC1: N : 5
-  operator
+  // RUN: true
index a532409d606c94f1b94e4d80206856fffb2f9c56..caba130f8b68c5ca10f5f647fdc82c5c008a0b8d 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 struct X { int x; };
 
 typedef struct X TYPEDEF;
@@ -10,3 +7,5 @@ void foo() {
   // CHECK-CC1: y : 0
   // CHECK-NEXT-CC1: TYPEDEF : 2
   // CHECK-NEXT-CC1: foo : 2
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:6:9 %s -o - | FileCheck -check-prefix=CC1 %s &&
+  // RUN: true
index 35ddda273e2c8a8556d4401133e670e8c97b66a7..e7250f540c4f74517e9df5364c5bab819ba4714f 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 enum X { x };
 enum Y { y };
 struct Z { };
@@ -9,6 +6,8 @@ void X();
 
 void test() {
   enum X { x };
+  enum
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:9:7 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: X : 0
   // CHECK-CC1: Y : 2
-  enum
+  // RUN: true
index d8f6f2fa0a8d202de22e8690a80ef7033725f215..201aec4dd38139eb32c8ee95da2c63e26dbf1a4c 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 class X { };
 struct Y { };
 
@@ -17,6 +14,8 @@ namespace N {
   class Y;
   
   void test() {
+    class
+    // RUN: clang-cc -fsyntax-only -code-completion-at=%s:17:10 %s -o - | FileCheck -check-prefix=CC1 %s &&
     // CHECK-CC1: Y : 2
     // CHECK-CC1: Z : 2
     // CHECK-CC1: A : 3
@@ -24,4 +23,4 @@ namespace N {
     // CHECK-CC1: Y : 3
     // CHECK-CC1: M : 6
     // CHECK-CC1: N : 6
-    class
+    // RUN: true
index f7751413b985c4abc9b7200ff466c4d45eccc2e6..22cca65bea986a481ebbf004395730b62ae8dfc3 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace std {
   template<typename T>
   class allocator;
@@ -10,8 +7,11 @@ namespace std {
 }
 
 void f() {
+  std::
+  // RUN: clang-cc -fsyntax-only -code-completion-at=%s:10:8 %s -o - | FileCheck -check-prefix=CC1 %s &&
   // CHECK-CC1: allocator<<#typename T#>>
   // CHECK-CC1: vector<<#typename T#>{#, <#typename Alloc#>#}>
-  std::
-
+  // RUN: true
+  
+  
 
diff --git a/test/CodeCompletion/truncation.c b/test/CodeCompletion/truncation.c
new file mode 100644 (file)
index 0000000..b64b066
--- /dev/null
@@ -0,0 +1,12 @@
+#include "truncation.c.h"
+
+struct 
+
+// RUN: clang-cc -fsyntax-only -code-completion-at=%s.h:4:8 -o - %s | FileCheck -check-prefix=CC1 %s &&
+// CHECK-CC1: X : 1
+// CHECK-NEXT-CC1: Y : 1
+// RUN: clang-cc -fsyntax-only -code-completion-at=%s:3:8 -o - %s | FileCheck -check-prefix=CC2 %s &&
+// CHECK-CC2: X : 1
+// CHECK-CC2: Xa : 1
+// CHECK-CC2: Y : 1
+// RUN: true
diff --git a/test/CodeCompletion/truncation.c.h b/test/CodeCompletion/truncation.c.h
new file mode 100644 (file)
index 0000000..a5ebbac
--- /dev/null
@@ -0,0 +1,5 @@
+struct X { };
+struct Y { };
+
+struct Xa { };
+
index b30b0bcfac9b7031fc7b13f534b031d2dd7ca7c6..95bff9b5eebc249510bffb1dc8ea696a8631707b 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace N4 {
   namespace N3 { }
 }
@@ -14,11 +11,11 @@ namespace N2 {
   namespace I1 { }
   
   void foo() {
+    using namespace
+    // RUN: clang-cc -fsyntax-only -code-completion-at=%s:14:20 %s -o - | FileCheck -check-prefix=CC1 %s &&
     // CHECK-CC1: I1 : 2
     // CHECK-CC1: I4 : 2
     // CHECK-CC1: I5 : 2
     // CHECK-CC1: N2 : 3
     // CHECK-NEXT-CC1: N4 : 3
-    using namespace
-
-
+    // RUN: true
index 7bef3534599422e5df0603e9dd9020783b9d6b37..27b85fc7661b8c1e92e32ea3a9dce1e93f5d7001 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
-// RUN: true
-
 namespace N4 {
   namespace N3 { }
 }
@@ -16,12 +13,13 @@ namespace N2 {
   void foo() {
     int N3;
     
+    using
+    // RUN: clang-cc -fsyntax-only -code-completion-at=%s:16:10 %s -o - | FileCheck -check-prefix=CC1 %s &&
     // CHECK-CC1: I1 : 2
     // CHECK-CC1: I4 : 2
     // CHECK-CC1: I5 : 2
     // CHECK-CC1: N2 : 3
     // CHECK-CC1: N3 : 3
     // CHECK-NEXT-CC1: N4 : 3
-    using
-    
+    // RUN: true
 
index f223534df5329f3ea05aa958160b4b7ffb6b5ef1..d6c536e0cc38f274b3fce39c41c9c35c84c0c517 100644 (file)
@@ -214,10 +214,10 @@ OutputFile("o",
  llvm::cl::desc("Specify output file"));
 
 
-static llvm::cl::opt<int>
-DumpCodeCompletion("code-completion-dump",
-                   llvm::cl::value_desc("N"),
-                   llvm::cl::desc("Dump code-completion information at $$N$$"));
+static llvm::cl::opt<ParsedSourceLocation>
+CodeCompletionAt("code-completion-at",
+                 llvm::cl::value_desc("file:line:column"),
+              llvm::cl::desc("Dump code-completion information at a location"));
 
 /// \brief Buld a new code-completion consumer that prints the results of
 /// code completion to standard output.
@@ -2057,17 +2057,22 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
     CodeCompleteConsumer *(*CreateCodeCompleter)(Sema &, void *) = 0;
     void *CreateCodeCompleterData = 0;
     
-    if (DumpCodeCompletion) {
-      // To dump code-completion information, we chop off the file at the
-      // location of the string $$N$$, where N is the value provided to
-      // -code-completion-dump, and then tell the lexer to return a 
-      // code-completion token before it hits the end of the file.
-      // FIXME: Find $$N$$ in the main file buffer
-      
-      PP.SetMainFileEofCodeCompletion();
-      
-      // Set up the creation routine for code-completion.
-      CreateCodeCompleter = BuildPrintingCodeCompleter;
+    if (!CodeCompletionAt.FileName.empty()) {
+      // Tell the source manager to chop off the given file at a specific
+      // line and column.
+      if (const FileEntry *Entry 
+            = PP.getFileManager().getFile(CodeCompletionAt.FileName)) {
+        // Truncate the named file at the given line/column.
+        PP.getSourceManager().truncateFileAt(Entry, CodeCompletionAt.Line,
+                                             CodeCompletionAt.Column);
+        
+        // Set up the creation routine for code-completion.
+        CreateCodeCompleter = BuildPrintingCodeCompleter;
+      } else {
+        PP.getDiagnostics().Report(FullSourceLoc(), 
+                                   diag::err_fe_invalid_code_complete_file)
+          << CodeCompletionAt.FileName;
+      }
     }
 
     ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats,