Add code-completion support directly to ASTUnit, which performs code

completion within the translation unit using the same command-line
arguments for parsing the translation unit. Eventually, we'll reuse
the precompiled preamble to improve code-completion performance, and
this also gives us a place to cache results.

Expose this function via the new libclang function
clang_codeCompleteAt(), which performs the code completion within a
CXTranslationUnit. The completion occurs in-process
(clang_codeCompletion() runs code completion out-of-process).

llvm-svn: 110210
This commit is contained in:
Douglas Gregor 2010-08-04 16:47:14 +00:00
parent 988bfce174
commit 8e984da800
13 changed files with 470 additions and 101 deletions

View File

@ -715,7 +715,7 @@ enum CXTranslationUnit_Flags {
* \p command_line_args.
*
* \param unsaved_files the files that have not yet been saved to disk
* but may be required for code completion, including the contents of
* but may be required for parsing, including the contents of
* those files. The contents and name of these files (as specified by
* CXUnsavedFile) are copied when necessary, so the client only needs to
* guarantee their validity until the call to this function returns.
@ -2078,6 +2078,76 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx,
unsigned complete_line,
unsigned complete_column);
/**
* \brief Perform code completion at a given location in a translation unit.
*
* This function performs code completion at a particular file, line, and
* column within source code, providing results that suggest potential
* code snippets based on the context of the completion. The basic model
* for code completion is that Clang will parse a complete source file,
* performing syntax checking up to the location where code-completion has
* been requested. At that point, a special code-completion token is passed
* to the parser, which recognizes this token and determines, based on the
* current location in the C/Objective-C/C++ grammar and the state of
* semantic analysis, what completions to provide. These completions are
* returned via a new \c CXCodeCompleteResults structure.
*
* Code completion itself is meant to be triggered by the client when the
* user types punctuation characters or whitespace, at which point the
* code-completion location will coincide with the cursor. For example, if \c p
* is a pointer, code-completion might be triggered after the "-" and then
* after the ">" in \c p->. When the code-completion location is afer the ">",
* the completion results will provide, e.g., the members of the struct that
* "p" points to. The client is responsible for placing the cursor at the
* beginning of the token currently being typed, then filtering the results
* based on the contents of the token. For example, when code-completing for
* the expression \c p->get, the client should provide the location just after
* the ">" (e.g., pointing at the "g") to this code-completion hook. Then, the
* client can filter the results based on the current token text ("get"), only
* showing those results that start with "get". The intent of this interface
* is to separate the relatively high-latency acquisition of code-completion
* results from the filtering of results on a per-character basis, which must
* have a lower latency.
*
* \param TU The translation unit in which code-completion should
* occur. The source files for this translation unit need not be
* completely up-to-date (and the contents of those source files may
* be overridden via \p unsaved_files). Cursors referring into the
* translation unit may be invalidated by this invocation.
*
* \param complete_filename The name of the source file where code
* completion should be performed. This filename may be any file
* included in the translation unit.
*
* \param complete_line The line at which code-completion should occur.
*
* \param complete_column The column at which code-completion should occur.
* Note that the column should point just after the syntactic construct that
* initiated code completion, and not in the middle of a lexical token.
*
* \param unsaved_files the Tiles that have not yet been saved to disk
* but may be required for parsing or code completion, including the
* contents of those files. The contents and name of these files (as
* specified by CXUnsavedFile) are copied when necessary, so the
* client only needs to guarantee their validity until the call to
* this function returns.
*
* \param num_unsaved_files The number of unsaved file entries in \p
* unsaved_files.
*
* \returns If successful, a new \c CXCodeCompleteResults structure
* containing code-completion results, which should eventually be
* freed with \c clang_disposeCodeCompleteResults(). If code
* completion fails, returns NULL.
*/
CINDEX_LINKAGE
CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU,
const char *complete_filename,
unsigned complete_line,
unsigned complete_column,
struct CXUnsavedFile *unsaved_files,
unsigned num_unsaved_files);
/**
* \brief Free the given set of code-completion results.
*/

