From 028d3e4d0fc16abb55ab566f909d9a30951a7325 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 9 Aug 2010 20:45:32 +0000 Subject: [PATCH] Use precompiled preambles for in-process code completion. llvm-svn: 110596 --- clang/include/clang-c/Index.h | 14 ++- clang/include/clang/Frontend/ASTUnit.h | 24 +++- clang/include/clang/Lex/Lexer.h | 5 +- clang/lib/Frontend/ASTUnit.cpp | 103 ++++++++++++++++-- clang/lib/Lex/Lexer.cpp | 18 ++- clang/test/Index/preamble.c | 8 ++ clang/tools/c-index-test/c-index-test.c | 28 +++-- clang/tools/libclang/CIndex.cpp | 9 +- clang/tools/libclang/CIndexCodeCompletion.cpp | 18 ++- 9 files changed, 188 insertions(+), 39 deletions(-) diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index bbc8fbaf962c..088503732147 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -686,7 +686,19 @@ enum CXTranslationUnit_Flags { * clang_reparseTranslationUnit() will re-use the implicit * precompiled header to improve parsing performance. */ - CXTranslationUnit_PrecompiledPreamble = 0x04 + CXTranslationUnit_PrecompiledPreamble = 0x04, + /** + * \brief Used to indicate that the translation unit is incomplete. + * + * When a translation unit is considered "incomplete", semantic + * analysis that is typically performed at the end of the + * translation unit will be suppressed. For example, this suppresses + * the completion of tentative declarations in C and of + * instantiation of implicitly-instantiation function templates in + * C++. This option is typically used when parsing a header with the + * intent of producing a precompiled header. + */ + CXTranslationUnit_Incomplete = 0x08 }; /** diff --git a/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h index e8d4ccef0293..665174330279 100644 --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/include/clang/Frontend/ASTUnit.h @@ -78,9 +78,12 @@ private: /// \brief Whether to capture any diagnostics produced. bool CaptureDiagnostics; - /// Track whether the main file was loaded from an AST or not. + /// \brief Track whether the main file was loaded from an AST or not. bool MainFileIsAST; + /// \brief Whether this AST represents a complete translation unit. + bool CompleteTranslationUnit; + /// Track the top-level decls which appeared in an ASTUnit which was loaded /// from a source file. // @@ -199,9 +202,12 @@ private: bool Parse(llvm::MemoryBuffer *OverrideMainBuffer); std::pair > - ComputePreamble(CompilerInvocation &Invocation, bool &CreatedBuffer); + ComputePreamble(CompilerInvocation &Invocation, + unsigned MaxLines, bool &CreatedBuffer); - llvm::MemoryBuffer *BuildPrecompiledPreamble(); + llvm::MemoryBuffer *getMainBufferWithPrecompiledPreamble( + bool AllowRebuild = true, + unsigned MaxLines = 0); void RealizeTopLevelDeclsFromPreamble(); public: @@ -318,6 +324,12 @@ public: return StoredDiagnostics; } + /// \brief Whether this AST represents a complete translation unit. + /// + /// If false, this AST is only a partial translation unit, e.g., one + /// that might still be used as a precompiled header or preamble. + bool isCompleteTranslationUnit() const { return CompleteTranslationUnit; } + /// \brief A mapping from a file name to the memory buffer that stores the /// remapped contents of that file. typedef std::pair RemappedFile; @@ -352,7 +364,8 @@ public: llvm::IntrusiveRefCntPtr Diags, bool OnlyLocalDecls = false, bool CaptureDiagnostics = false, - bool PrecompilePreamble = false); + bool PrecompilePreamble = false, + bool CompleteTranslationUnit = true); /// LoadFromCommandLine - Create an ASTUnit from a vector of command line /// arguments, which must specify exactly one source file. @@ -376,7 +389,8 @@ public: RemappedFile *RemappedFiles = 0, unsigned NumRemappedFiles = 0, bool CaptureDiagnostics = false, - bool PrecompilePreamble = false); + bool PrecompilePreamble = false, + bool CompleteTranslationUnit = true); /// \brief Reparse the source files using the same command-line options that /// were originally used to produce this translation unit. diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 66a33f24f60d..9e0fb7ee70ea 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -237,11 +237,14 @@ public: /// /// \param Buffer The memory buffer containing the file's contents. /// + /// \param MaxLines If non-zero, restrict the length of the preamble + /// to fewer than this number of lines. + /// /// \returns The offset into the file where the preamble ends and the rest /// of the file begins along with a boolean value indicating whether /// the preamble ends at the beginning of a new line. static std::pair - ComputePreamble(const llvm::MemoryBuffer *Buffer); + ComputePreamble(const llvm::MemoryBuffer *Buffer, unsigned MaxLines = 0); //===--------------------------------------------------------------------===// // Internal implementation interfaces. diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 88c55a885a22..b287522806ed 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -34,6 +34,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/System/Host.h" #include "llvm/System/Path.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Timer.h" #include #include @@ -48,8 +49,8 @@ const unsigned DefaultPreambleRebuildInterval = 5; ASTUnit::ASTUnit(bool _MainFileIsAST) : CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), - ConcurrencyCheckValue(CheckUnlocked), PreambleRebuildCounter(0), - SavedMainFileBuffer(0) { + CompleteTranslationUnit(true), ConcurrencyCheckValue(CheckUnlocked), + PreambleRebuildCounter(0), SavedMainFileBuffer(0) { } ASTUnit::~ASTUnit() { @@ -334,6 +335,9 @@ public: TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} virtual bool hasCodeCompletionSupport() const { return false; } + virtual bool usesCompleteTranslationUnit() { + return Unit.isCompleteTranslationUnit(); + } }; class PrecompilePreambleConsumer : public PCHGenerator { @@ -396,6 +400,7 @@ public: virtual bool hasCodeCompletionSupport() const { return false; } virtual bool hasASTFileSupport() const { return false; } + virtual bool usesCompleteTranslationUnit() { return false; } }; } @@ -567,7 +572,8 @@ static std::string GetPreamblePCHPath() { /// that corresponds to the main file along with a pair (bytes, start-of-line) /// that describes the preamble. std::pair > -ASTUnit::ComputePreamble(CompilerInvocation &Invocation, bool &CreatedBuffer) { +ASTUnit::ComputePreamble(CompilerInvocation &Invocation, + unsigned MaxLines, bool &CreatedBuffer) { FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); PreprocessorOptions &PreprocessorOpts = Invocation.getPreprocessorOpts(); @@ -642,7 +648,7 @@ ASTUnit::ComputePreamble(CompilerInvocation &Invocation, bool &CreatedBuffer) { CreatedBuffer = true; } - return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer)); + return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer, MaxLines)); } static llvm::MemoryBuffer *CreatePaddedMainFileBuffer(llvm::MemoryBuffer *Old, @@ -673,10 +679,19 @@ static llvm::MemoryBuffer *CreatePaddedMainFileBuffer(llvm::MemoryBuffer *Old, /// this routine will determine if it is still valid and, if so, avoid /// rebuilding the precompiled preamble. /// +/// \param AllowRebuild When true (the default), this routine is +/// allowed to rebuild the precompiled preamble if it is found to be +/// out-of-date. +/// +/// \param MaxLines When non-zero, the maximum number of lines that +/// can occur within the preamble. +/// /// \returns If the precompiled preamble can be used, returns a newly-allocated /// buffer that should be used in place of the main file when doing so. /// Otherwise, returns a NULL pointer. -llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() { +llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( + bool AllowRebuild, + unsigned MaxLines) { CompilerInvocation PreambleInvocation(*Invocation); FrontendOptions &FrontendOpts = PreambleInvocation.getFrontendOpts(); PreprocessorOptions &PreprocessorOpts @@ -684,7 +699,7 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() { bool CreatedPreambleBuffer = false; std::pair > NewPreamble - = ComputePreamble(PreambleInvocation, CreatedPreambleBuffer); + = ComputePreamble(PreambleInvocation, MaxLines, CreatedPreambleBuffer); if (!NewPreamble.second.first) { // We couldn't find a preamble in the main source. Clear out the current @@ -793,12 +808,21 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() { FrontendOpts.Inputs[0].second); } } + + // If we aren't allowed to rebuild the precompiled preamble, just + // return now. + if (!AllowRebuild) + return 0; // We can't reuse the previously-computed preamble. Build a new one. Preamble.clear(); llvm::sys::Path(PreambleFile).eraseFromDisk(); PreambleRebuildCounter = 1; - } + } else if (!AllowRebuild) { + // We aren't allowed to rebuild the precompiled preamble; just + // return now. + return 0; + } // If the preamble rebuild counter > 1, it's because we previously // failed to build a preamble and we're not yet ready to try @@ -1004,7 +1028,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, llvm::IntrusiveRefCntPtr Diags, bool OnlyLocalDecls, bool CaptureDiagnostics, - bool PrecompilePreamble) { + bool PrecompilePreamble, + bool CompleteTranslationUnit) { if (!Diags.getPtr()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. @@ -1018,6 +1043,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, AST->Diagnostics = Diags; AST->CaptureDiagnostics = CaptureDiagnostics; AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CompleteTranslationUnit = CompleteTranslationUnit; AST->Invocation.reset(CI); CI->getPreprocessorOpts().RetainRemappedFileBuffers = true; @@ -1030,7 +1056,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, // FIXME: When C++ PCH is ready, allow use of it for a precompiled preamble. if (PrecompilePreamble && !CI->getLangOpts().CPlusPlus) { AST->PreambleRebuildCounter = 1; - OverrideMainBuffer = AST->BuildPrecompiledPreamble(); + OverrideMainBuffer = AST->getMainBufferWithPrecompiledPreamble(); } llvm::Timer *ParsingTimer = 0; @@ -1055,7 +1081,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, RemappedFile *RemappedFiles, unsigned NumRemappedFiles, bool CaptureDiagnostics, - bool PrecompilePreamble) { + bool PrecompilePreamble, + bool CompleteTranslationUnit) { if (!Diags.getPtr()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. @@ -1116,7 +1143,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, CI->getFrontendOpts().DisableFree = true; return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls, - CaptureDiagnostics, PrecompilePreamble); + CaptureDiagnostics, PrecompilePreamble, + CompleteTranslationUnit); } bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { @@ -1140,7 +1168,7 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { // build a precompiled preamble, do so now. llvm::MemoryBuffer *OverrideMainBuffer = 0; if (!PreambleFile.empty() || PreambleRebuildCounter > 0) - OverrideMainBuffer = BuildPrecompiledPreamble(); + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(); // Clear out the diagnostics state. if (!OverrideMainBuffer) @@ -1165,6 +1193,17 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, if (!Invocation.get()) return; + llvm::Timer *CompletionTimer = 0; + if (TimerGroup.get()) { + llvm::SmallString<128> TimerName; + llvm::raw_svector_ostream TimerNameOut(TimerName); + TimerNameOut << "Code completion @ " << File << ":" << Line << ":" + << Column; + CompletionTimer = new llvm::Timer(TimerNameOut.str(), *TimerGroup); + CompletionTimer->startTimer(); + Timers.push_back(CompletionTimer); + } + CompilerInvocation CCInvocation(*Invocation); FrontendOptions &FrontendOpts = CCInvocation.getFrontendOpts(); PreprocessorOptions &PreprocessorOpts = CCInvocation.getPreprocessorOpts(); @@ -1230,6 +1269,42 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, // Use the code completion consumer we were given. Clang.setCodeCompletionConsumer(&Consumer); + // If we have a precompiled preamble, try to use it. We only allow + // the use of the precompiled preamble if we're if the completion + // point is within the main file, after the end of the precompiled + // preamble. + llvm::MemoryBuffer *OverrideMainBuffer = 0; + if (!PreambleFile.empty()) { + using llvm::sys::FileStatus; + llvm::sys::PathWithStatus CompleteFilePath(File); + llvm::sys::PathWithStatus MainPath(OriginalSourceFile); + if (const FileStatus *CompleteFileStatus = CompleteFilePath.getFileStatus()) + if (const FileStatus *MainStatus = MainPath.getFileStatus()) + if (CompleteFileStatus->getUniqueID() == MainStatus->getUniqueID()) + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(false, + Line); + } + + // If the main file has been overridden due to the use of a preamble, + // make that override happen and introduce the preamble. + if (OverrideMainBuffer) { + PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); + PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); + PreprocessorOpts.PrecompiledPreambleBytes.second + = PreambleEndsAtStartOfLine; + PreprocessorOpts.ImplicitPCHInclude = PreambleFile; + PreprocessorOpts.DisablePCHValidation = true; + + // The stored diagnostics have the old source manager. Copy them + // to our output set of stored diagnostics, updating the source + // manager to the one we were given. + for (unsigned I = 0, N = this->StoredDiagnostics.size(); I != N; ++I) { + StoredDiagnostics.push_back(this->StoredDiagnostics[I]); + FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), SourceMgr); + StoredDiagnostics[I].setLocation(Loc); + } + } + llvm::OwningPtr Act; Act.reset(new SyntaxOnlyAction); if (Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, @@ -1237,8 +1312,12 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, Act->Execute(); Act->EndSourceFile(); } + + if (CompletionTimer) + CompletionTimer->stopTimer(); // Steal back our resources. + delete OverrideMainBuffer; Clang.takeFileManager(); Clang.takeSourceManager(); Clang.takeInvocation(); diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 5e435908bebd..b492d7769177 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -311,7 +311,7 @@ namespace { } std::pair -Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) { +Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer, unsigned MaxLines) { // Create a lexer starting at the beginning of the file. Note that we use a // "fake" file source location at offset 1 so that the lexer will track our // position within the file. @@ -325,6 +325,8 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) { Token TheTok; Token IfStartTok; unsigned IfCount = 0; + unsigned Line = 0; + do { TheLexer.LexFromRawLexer(TheTok); @@ -345,6 +347,16 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) { InPreprocessorDirective = false; } + // Keep track of the # of lines in the preamble. + if (TheTok.isAtStartOfLine()) { + ++Line; + + // If we were asked to limit the number of lines in the preamble, + // and we're about to exceed that limit, we're done. + if (MaxLines && Line >= MaxLines) + break; + } + // Comments are okay; skip over them. if (TheTok.getKind() == tok::comment) continue; @@ -418,7 +430,9 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) { TheTok = HashTok; } - // We hit a token + // We hit a token that we don't recognize as being in the + // "preprocessing only" part of the file, so we're no longer in + // the preamble. break; } while (true); diff --git a/clang/test/Index/preamble.c b/clang/test/Index/preamble.c index 7e75bf9b89c8..c285cd2b2daa 100644 --- a/clang/test/Index/preamble.c +++ b/clang/test/Index/preamble.c @@ -2,6 +2,9 @@ #include "preamble.h" int wibble(int); +void f(int x) { + +} // RUN: %clang -x c-header -o %t.pch %S/Inputs/prefix.h // RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 5 local -I %S/Inputs -include %t %s 2> %t.stderr.txt | FileCheck %s // RUN: FileCheck -check-prefix CHECK-DIAG %s < %t.stderr.txt @@ -18,3 +21,8 @@ int wibble(int); // CHECK: preamble.c:3:5: FunctionDecl=wibble:3:5 Extent=[3:5 - 3:16] // CHECK: preamble.c:3:15: ParmDecl=:3:15 (Definition) Extent=[3:12 - 3:16] // CHECK-DIAG: preamble.h:4:7:{4:9-4:13}: warning: incompatible pointer types assigning to 'int *' from 'float *' +// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:6:1 -I %S/Inputs -include %t %s 2> %t.stderr.txt | FileCheck -check-prefix CHECK-CC %s +// CHECK-CC: FunctionDecl:{ResultType int}{TypedText bar}{LeftParen (}{Placeholder int i}{RightParen )} (50) +// CHECK-CC: FunctionDecl:{ResultType void}{TypedText f}{LeftParen (}{Placeholder int x}{RightParen )} (50) +// CHECK-CC: FunctionDecl:{ResultType int}{TypedText foo}{LeftParen (}{Placeholder int}{RightParen )} (50) +// CHECK-CC: FunctionDecl:{ResultType int}{TypedText wibble}{LeftParen (}{Placeholder int}{RightParen )} (50) diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index 1530aa86977d..db897b3a5007 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -874,7 +874,8 @@ int perform_code_completion(int argc, const char **argv, int timing_only) { struct CXUnsavedFile *unsaved_files = 0; int num_unsaved_files = 0; CXCodeCompleteResults *results = 0; - + CXTranslationUnit *TU = 0; + if (timing_only) input += strlen("-code-completion-timing="); else @@ -889,15 +890,20 @@ int perform_code_completion(int argc, const char **argv, int timing_only) { CIdx = clang_createIndex(0, 1); if (getenv("CINDEXTEST_EDITING")) { - CXTranslationUnit *TU = clang_parseTranslationUnit(CIdx, 0, - argv + num_unsaved_files + 2, - argc - num_unsaved_files - 2, - unsaved_files, - num_unsaved_files, - getDefaultParsingOptions()); - results = clang_codeCompleteAt(TU, filename, line, column, - unsaved_files, num_unsaved_files, - clang_defaultCodeCompleteOptions()); + TU = clang_parseTranslationUnit(CIdx, 0, + argv + num_unsaved_files + 2, + argc - num_unsaved_files - 2, + unsaved_files, + num_unsaved_files, + getDefaultParsingOptions()); + unsigned I, Repeats = 5; + for (I = 0; I != Repeats; ++I) { + results = clang_codeCompleteAt(TU, filename, line, column, + unsaved_files, num_unsaved_files, + clang_defaultCodeCompleteOptions()); + if (I != Repeats-1) + clang_disposeCodeCompleteResults(results); + } } else results = clang_codeComplete(CIdx, argv[argc - 1], argc - num_unsaved_files - 3, @@ -918,7 +924,7 @@ int perform_code_completion(int argc, const char **argv, int timing_only) { } clang_disposeCodeCompleteResults(results); } - + clang_disposeTranslationUnit(TU); clang_disposeIndex(CIdx); free(filename); diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 6414166d3723..60eef4d2839b 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1195,7 +1195,9 @@ CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx, if (options & CXTranslationUnit_Editing) options |= CXTranslationUnit_PrecompiledPreamble; bool PrecompilePreamble = options & CXTranslationUnit_PrecompiledPreamble; - + bool CompleteTranslationUnit + = ((options & CXTranslationUnit_Incomplete) == 0); + // Configure the diagnostics. DiagnosticOptions DiagOpts; llvm::IntrusiveRefCntPtr Diags; @@ -1250,7 +1252,8 @@ CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx, RemappedFiles.data(), RemappedFiles.size(), /*CaptureDiagnostics=*/true, - PrecompilePreamble)); + PrecompilePreamble, + CompleteTranslationUnit)); if (NumErrors != Diags->getNumErrors()) { // Make sure to check that 'Unit' is non-NULL. @@ -1451,7 +1454,7 @@ int clang_reparseTranslationUnit(CXTranslationUnit TU, return static_cast(TU)->Reparse(RemappedFiles.data(), RemappedFiles.size())? 1 : 0; } - + CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) { if (!CTUnit) return createCXString(""); diff --git a/clang/tools/libclang/CIndexCodeCompletion.cpp b/clang/tools/libclang/CIndexCodeCompletion.cpp index 4ab6b9bd91e3..790f32fbda4d 100644 --- a/clang/tools/libclang/CIndexCodeCompletion.cpp +++ b/clang/tools/libclang/CIndexCodeCompletion.cpp @@ -20,17 +20,18 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Sema/CodeCompleteConsumer.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/System/Program.h" #include #include + #ifdef UDP_CODE_COMPLETION_LOGGER #include "clang/Basic/Version.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/Timer.h" -#include "llvm/Support/raw_ostream.h" #include #include #include @@ -277,7 +278,16 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, #endif bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0; - + + llvm::OwningPtr CCTimer; + if (getenv("LIBCLANG_TIMING")) { + llvm::SmallString<128> TimerName; + llvm::raw_svector_ostream TimerNameOut(TimerName); + TimerNameOut << "Code completion @ " << complete_filename << ":" + << complete_line << ":" << complete_column; + CCTimer.reset(new llvm::NamedRegionTimer(TimerNameOut.str())); + } + // The indexer, which is mainly used to determine where diagnostics go. CIndexer *CXXIdx = static_cast(CIdx);