From b657f115c3b4e262e72906a28cbcf3eaccd9460c Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 22 Sep 2009 21:11:38 +0000 Subject: [PATCH] Replace the -code-completion-dump option with -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 --- .../clang/Basic/DiagnosticFrontendKinds.td | 2 + include/clang/Basic/SourceManager.h | 34 +++++++- include/clang/Lex/Preprocessor.h | 10 --- lib/Basic/SourceManager.cpp | 81 ++++++++++++++++++- lib/Lex/Lexer.cpp | 30 ++++--- lib/Lex/Preprocessor.cpp | 8 -- test/CodeCompletion/call.cpp | 17 ++-- .../enum-switch-case-qualified.cpp | 22 ++--- test/CodeCompletion/enum-switch-case.c | 10 ++- test/CodeCompletion/enum-switch-case.cpp | 30 +++---- test/CodeCompletion/function-templates.cpp | 7 +- test/CodeCompletion/functions.cpp | 6 +- test/CodeCompletion/member-access.c | 6 +- test/CodeCompletion/member-access.cpp | 8 +- test/CodeCompletion/namespace-alias.cpp | 9 +-- test/CodeCompletion/namespace.cpp | 8 +- test/CodeCompletion/nested-name-specifier.cpp | 8 +- test/CodeCompletion/operator.cpp | 7 +- test/CodeCompletion/ordinary-name.c | 5 +- test/CodeCompletion/tag.c | 7 +- test/CodeCompletion/tag.cpp | 7 +- test/CodeCompletion/templates.cpp | 10 +-- test/CodeCompletion/truncation.c | 12 +++ test/CodeCompletion/truncation.c.h | 5 ++ test/CodeCompletion/using-namespace.cpp | 9 +-- test/CodeCompletion/using.cpp | 8 +- tools/clang-cc/clang-cc.cpp | 35 ++++---- 27 files changed, 261 insertions(+), 140 deletions(-) create mode 100644 test/CodeCompletion/truncation.c create mode 100644 test/CodeCompletion/truncation.c.h diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index b4e7f1093b..385b1c2a0b 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -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< diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index e0eb2197ce..7eb988f005 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -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_iterator fileinfo_iterator; diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 17823cd257..0765ac391b 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -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 { diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp index d1c47095a2..962cb4c42a 100644 --- a/lib/Basic/SourceManager.cpp +++ b/lib/Basic/SourceManager.cpp @@ -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(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(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::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/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp index 74ac55cf2e..0f01155a8f 100644 --- a/lib/Lex/Lexer.cpp +++ b/lib/Lex/Lexer.cpp @@ -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. diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 4e522cbb8a..bfa090a09e 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -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(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)) diff --git a/test/CodeCompletion/call.cpp b/test/CodeCompletion/call.cpp index 4faff15c05..dd90083874 100644 --- a/test/CodeCompletion/call.cpp +++ b/test/CodeCompletion/call.cpp @@ -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 +} diff --git a/test/CodeCompletion/enum-switch-case-qualified.cpp b/test/CodeCompletion/enum-switch-case-qualified.cpp index fc1b0ea075..468a4f979f 100644 --- a/test/CodeCompletion/enum-switch-case-qualified.cpp +++ b/test/CodeCompletion/enum-switch-case-qualified.cpp @@ -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 + diff --git a/test/CodeCompletion/enum-switch-case.c b/test/CodeCompletion/enum-switch-case.c index 08488f75c1..255fbbce2d 100644 --- a/test/CodeCompletion/enum-switch-case.c +++ b/test/CodeCompletion/enum-switch-case.c @@ -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 + diff --git a/test/CodeCompletion/enum-switch-case.cpp b/test/CodeCompletion/enum-switch-case.cpp index 49b33c830a..15e50fdf48 100644 --- a/test/CodeCompletion/enum-switch-case.cpp +++ b/test/CodeCompletion/enum-switch-case.cpp @@ -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 diff --git a/test/CodeCompletion/function-templates.cpp b/test/CodeCompletion/function-templates.cpp index c9a893ec9c..52cba71bd2 100644 --- a/test/CodeCompletion/function-templates.cpp +++ b/test/CodeCompletion/function-templates.cpp @@ -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 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 diff --git a/test/CodeCompletion/functions.cpp b/test/CodeCompletion/functions.cpp index f722e9a07e..f04ef01d70 100644 --- a/test/CodeCompletion/functions.cpp +++ b/test/CodeCompletion/functions.cpp @@ -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 diff --git a/test/CodeCompletion/member-access.c b/test/CodeCompletion/member-access.c index 25b2b9ce22..1e8e563098 100644 --- a/test/CodeCompletion/member-access.c +++ b/test/CodeCompletion/member-access.c @@ -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 diff --git a/test/CodeCompletion/member-access.cpp b/test/CodeCompletion/member-access.cpp index c2dfee44dd..cbd19db1a5 100644 --- a/test/CodeCompletion/member-access.cpp +++ b/test/CodeCompletion/member-access.cpp @@ -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 + diff --git a/test/CodeCompletion/namespace-alias.cpp b/test/CodeCompletion/namespace-alias.cpp index 8d70c4517d..cae3d561d1 100644 --- a/test/CodeCompletion/namespace-alias.cpp +++ b/test/CodeCompletion/namespace-alias.cpp @@ -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 diff --git a/test/CodeCompletion/namespace.cpp b/test/CodeCompletion/namespace.cpp index db841248ab..5563698e93 100644 --- a/test/CodeCompletion/namespace.cpp +++ b/test/CodeCompletion/namespace.cpp @@ -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 + diff --git a/test/CodeCompletion/nested-name-specifier.cpp b/test/CodeCompletion/nested-name-specifier.cpp index 4d6a75f8cb..0cc5a19421 100644 --- a/test/CodeCompletion/nested-name-specifier.cpp +++ b/test/CodeCompletion/nested-name-specifier.cpp @@ -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 + diff --git a/test/CodeCompletion/operator.cpp b/test/CodeCompletion/operator.cpp index 808940526f..72a3f6bb71 100644 --- a/test/CodeCompletion/operator.cpp +++ b/test/CodeCompletion/operator.cpp @@ -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 diff --git a/test/CodeCompletion/ordinary-name.c b/test/CodeCompletion/ordinary-name.c index a532409d60..caba130f8b 100644 --- a/test/CodeCompletion/ordinary-name.c +++ b/test/CodeCompletion/ordinary-name.c @@ -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 diff --git a/test/CodeCompletion/tag.c b/test/CodeCompletion/tag.c index 35ddda273e..e7250f540c 100644 --- a/test/CodeCompletion/tag.c +++ b/test/CodeCompletion/tag.c @@ -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 diff --git a/test/CodeCompletion/tag.cpp b/test/CodeCompletion/tag.cpp index d8f6f2fa0a..201aec4dd3 100644 --- a/test/CodeCompletion/tag.cpp +++ b/test/CodeCompletion/tag.cpp @@ -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 diff --git a/test/CodeCompletion/templates.cpp b/test/CodeCompletion/templates.cpp index f7751413b9..22cca65bea 100644 --- a/test/CodeCompletion/templates.cpp +++ b/test/CodeCompletion/templates.cpp @@ -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 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 index 0000000000..b64b066586 --- /dev/null +++ b/test/CodeCompletion/truncation.c @@ -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 index 0000000000..a5ebbacb34 --- /dev/null +++ b/test/CodeCompletion/truncation.c.h @@ -0,0 +1,5 @@ +struct X { }; +struct Y { }; + +struct Xa { }; + diff --git a/test/CodeCompletion/using-namespace.cpp b/test/CodeCompletion/using-namespace.cpp index b30b0bcfac..95bff9b5ee 100644 --- a/test/CodeCompletion/using-namespace.cpp +++ b/test/CodeCompletion/using-namespace.cpp @@ -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 diff --git a/test/CodeCompletion/using.cpp b/test/CodeCompletion/using.cpp index 7bef353459..27b85fc766 100644 --- a/test/CodeCompletion/using.cpp +++ b/test/CodeCompletion/using.cpp @@ -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 diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp index f223534df5..d6c536e0cc 100644 --- a/tools/clang-cc/clang-cc.cpp +++ b/tools/clang-cc/clang-cc.cpp @@ -214,10 +214,10 @@ OutputFile("o", llvm::cl::desc("Specify output file")); -static llvm::cl::opt -DumpCodeCompletion("code-completion-dump", - llvm::cl::value_desc("N"), - llvm::cl::desc("Dump code-completion information at $$N$$")); +static llvm::cl::opt +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, -- 2.40.0