View File

@ -38,6 +38,7 @@ namespace llvm {
namespace clang {
class ASTContext;
class CodeCompleteConsumer;
class CompilerInvocation;
class Decl;
class Diagnostic;
@ -384,6 +385,24 @@ public:
/// contain any translation-unit information, false otherwise.
bool Reparse(RemappedFile *RemappedFiles = 0,
unsigned NumRemappedFiles = 0);
/// \brief Perform code completion at the given file, line, and
/// column within this translation unit.
///
/// \brief File The file in which code completion will occur.
/// \brief Line The line at which code completion will occur.
/// \brief Column The column at which code completion will occur.
/// \brief Consumer The consumer that will receive code-completion results.
///
/// FIXME: The Diag, LangOpts, SourceMgr, FileMgr, and
/// StoredDiagnostics parameters are all disgusting hacks. They will
/// go away.
void CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
RemappedFile *RemappedFiles, unsigned NumRemappedFiles,
CodeCompleteConsumer &Consumer,
Diagnostic &Diag, LangOptions &LangOpts,
SourceManager &SourceMgr, FileManager &FileMgr,
llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics);
};
} // namespace clang

View File

@ -274,7 +274,10 @@ public:
std::string getAsString() const;
/// \brief Clone this code-completion string.
CodeCompletionString *Clone() const;
///
/// \param Result If non-NULL, points to an empty code-completion
/// result that will be given a cloned copy of
CodeCompletionString *Clone(CodeCompletionString *Result = 0) const;
/// \brief Serialize this code-completion string to the given stream.
void Serialize(llvm::raw_ostream &OS) const;
@ -410,7 +413,14 @@ public:
/// \brief Create a new code-completion string that describes how to insert
/// this result into a program.
CodeCompletionString *CreateCodeCompletionString(Sema &S);
///
/// \param S The semantic analysis that created the result.
///
/// \param Result If non-NULL, the already-allocated, empty
/// code-completion string that will be populated with the
/// appropriate code completion string for this result.
CodeCompletionString *CreateCodeCompletionString(Sema &S,
CodeCompletionString *Result = 0);
void Destroy();

View File

