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:
parent
988bfce174
commit
8e984da800
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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?");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
_clang_CXXMethod_isStatic
|
||||
_clang_annotateTokens
|
||||
_clang_codeComplete
|
||||
_clang_codeCompleteAt
|
||||
_clang_codeCompleteGetDiagnostic
|
||||
_clang_codeCompleteGetNumDiagnostics
|
||||
_clang_constructUSR_ObjCCategory
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
clang_CXXMethod_isStatic
|
||||
clang_annotateTokens
|
||||
clang_codeComplete
|
||||
clang_codeCompleteAt
|
||||
clang_codeCompleteGetDiagnostic
|
||||
clang_codeCompleteGetNumDiagnostics
|
||||
clang_constructUSR_ObjCCategory
|
||||
|
|
Loading…
Reference in New Issue