Use precompiled preambles for in-process code completion.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@110596 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index bbc8fba..0885037 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -686,7 +686,19 @@
* 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/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h
index e8d4cce..6651743 100644
--- a/include/clang/Frontend/ASTUnit.h
+++ b/include/clang/Frontend/ASTUnit.h
@@ -78,9 +78,12 @@
/// \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 @@
bool Parse(llvm::MemoryBuffer *OverrideMainBuffer);
std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> >
- 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 @@
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<std::string, const llvm::MemoryBuffer *> RemappedFile;
@@ -352,7 +364,8 @@
llvm::IntrusiveRefCntPtr<Diagnostic> 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 @@
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/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h
index 66a33f2..9e0fb7e 100644
--- a/include/clang/Lex/Lexer.h
+++ b/include/clang/Lex/Lexer.h
@@ -237,11 +237,14 @@
///
/// \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<unsigned, bool>
- ComputePreamble(const llvm::MemoryBuffer *Buffer);
+ ComputePreamble(const llvm::MemoryBuffer *Buffer, unsigned MaxLines = 0);
//===--------------------------------------------------------------------===//
// Internal implementation interfaces.
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index 88c55a8..b287522 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/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 <cstdlib>
#include <cstdio>
@@ -48,8 +49,8 @@
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 @@
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 @@
virtual bool hasCodeCompletionSupport() const { return false; }
virtual bool hasASTFileSupport() const { return false; }
+ virtual bool usesCompleteTranslationUnit() { return false; }
};
}
@@ -567,7 +572,8 @@
/// that corresponds to the main file along with a pair (bytes, start-of-line)
/// that describes the preamble.
std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> >
-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 @@
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 @@
/// 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 @@
bool CreatedPreambleBuffer = false;
std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> > 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 @@
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 @@
llvm::IntrusiveRefCntPtr<Diagnostic> 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 @@
AST->Diagnostics = Diags;
AST->CaptureDiagnostics = CaptureDiagnostics;
AST->OnlyLocalDecls = OnlyLocalDecls;
+ AST->CompleteTranslationUnit = CompleteTranslationUnit;
AST->Invocation.reset(CI);
CI->getPreprocessorOpts().RetainRemappedFileBuffers = true;
@@ -1030,7 +1056,7 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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<SyntaxOnlyAction> Act;
Act.reset(new SyntaxOnlyAction);
if (Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
@@ -1237,8 +1312,12 @@
Act->Execute();
Act->EndSourceFile();
}
+
+ if (CompletionTimer)
+ CompletionTimer->stopTimer();
// Steal back our resources.
+ delete OverrideMainBuffer;
Clang.takeFileManager();
Clang.takeSourceManager();
Clang.takeInvocation();
diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp
index 5e43590..b492d77 100644
--- a/lib/Lex/Lexer.cpp
+++ b/lib/Lex/Lexer.cpp
@@ -311,7 +311,7 @@
}
std::pair<unsigned, bool>
-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 @@
Token TheTok;
Token IfStartTok;
unsigned IfCount = 0;
+ unsigned Line = 0;
+
do {
TheLexer.LexFromRawLexer(TheTok);
@@ -345,6 +347,16 @@
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 @@
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/test/Index/preamble.c b/test/Index/preamble.c
index 7e75bf9..c285cd2 100644
--- a/test/Index/preamble.c
+++ b/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 @@
// 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/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 1530aa8..db897b3 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -874,7 +874,8 @@
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 @@
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 @@
}
clang_disposeCodeCompleteResults(results);
}
-
+ clang_disposeTranslationUnit(TU);
clang_disposeIndex(CIdx);
free(filename);
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 6414166..60eef4d 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -1195,7 +1195,9 @@
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<Diagnostic> Diags;
@@ -1250,7 +1252,8 @@
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 @@
return static_cast<ASTUnit *>(TU)->Reparse(RemappedFiles.data(),
RemappedFiles.size())? 1 : 0;
}
-
+
CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) {
if (!CTUnit)
return createCXString("");
diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp
index 4ab6b9b..790f32f 100644
--- a/tools/libclang/CIndexCodeCompletion.cpp
+++ b/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 <cstdlib>
#include <cstdio>
+
#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 <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -277,7 +278,16 @@
#endif
bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0;
-
+
+ llvm::OwningPtr<llvm::NamedRegionTimer> 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<CIndexer *>(CIdx);