@ -417,8 +417,12 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) {
Clang.setInvocation(Invocation.take());
OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
// Set up diagnostics.
// Set up diagnostics, capturing any diagnostics that would
// otherwise be dropped.
Clang.setDiagnostics(&getDiagnostics());
CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
getDiagnostics(),
StoredDiagnostics);
Clang.setDiagnosticClient(getDiagnostics().getClient());
// Create the target instance.
@ -456,12 +460,7 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) {
if (!OverrideMainBuffer)
StoredDiagnostics.clear();
// Capture any diagnostics that would otherwise be dropped.
CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
Clang.getDiagnostics(),
StoredDiagnostics);
// Create a file manager object to provide access to and cache the filesystem.
Clang.setFileManager(&getFileManager());
@ -471,11 +470,13 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) {
// If the main file has been overridden due to the use of a preamble,
// make that override happen and introduce the preamble.
PreprocessorOptions &PreprocessorOpts = Clang.getPreprocessorOpts();
std::string PriorImplicitPCHInclude;
if (OverrideMainBuffer) {
PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer);
PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size();
PreprocessorOpts.PrecompiledPreambleBytes.second
= PreambleEndsAtStartOfLine;
PriorImplicitPCHInclude = PreprocessorOpts.ImplicitPCHInclude;
PreprocessorOpts.ImplicitPCHInclude = PreambleFile;
PreprocessorOpts.DisablePCHValidation = true;
@ -513,10 +514,12 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) {
Act->EndSourceFile();
// Remove the overridden buffer we used for the preamble.
if (OverrideMainBuffer)
if (OverrideMainBuffer) {
PreprocessorOpts.eraseRemappedFile(
PreprocessorOpts.remapped_file_buffer_end() - 1);
PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude;
}
Clang.takeDiagnosticClient();
Invocation.reset(Clang.takeInvocation());
@ -528,6 +531,7 @@ error:
PreprocessorOpts.eraseRemappedFile(
PreprocessorOpts.remapped_file_buffer_end() - 1);
PreprocessorOpts.DisablePCHValidation = true;
PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude;
}
Clang.takeSourceManager();
@ -853,8 +857,11 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() {
Clang.setInvocation(&PreambleInvocation);
OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
// Set up diagnostics.
// Set up diagnostics, capturing all of the diagnostics produced.
Clang.setDiagnostics(&getDiagnostics());
CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
getDiagnostics(),
StoredDiagnostics);
Clang.setDiagnosticClient(getDiagnostics().getClient());
// Create the target instance.
@ -889,11 +896,6 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() {
StoredDiagnostics.clear();
TopLevelDecls.clear();
TopLevelDeclsInPreamble.clear();
// Capture any diagnostics that would otherwise be dropped.
CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
getDiagnostics(),
StoredDiagnostics);
// Create a file manager object to provide access to and cache the filesystem.
Clang.setFileManager(new FileManager);
@ -1127,7 +1129,6 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
}
// Remap files.
// FIXME: Do we want to remove old mappings for these files?
Invocation->getPreprocessorOpts().clearRemappedFiles();
for (unsigned I = 0; I != NumRemappedFiles; ++I)
Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first,
@ -1149,3 +1150,88 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
ReparsingTimer->stopTimer();
return Result;
}
void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
RemappedFile *RemappedFiles,
unsigned NumRemappedFiles,
CodeCompleteConsumer &Consumer,
Diagnostic &Diag, LangOptions &LangOpts,
SourceManager &SourceMgr, FileManager &FileMgr,
llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics) {
if (!Invocation.get())
return;
CompilerInvocation CCInvocation(*Invocation);
FrontendOptions &FrontendOpts = CCInvocation.getFrontendOpts();
PreprocessorOptions &PreprocessorOpts = CCInvocation.getPreprocessorOpts();
FrontendOpts.ShowMacrosInCodeCompletion = 1;
FrontendOpts.ShowCodePatternsInCodeCompletion = 1;
FrontendOpts.CodeCompletionAt.FileName = File;
FrontendOpts.CodeCompletionAt.Line = Line;
FrontendOpts.CodeCompletionAt.Column = Column;
// Set the language options appropriately.
LangOpts = CCInvocation.getLangOpts();
CompilerInstance Clang;
Clang.setInvocation(&CCInvocation);
OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
// Set up diagnostics, capturing any diagnostics produced.
Clang.setDiagnostics(&Diag);
CaptureDroppedDiagnostics Capture(true,
Clang.getDiagnostics(),
StoredDiagnostics);
Clang.setDiagnosticClient(Diag.getClient());
// Create the target instance.
Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
Clang.getTargetOpts()));
if (!Clang.hasTarget()) {
Clang.takeDiagnosticClient();
Clang.takeInvocation();
}
// Inform the target of the language options.
//
// FIXME: We shouldn't need to do this, the target should be immutable once
// created. This complexity should be lifted elsewhere.
Clang.getTarget().setForcedLangOptions(Clang.getLangOpts());
assert(Clang.getFrontendOpts().Inputs.size() == 1 &&
"Invocation must have exactly one source file!");
assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST &&
"FIXME: AST inputs not yet supported here!");
assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR &&
"IR inputs not support here!");
// Use the source and file managers that we were given.
Clang.setFileManager(&FileMgr);
Clang.setSourceManager(&SourceMgr);
// Remap files.
PreprocessorOpts.clearRemappedFiles();
for (unsigned I = 0; I != NumRemappedFiles; ++I)
PreprocessorOpts.addRemappedFile(RemappedFiles[I].first,
RemappedFiles[I].second);
// Use the code completion consumer we were given.
Clang.setCodeCompletionConsumer(&Consumer);
llvm::OwningPtr<SyntaxOnlyAction> Act;
Act.reset(new SyntaxOnlyAction);
if (Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
Clang.getFrontendOpts().Inputs[0].first)) {
Act->Execute();
Act->EndSourceFile();
}
// Steal back our resources.
Clang.takeFileManager();
Clang.takeSourceManager();
Clang.takeInvocation();
Clang.takeDiagnosticClient();
Clang.takeCodeCompletionConsumer();
}

View File

@ -296,17 +296,41 @@ CompilerInstance::createPCHExternalASTSource(llvm::StringRef Path,
// Code Completion
static bool EnableCodeCompletion(Preprocessor &PP,
const std::string &Filename,
unsigned Line,
unsigned Column) {
// Tell the source manager to chop off the given file at a specific
// line and column.
const FileEntry *Entry = PP.getFileManager().getFile(Filename);
if (!Entry) {
PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file)
<< Filename;
return true;
}
// Truncate the named file at the given line/column.
PP.SetCodeCompletionPoint(Entry, Line, Column);
return false;
}
void CompilerInstance::createCodeCompletionConsumer() {
const ParsedSourceLocation &Loc = getFrontendOpts().CodeCompletionAt;
CompletionConsumer.reset(
createCodeCompletionConsumer(getPreprocessor(),
Loc.FileName, Loc.Line, Loc.Column,
getFrontendOpts().DebugCodeCompletionPrinter,
getFrontendOpts().ShowMacrosInCodeCompletion,
if (!CompletionConsumer) {
CompletionConsumer.reset(
createCodeCompletionConsumer(getPreprocessor(),
Loc.FileName, Loc.Line, Loc.Column,
getFrontendOpts().DebugCodeCompletionPrinter,
getFrontendOpts().ShowMacrosInCodeCompletion,
getFrontendOpts().ShowCodePatternsInCodeCompletion,
llvm::outs()));
if (!CompletionConsumer)
llvm::outs()));
if (!CompletionConsumer)
return;
} else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName,
Loc.Line, Loc.Column)) {
CompletionConsumer.reset();
return;
}
if (CompletionConsumer->isOutputBinary() &&
llvm::sys::Program::ChangeStdoutToBinary()) {
@ -328,17 +352,8 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP,
bool ShowMacros,
bool ShowCodePatterns,
llvm::raw_ostream &OS) {
// Tell the source manager to chop off the given file at a specific
// line and column.
const FileEntry *Entry = PP.getFileManager().getFile(Filename);
if (!Entry) {
PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file)
<< Filename;
if (EnableCodeCompletion(PP, Filename, Line, Column))
return 0;
}
// Truncate the named file at the given line/column.
PP.SetCodeCompletionPoint(Entry, Line, Column);
// Set up the creation routine for code-completion.
if (UseDebugPrinter)

View File

@ -245,8 +245,10 @@ const char *CodeCompletionString::getTypedText() const {
return 0;
}
CodeCompletionString *CodeCompletionString::Clone() const {
CodeCompletionString *Result = new CodeCompletionString;
CodeCompletionString *
CodeCompletionString::Clone(CodeCompletionString *Result) const {
if (!Result)
Result = new CodeCompletionString;
for (iterator C = begin(), CEnd = end(); C != CEnd; ++C)
Result->AddChunk(C->Clone());
return Result;
@ -493,98 +495,81 @@ PrintingCodeCompleteConsumer::ProcessOverloadCandidates(Sema &SemaRef,
}
}
void
CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
Result *Results,
unsigned NumResults) {
// Print the results.
for (unsigned I = 0; I != NumResults; ++I) {
CXCursorKind Kind = CXCursor_NotImplemented;
switch (Results[I].Kind) {
namespace clang {
// FIXME: Used externally by CIndexCodeCompletion.cpp; this code
// will move there, eventually, when the CIndexCodeCompleteConsumer
// dies.
CXCursorKind
getCursorKindForCompletionResult(const CodeCompleteConsumer::Result &R) {
typedef CodeCompleteConsumer::Result Result;
switch (R.Kind) {
case Result::RK_Declaration:
switch (Results[I].Declaration->getKind()) {
switch (R.Declaration->getKind()) {
case Decl::Record:
case Decl::CXXRecord:
case Decl::ClassTemplateSpecialization: {
RecordDecl *Record = cast<RecordDecl>(Results[I].Declaration);
RecordDecl *Record = cast<RecordDecl>(R.Declaration);
if (Record->isStruct())
Kind = CXCursor_StructDecl;
return CXCursor_StructDecl;
else if (Record->isUnion())
Kind = CXCursor_UnionDecl;
return CXCursor_UnionDecl;
else
Kind = CXCursor_ClassDecl;
break;
return CXCursor_ClassDecl;
}
case Decl::ObjCMethod: {
ObjCMethodDecl *Method = cast<ObjCMethodDecl>(Results[I].Declaration);
ObjCMethodDecl *Method = cast<ObjCMethodDecl>(R.Declaration);
if (Method->isInstanceMethod())
Kind = CXCursor_ObjCInstanceMethodDecl;
return CXCursor_ObjCInstanceMethodDecl;
else
Kind = CXCursor_ObjCClassMethodDecl;
break;
return CXCursor_ObjCClassMethodDecl;
}
case Decl::Typedef:
Kind = CXCursor_TypedefDecl;
break;
return CXCursor_TypedefDecl;
case Decl::Enum:
Kind = CXCursor_EnumDecl;
break;
return CXCursor_EnumDecl;
case Decl::Field:
Kind = CXCursor_FieldDecl;
break;
return CXCursor_FieldDecl;
case Decl::EnumConstant:
Kind = CXCursor_EnumConstantDecl;
break;
return CXCursor_EnumConstantDecl;
case Decl::Function:
case Decl::CXXMethod:
case Decl::CXXConstructor:
case Decl::CXXDestructor:
case Decl::CXXConversion:
Kind = CXCursor_FunctionDecl;
break;
return CXCursor_FunctionDecl;
case Decl::Var:
Kind = CXCursor_VarDecl;
break;
return CXCursor_VarDecl;
case Decl::ParmVar:
Kind = CXCursor_ParmDecl;
break;
return CXCursor_ParmDecl;
case Decl::ObjCInterface:
Kind = CXCursor_ObjCInterfaceDecl;
break;
return CXCursor_ObjCInterfaceDecl;
case Decl::ObjCCategory:
Kind = CXCursor_ObjCCategoryDecl;
break;
return CXCursor_ObjCCategoryDecl;
case Decl::ObjCProtocol:
Kind = CXCursor_ObjCProtocolDecl;
break;
return CXCursor_ObjCProtocolDecl;
case Decl::ObjCProperty:
Kind = CXCursor_ObjCPropertyDecl;
break;
return CXCursor_ObjCPropertyDecl;
case Decl::ObjCIvar:
Kind = CXCursor_ObjCIvarDecl;
break;
return CXCursor_ObjCIvarDecl;
case Decl::ObjCImplementation:
Kind = CXCursor_ObjCImplementationDecl;
break;
return CXCursor_ObjCImplementationDecl;
case Decl::ObjCCategoryImpl:
Kind = CXCursor_ObjCCategoryImplDecl;
break;
return CXCursor_ObjCCategoryImplDecl;
default:
break;
@ -592,16 +577,23 @@ CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
break;
case Result::RK_Macro:
Kind = CXCursor_MacroDefinition;
break;
return CXCursor_MacroDefinition;
case Result::RK_Keyword:
case Result::RK_Pattern:
Kind = CXCursor_NotImplemented;
break;
return CXCursor_NotImplemented;
}
return CXCursor_NotImplemented;
}
}
WriteUnsigned(OS, Kind);
void
CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
Result *Results,
unsigned NumResults) {
// Print the results.
for (unsigned I = 0; I != NumResults; ++I) {
WriteUnsigned(OS, getCursorKindForCompletionResult(Results[I]));
WriteUnsigned(OS, Results[I].Priority);
CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef);
assert(CCS && "No code-completion string?");

View File

@ -1811,13 +1811,15 @@ static void AddFunctionTypeQualsToCompletionString(CodeCompletionString *Result,
/// how to use this result, or NULL to indicate that the string or name of the
/// result is all that is needed.
CodeCompletionString *
CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S,
CodeCompletionString *Result) {
typedef CodeCompletionString::Chunk Chunk;
if (Kind == RK_Pattern)
return Pattern->Clone();
return Pattern->Clone(Result);
CodeCompletionString *Result = new CodeCompletionString;
if (!Result)
Result = new CodeCompletionString;
if (Kind == RK_Keyword) {
Result->AddTypedTextChunk(Keyword);

View File

@ -13,12 +13,14 @@ void f2() { f1(17); }
const char *str = "Hello, \nWorld";
// RUN: c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: macro definition:{TypedText __VERSION__} (70)
// CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (12)
// CHECK-CC1-NOT: NotImplemented:{TypedText float} (40)
// CHECK-CC1: ParmDecl:{ResultType int}{TypedText j} (2)
// CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// RUN: c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
// CHECK-CC3: macro definition:{TypedText __VERSION__} (70)
// CHECK-CC3: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC3-NOT: NotImplemented:{TypedText float} (40)

View File

@ -888,11 +888,21 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
return -1;
CIdx = clang_createIndex(0, 1);
results = clang_codeComplete(CIdx,
argv[argc - 1], argc - num_unsaved_files - 3,
argv + num_unsaved_files + 2,
num_unsaved_files, unsaved_files,
filename, line, column);
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);
} else
results = clang_codeComplete(CIdx,
argv[argc - 1], argc - num_unsaved_files - 3,
argv + num_unsaved_files + 2,
num_unsaved_files, unsaved_files,
filename, line, column);
if (results) {
unsigned i, n = results->NumResults;

View File

@ -1443,7 +1443,7 @@ int clang_reparseTranslationUnit(CXTranslationUnit TU,
for (unsigned I = 0; I != num_unsaved_files; ++I) {
llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length);
const llvm::MemoryBuffer *Buffer
= llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
= llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename,
Buffer));
}

View File

@ -16,6 +16,7 @@
#include "CIndexDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Sema/CodeCompleteConsumer.h"
@ -231,7 +232,7 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults {
llvm::SmallVector<StoredDiagnostic, 8> Diagnostics;
/// \brief Diag object
Diagnostic Diag;
llvm::IntrusiveRefCntPtr<Diagnostic> Diag;
/// \brief Language options used to adjust source locations.
LangOptions LangOpts;
@ -248,7 +249,8 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults {
};
AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults()
: CXCodeCompleteResults(), Buffer(0), SourceMgr(Diag) { }
: CXCodeCompleteResults(), Buffer(0), Diag(new Diagnostic),
SourceMgr(*Diag) { }
AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() {
for (unsigned I = 0, N = NumResults; I != N; ++I)
@ -454,6 +456,165 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx,
// destroyed.
Results->TemporaryFiles.swap(TemporaryFiles);
#ifdef UDP_CODE_COMPLETION_LOGGER
#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime();
llvm::SmallString<256> LogResult;
llvm::raw_svector_ostream os(LogResult);
// Figure out the language and whether or not it uses PCH.
const char *lang = 0;
bool usesPCH = false;
for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end();
I != E; ++I) {
if (*I == 0)
continue;
if (strcmp(*I, "-x") == 0) {
if (I + 1 != E) {
lang = *(++I);
continue;
}
}
else if (strcmp(*I, "-include") == 0) {
if (I+1 != E) {
const char *arg = *(++I);
llvm::SmallString<512> pchName;
{
llvm::raw_svector_ostream os(pchName);
os << arg << ".pth";
}
pchName.push_back('\0');
struct stat stat_results;
if (stat(pchName.data(), &stat_results) == 0)
usesPCH = true;
continue;
}
}
}
os << "{ ";
os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime());
os << ", \"numRes\": " << Results->NumResults;
os << ", \"diags\": " << Results->Diagnostics.size();
os << ", \"pch\": " << (usesPCH ? "true" : "false");
os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"';
const char *name = getlogin();
os << ", \"user\": \"" << (name ? name : "unknown") << '"';
os << ", \"clangVer\": \"" << getClangFullVersion() << '"';
os << " }";
llvm::StringRef res = os.str();
if (res.size() > 0) {
do {
// Setup the UDP socket.
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT);
if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER,
&servaddr.sin_addr) <= 0)
break;
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
break;
sendto(sockfd, res.data(), res.size(), 0,
(struct sockaddr *)&servaddr, sizeof(servaddr));
close(sockfd);
}
while (false);
}
#endif
#endif
return Results;
}
} // end extern "C"
namespace clang {
// FIXME: defined in CodeCompleteConsumer.cpp, but should be a
// static function here.
CXCursorKind
getCursorKindForCompletionResult(const CodeCompleteConsumer::Result &R);
}
namespace {
class CaptureCompletionResults : public CodeCompleteConsumer {
AllocatedCXCodeCompleteResults &AllocatedResults;
public:
explicit CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results)
: CodeCompleteConsumer(true, false, false), AllocatedResults(Results) { }
virtual void ProcessCodeCompleteResults(Sema &S, Result *Results,
unsigned NumResults) {
AllocatedResults.Results = new CXCompletionResult [NumResults];
AllocatedResults.NumResults = NumResults;
for (unsigned I = 0; I != NumResults; ++I) {
CXStoredCodeCompletionString *StoredCompletion
= new CXStoredCodeCompletionString(Results[I].Priority);
(void)Results[I].CreateCodeCompletionString(S, StoredCompletion);
AllocatedResults.Results[I].CursorKind
= getCursorKindForCompletionResult(Results[I]);
AllocatedResults.Results[I].CompletionString = StoredCompletion;
}
}
};
}
extern "C" {
CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU,
const char *complete_filename,
unsigned complete_line,
unsigned complete_column,
struct CXUnsavedFile *unsaved_files,
unsigned num_unsaved_files) {
#ifdef UDP_CODE_COMPLETION_LOGGER
#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime();
#endif
#endif
bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0;
ASTUnit *AST = static_cast<ASTUnit *>(TU);
if (!AST)
return 0;
// Perform the remapping of source files.
llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles;
for (unsigned I = 0; I != num_unsaved_files; ++I) {
llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length);
const llvm::MemoryBuffer *Buffer
= llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename,
Buffer));
}
if (EnableLogging) {
// FIXME: Add logging.
}
// Parse the resulting source file to find code-completion results.
AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults;
Results->Results = 0;
Results->NumResults = 0;
Results->Buffer = 0;
// Create a code-completion consumer to capture the results.
CaptureCompletionResults Capture(*Results);
// Perform completion.
AST->CodeComplete(complete_filename, complete_line, complete_column,
RemappedFiles.data(), RemappedFiles.size(), Capture,
*Results->Diag, Results->LangOpts, Results->SourceMgr,
Results->FileMgr, Results->Diagnostics);
#ifdef UDP_CODE_COMPLETION_LOGGER
#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime();

View File

@ -1,6 +1,7 @@
_clang_CXXMethod_isStatic
_clang_annotateTokens
_clang_codeComplete
_clang_codeCompleteAt
_clang_codeCompleteGetDiagnostic
_clang_codeCompleteGetNumDiagnostics
_clang_constructUSR_ObjCCategory

View File

@ -1,6 +1,7 @@
clang_CXXMethod_isStatic
clang_annotateTokens
clang_codeComplete
clang_codeCompleteAt
clang_codeCompleteGetDiagnostic
clang_codeCompleteGetNumDiagnostics
clang_constructUSR_ObjCCategory