Switch the clang-to-CIndex interface for code-completion to a binary format, for a massive speedup

llvm-svn: 90209
This commit is contained in:
Douglas Gregor 2009-12-01 05:55:20 +00:00
parent c377c81dcf
commit f09935f16c
6 changed files with 239 additions and 438 deletions

View File

@ -17,6 +17,8 @@ def err_fe_invalid_ast_file : Error<"invalid AST file: '%0'">, DefaultFatal;
def err_fe_invalid_ast_action : Error<"invalid action for AST input">, DefaultFatal;
def err_fe_invalid_code_complete_file
: Error<"cannot locate code-completion file %0">, DefaultFatal;
def err_fe_stdout_binary : Error<"unable to change standard output to binary">,
DefaultFatal;
def err_fe_dependency_file_requires_MT : Error<
"-dependency-file requires at least one -MT option">;
def err_fe_incompatible_options : Error<

View File

@ -209,7 +209,8 @@ public:
void Serialize(llvm::raw_ostream &OS) const;
/// \brief Deserialize a code-completion string from the given string.
static CodeCompletionString *Deserialize(llvm::StringRef &Str);
static CodeCompletionString *Deserialize(const char *&Str,
const char *StrEnd);
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
@ -221,6 +222,10 @@ class CodeCompleteConsumer {
protected:
/// \brief Whether to include macros in the code-completion results.
bool IncludeMacros;
/// \brief Whether the output format for the code-completion consumer is
/// binary.
bool OutputIsBinary;
public:
/// \brief Captures a result of code completion.
@ -394,17 +399,20 @@ public:
Sema &S) const;
};
CodeCompleteConsumer() : IncludeMacros(false) { }
CodeCompleteConsumer() : IncludeMacros(false), OutputIsBinary(false) { }
explicit CodeCompleteConsumer(bool IncludeMacros)
: IncludeMacros(IncludeMacros) { }
CodeCompleteConsumer(bool IncludeMacros, bool OutputIsBinary)
: IncludeMacros(IncludeMacros), OutputIsBinary(OutputIsBinary) { }
/// \brief Whether the code-completion consumer wants to see macros.
bool includeMacros() const { return IncludeMacros; }
/// \brief Determine whether the output of this consumer is binary.
bool isOutputBinary() const { return OutputIsBinary; }
/// \brief Deregisters and destroys this code-completion consumer.
virtual ~CodeCompleteConsumer();
/// \name Code-completion callbacks
//@{
/// \brief Process the finalized code-completion results.
@ -436,7 +444,7 @@ public:
/// results to the given raw output stream.
PrintingCodeCompleteConsumer(bool IncludeMacros,
llvm::raw_ostream &OS)
: CodeCompleteConsumer(IncludeMacros), OS(OS) { }
: CodeCompleteConsumer(IncludeMacros, false), OS(OS) { }
/// \brief Prints the finalized code-completion results.
virtual void ProcessCodeCompleteResults(Sema &S, Result *Results,
@ -458,7 +466,7 @@ public:
/// results to the given raw output stream in a format readable to the CIndex
/// library.
CIndexCodeCompleteConsumer(bool IncludeMacros, llvm::raw_ostream &OS)
: CodeCompleteConsumer(IncludeMacros), OS(OS) { }
: CodeCompleteConsumer(IncludeMacros, true), OS(OS) { }
/// \brief Prints the finalized code-completion results.
virtual void ProcessCodeCompleteResults(Sema &S, Result *Results,

View File

@ -29,6 +29,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Timer.h"
#include "llvm/System/Path.h"
#include "llvm/System/Program.h"
using namespace clang;
CompilerInstance::CompilerInstance(llvm::LLVMContext *_LLVMContext,
@ -256,6 +257,12 @@ void CompilerInstance::createCodeCompletionConsumer() {
getFrontendOpts().DebugCodeCompletionPrinter,
getFrontendOpts().ShowMacrosInCodeCompletion,
llvm::outs()));
if (CompletionConsumer->isOutputBinary() &&
llvm::sys::Program::ChangeStdoutToBinary()) {
getPreprocessor().getDiagnostics().Report(diag::err_fe_stdout_binary);
CompletionConsumer.reset();
}
}
void CompilerInstance::createFrontendTimer() {

View File

@ -14,6 +14,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/Parse/Scope.h"
#include "clang/Lex/Preprocessor.h"
#include "clang-c/Index.h"
#include "Sema.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
@ -209,306 +210,111 @@ CodeCompletionString *CodeCompletionString::Clone() const {
return Result;
}
namespace {
// Escape a string for XML-like formatting.
struct EscapedString {
EscapedString(llvm::StringRef Str) : Str(Str) { }
llvm::StringRef Str;
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, EscapedString EStr) {
llvm::StringRef Str = EStr.Str;
while (!Str.empty()) {
// Find the next escaped character.
llvm::StringRef::size_type Pos = Str.find_first_of("<>&\"'");
// Print everything before that escaped character.
OS << Str.substr(0, Pos);
static void WriteUnsigned(llvm::raw_ostream &OS, unsigned Value) {
OS.write((const char *)&Value, sizeof(unsigned));
}
// If we didn't find any escaped characters, we're done.
if (Pos == llvm::StringRef::npos)
break;
// Print the appropriate escape sequence.
switch (Str[Pos]) {
case '<': OS << "&lt;"; break;
case '>': OS << "&gt;"; break;
case '&': OS << "&amp;"; break;
case '"': OS << "&quot;"; break;
case '\'': OS << "&apos;"; break;
}
// Remove everything up to and including that escaped character.
Str = Str.substr(Pos + 1);
}
return OS;
}
/// \brief Remove XML-like escaping from a string.
std::string UnescapeString(llvm::StringRef Str) {
using llvm::StringRef;
std::string Result;
llvm::raw_string_ostream OS(Result);
while (!Str.empty()) {
StringRef::size_type Amp = Str.find('&');
OS << Str.substr(0, Amp);
if (Amp == StringRef::npos)
break;
StringRef::size_type Semi = Str.substr(Amp).find(';');
if (Semi == StringRef::npos) {
// Malformed input; do the best we can.
OS << '&';
Str = Str.substr(Amp + 1);
continue;
}
char Unescaped = llvm::StringSwitch<char>(Str.substr(Amp + 1, Semi - 1))
.Case("lt", '<')
.Case("gt", '>')
.Case("amp", '&')
.Case("quot", '"')
.Case("apos", '\'')
.Default('\0');
if (Unescaped)
OS << Unescaped;
else
OS << Str.substr(Amp, Semi + 1);
Str = Str.substr(Amp + Semi + 1);
}
return OS.str();
}
static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd,
unsigned &Value) {
if (Memory + sizeof(unsigned) > MemoryEnd)
return true;
memmove(&Value, Memory, sizeof(unsigned));
Memory += sizeof(unsigned);
return false;
}
void CodeCompletionString::Serialize(llvm::raw_ostream &OS) const {
// Write the number of chunks.
WriteUnsigned(OS, size());
for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) {
WriteUnsigned(OS, C->Kind);
switch (C->Kind) {
case CK_TypedText:
OS << "<typed-text>" << EscapedString(C->Text) << "</>";
break;
case CK_Text:
OS << "<text>" << EscapedString(C->Text) << "</>";
break;
case CK_Optional:
OS << "<optional>";
C->Optional->Serialize(OS);
OS << "</>";
break;
case CK_Placeholder:
OS << "<placeholder>" << EscapedString(C->Text) << "</>";
break;
case CK_Informative:
OS << "<informative>" << EscapedString(C->Text) << "</>";
case CK_CurrentParameter: {
const char *Text = C->Text;
unsigned StrLen = strlen(Text);
WriteUnsigned(OS, StrLen);
OS.write(Text, StrLen);
break;
case CK_CurrentParameter:
OS << "<current-parameter>" << EscapedString(C->Text) << "</>";
}
case CK_Optional:
C->Optional->Serialize(OS);
break;
case CK_LeftParen:
OS << "<lparen/>";
break;
case CK_RightParen:
OS << "<rparen/>";
break;
case CK_LeftBracket:
OS << "<lbracket/>";
break;
case CK_RightBracket:
OS << "<rbracket/>";
break;
case CK_LeftBrace:
OS << "<lbrace/>";
break;
case CK_RightBrace:
OS << "<rbrace/>";
break;
case CK_LeftAngle:
OS << "<langle/>";
break;
case CK_RightAngle:
OS << "<rangle/>";
break;
case CK_Comma:
OS << "<comma/>";
break;
}
}
}
}
/// \brief Parse the next XML-ish tag of the form <blah>.
///
/// \param Str the string in which we're looking for the next tag.
///
/// \param TagPos if successful, will be set to the start of the tag we found.
///
/// \param Standalone will indicate whether this is a "standalone" tag that
/// has no associated data, e.g., <comma/>.
///
/// \param Terminator will indicate whether this is a terminating tag (that is
/// or starts with '/').
///
/// \returns the tag itself, without the angle brackets.
static llvm::StringRef ParseNextTag(llvm::StringRef Str,
llvm::StringRef::size_type &StartTag,
llvm::StringRef::size_type &AfterTag,
bool &Standalone, bool &Terminator) {
using llvm::StringRef;
Standalone = false;
Terminator = false;
AfterTag = StringRef::npos;
// Find the starting '<'.
StartTag = Str.find('<');
if (StartTag == StringRef::npos)
return llvm::StringRef();
// Find the corresponding '>'.
llvm::StringRef::size_type EndTag = Str.substr(StartTag).find('>');
if (EndTag == StringRef::npos)
return llvm::StringRef();
AfterTag = StartTag + EndTag + 1;
// Determine whether this is a terminating tag.
if (Str[StartTag + 1] == '/') {
Terminator = true;
Str = Str.substr(1);
--EndTag;
}
// Determine whether this is a standalone tag.
if (!Terminator && Str[StartTag + EndTag - 1] == '/') {
Standalone = true;
if (EndTag > 1)
--EndTag;
}
CodeCompletionString *CodeCompletionString::Deserialize(const char *&Str,
const char *StrEnd) {
if (Str == StrEnd || *Str == 0)
return 0;
return Str.substr(StartTag + 1, EndTag - 1);
}
CodeCompletionString *CodeCompletionString::Deserialize(llvm::StringRef &Str) {
using llvm::StringRef;
CodeCompletionString *Result = new CodeCompletionString;
do {
// Parse the next tag.
StringRef::size_type StartTag, AfterTag;
bool Standalone, Terminator;
StringRef Tag = ParseNextTag(Str, StartTag, AfterTag, Standalone,
Terminator);
if (StartTag == StringRef::npos)
break;
// Figure out what kind of chunk we have.
const unsigned UnknownKind = 10000;
unsigned Kind = llvm::StringSwitch<unsigned>(Tag)
.Case("typed-text", CK_TypedText)
.Case("text", CK_Text)
.Case("optional", CK_Optional)
.Case("placeholder", CK_Placeholder)
.Case("informative", CK_Informative)
.Case("current-parameter", CK_CurrentParameter)
.Case("lparen", CK_LeftParen)
.Case("rparen", CK_RightParen)
.Case("lbracket", CK_LeftBracket)
.Case("rbracket", CK_RightBracket)
.Case("lbrace", CK_LeftBrace)
.Case("rbrace", CK_RightBrace)
.Case("langle", CK_LeftAngle)
.Case("rangle", CK_RightAngle)
.Case("comma", CK_Comma)
.Default(UnknownKind);
// If we've hit a terminator tag, we're done.
if (Terminator)
break;
// Consume the tag.
Str = Str.substr(AfterTag);
unsigned NumBlocks;
if (ReadUnsigned(Str, StrEnd, NumBlocks))
return Result;
// Handle standalone tags now, since they don't need to be matched to
// anything.
if (Standalone) {
// Ignore anything we don't know about.
if (Kind == UnknownKind)
continue;
switch ((ChunkKind)Kind) {
case CK_TypedText:
case CK_Text:
case CK_Optional:
case CK_Placeholder:
case CK_Informative:
case CK_CurrentParameter:
// There is no point in creating empty chunks of these kinds.
break;
case CK_LeftParen:
case CK_RightParen:
case CK_LeftBracket:
case CK_RightBracket:
case CK_LeftBrace:
case CK_RightBrace:
case CK_LeftAngle:
case CK_RightAngle:
case CK_Comma:
Result->AddChunk(Chunk((ChunkKind)Kind));
break;
}
continue;
for (unsigned I = 0; I != NumBlocks; ++I) {
if (Str + 1 >= StrEnd)
break;
// Parse the next kind.
unsigned KindValue;
if (ReadUnsigned(Str, StrEnd, KindValue))
return Result;
switch (ChunkKind Kind = (ChunkKind)KindValue) {
case CK_TypedText:
case CK_Text:
case CK_Placeholder:
case CK_Informative:
case CK_CurrentParameter: {
unsigned StrLen;
if (ReadUnsigned(Str, StrEnd, StrLen) || (Str + StrLen > StrEnd))
return Result;
Result->AddChunk(Chunk(Kind, StringRef(Str, StrLen)));
Str += StrLen;
break;
}
if (Kind == CK_Optional) {
// Deserialize the optional code-completion string.
std::auto_ptr<CodeCompletionString> Optional(Deserialize(Str));
case CK_Optional: {
std::auto_ptr<CodeCompletionString> Optional(Deserialize(Str, StrEnd));
Result->AddOptionalChunk(Optional);
break;
}
StringRef EndTag = ParseNextTag(Str, StartTag, AfterTag, Standalone,
Terminator);
if (StartTag == StringRef::npos || !Terminator || Standalone)
break; // Parsing failed; just give up.
if (EndTag.empty() || Tag == EndTag) {
// Found the matching end tag. Add this chunk based on the text
// between the tags, then consume that input.
StringRef Text = Str.substr(0, StartTag);
switch ((ChunkKind)Kind) {
case CK_TypedText:
case CK_Text:
case CK_Placeholder:
case CK_Informative:
case CK_CurrentParameter:
case CK_LeftParen:
case CK_RightParen:
case CK_LeftBracket:
case CK_RightBracket:
case CK_LeftBrace:
case CK_RightBrace:
case CK_LeftAngle:
case CK_RightAngle:
case CK_Comma:
Result->AddChunk(Chunk((ChunkKind)Kind, UnescapeString(Text)));
break;
case CK_Optional:
// We've already added the optional chunk.
break;
}
case CK_LeftParen:
case CK_RightParen:
case CK_LeftBracket:
case CK_RightBracket:
case CK_LeftBrace:
case CK_RightBrace:
case CK_LeftAngle:
case CK_RightAngle:
case CK_Comma:
Result->AddChunk(Chunk(Kind));
break;
}
// Remove this tag.
Str = Str.substr(AfterTag);
} while (!Str.empty());
};
return Result;
}
@ -632,62 +438,110 @@ CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
unsigned NumResults) {
// Print the results.
for (unsigned I = 0; I != NumResults; ++I) {
OS << "COMPLETION:" << Results[I].Rank << ":";
CXCursorKind Kind = CXCursor_NotImplemented;
switch (Results[I].Kind) {
case Result::RK_Declaration:
if (RecordDecl *Record = dyn_cast<RecordDecl>(Results[I].Declaration)) {
if (Record->isStruct())
OS << "Struct:";
else if (Record->isUnion())
OS << "Union:";
else
OS << "Class:";
} else if (ObjCMethodDecl *Method
= dyn_cast<ObjCMethodDecl>(Results[I].Declaration)) {
if (Method->isInstanceMethod())
OS << "ObjCInstanceMethod:";
else
OS << "ObjCClassMethod:";
} else {
OS << Results[I].Declaration->getDeclKindName() << ":";
}
if (CodeCompletionString *CCS
= Results[I].CreateCodeCompletionString(SemaRef)) {
CCS->Serialize(OS);
delete CCS;
} else {
OS << "<typed-text>"
<< Results[I].Declaration->getNameAsString()
<< "</>";
}
OS << '\n';
break;
case Result::RK_Keyword:
OS << "Keyword:<typed-text>" << Results[I].Keyword << "</>\n";
break;
case Result::RK_Macro: {
OS << "Macro:";
if (CodeCompletionString *CCS
= Results[I].CreateCodeCompletionString(SemaRef)) {
CCS->Serialize(OS);
delete CCS;
} else {
OS << "<typed-text>" << Results[I].Macro->getName() << "</>";
}
OS << '\n';
case Result::RK_Declaration:
switch (Results[I].Declaration->getKind()) {
case Decl::Record:
case Decl::CXXRecord:
case Decl::ClassTemplateSpecialization: {
RecordDecl *Record = cast<RecordDecl>(Results[I].Declaration);
if (Record->isStruct())
Kind = CXCursor_StructDecl;
else if (Record->isUnion())
Kind = CXCursor_UnionDecl;
else
Kind = CXCursor_ClassDecl;
break;
}
case Result::RK_Pattern: {
OS << "Pattern:";
Results[I].Pattern->Serialize(OS);
OS << '\n';
case Decl::ObjCMethod: {
ObjCMethodDecl *Method = cast<ObjCMethodDecl>(Results[I].Declaration);
if (Method->isInstanceMethod())
Kind = CXCursor_ObjCInstanceMethodDecl;
else
Kind = CXCursor_ObjCClassMethodDecl;
break;
}
case Decl::Typedef:
Kind = CXCursor_TypedefDecl;
break;
case Decl::Enum:
Kind = CXCursor_EnumDecl;
break;
case Decl::Field:
Kind = CXCursor_FieldDecl;
break;
case Decl::EnumConstant:
Kind = CXCursor_EnumConstantDecl;
break;
case Decl::Function:
case Decl::CXXMethod:
case Decl::CXXConstructor:
case Decl::CXXDestructor:
case Decl::CXXConversion:
Kind = CXCursor_FunctionDecl;
break;
case Decl::Var:
Kind = CXCursor_VarDecl;
break;
case Decl::ParmVar:
Kind = CXCursor_ParmDecl;
break;
case Decl::ObjCInterface:
Kind = CXCursor_ObjCInterfaceDecl;
break;
case Decl::ObjCCategory:
Kind = CXCursor_ObjCCategoryDecl;
break;
case Decl::ObjCProtocol:
Kind = CXCursor_ObjCProtocolDecl;
break;
case Decl::ObjCProperty:
Kind = CXCursor_ObjCPropertyDecl;
break;
case Decl::ObjCIvar:
Kind = CXCursor_ObjCIvarDecl;
break;
case Decl::ObjCImplementation:
Kind = CXCursor_ObjCClassDefn;
break;
case Decl::ObjCCategoryImpl:
Kind = CXCursor_ObjCCategoryDefn;
break;
default:
break;
}
break;
case Result::RK_Keyword:
case Result::RK_Macro:
case Result::RK_Pattern:
Kind = CXCursor_NotImplemented;
break;
}
WriteUnsigned(OS, Kind);
CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef);
assert(CCS && "No code-completion string?");
CCS->Serialize(OS);
delete CCS;
}
// Once we've printed the code-completion results, suppress remaining
@ -702,13 +556,12 @@ CIndexCodeCompleteConsumer::ProcessOverloadCandidates(Sema &SemaRef,
OverloadCandidate *Candidates,
unsigned NumCandidates) {
for (unsigned I = 0; I != NumCandidates; ++I) {
if (CodeCompletionString *CCS
= Candidates[I].CreateSignatureString(CurrentArg, SemaRef)) {
OS << "OVERLOAD:";
CCS->Serialize(OS);
OS << '\n';
delete CCS;
}
WriteUnsigned(OS, CXCursor_NotImplemented);
CodeCompletionString *CCS
= Candidates[I].CreateSignatureString(CurrentArg, SemaRef);
assert(CCS && "No code-completion string?");
CCS->Serialize(OS);
delete CCS;
}
// Once we've printed the code-completion results, suppress remaining

View File

@ -801,17 +801,26 @@ CodeCompletionString *
CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
typedef CodeCompletionString::Chunk Chunk;
if (Kind == RK_Keyword)
return 0;
if (Kind == RK_Pattern)
return Pattern->Clone();
CodeCompletionString *Result = new CodeCompletionString;
if (Kind == RK_Keyword) {
Result->AddTypedTextChunk(Keyword);
return Result;
}
if (Kind == RK_Macro) {
MacroInfo *MI = S.PP.getMacroInfo(Macro);
if (!MI || !MI->isFunctionLike())
return 0;
assert(MI && "Not a macro?");
Result->AddTypedTextChunk(Macro->getName());
if (!MI->isFunctionLike())
return Result;
// Format a function-like macro with placeholders for the arguments.
CodeCompletionString *Result = new CodeCompletionString;
Result->AddTypedTextChunk(Macro->getName());
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftParen));
for (MacroInfo::arg_iterator A = MI->arg_begin(), AEnd = MI->arg_end();
A != AEnd; ++A) {
@ -843,14 +852,12 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
NamedDecl *ND = Declaration;
if (StartsNestedNameSpecifier) {
CodeCompletionString *Result = new CodeCompletionString;
Result->AddTypedTextChunk(ND->getNameAsString());
Result->AddTextChunk("::");
return Result;
}
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
Result->AddTypedTextChunk(Function->getNameAsString());
@ -861,7 +868,6 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
}
if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
FunctionDecl *Function = FunTmpl->getTemplatedDecl();
@ -915,7 +921,6 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
}
if (TemplateDecl *Template = dyn_cast<TemplateDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
Result->AddTypedTextChunk(Template->getNameAsString());
@ -926,7 +931,6 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
}
if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
Selector Sel = Method->getSelector();
if (Sel.isUnarySelector()) {
Result->AddTypedTextChunk(Sel.getIdentifierInfoForSlot(0)->getName());
@ -982,15 +986,12 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
return Result;
}
if (Qualifier) {
CodeCompletionString *Result = new CodeCompletionString;
if (Qualifier)
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
Result->AddTypedTextChunk(ND->getNameAsString());
return Result;
}
return 0;
Result->AddTypedTextChunk(ND->getNameAsString());
return Result;
}
CodeCompletionString *

View File

@ -24,7 +24,6 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/ASTUnit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Config/config.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
@ -1128,31 +1127,14 @@ unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) {
return CCStr? CCStr->size() : 0;
}
static CXCursorKind parseResultKind(llvm::StringRef Str) {
return llvm::StringSwitch<CXCursorKind>(Str)
.Case("Typedef", CXCursor_TypedefDecl)
.Case("Struct", CXCursor_StructDecl)
.Case("Union", CXCursor_UnionDecl)
.Case("Class", CXCursor_ClassDecl)
.Case("Enum", CXCursor_EnumDecl)
.Case("Field", CXCursor_FieldDecl)
.Case("EnumConstant", CXCursor_EnumConstantDecl)
.Case("Function", CXCursor_FunctionDecl)
// FIXME: Hacks here to make C++ member functions look like C functions
.Case("CXXMethod", CXCursor_FunctionDecl)
.Case("CXXConstructor", CXCursor_FunctionDecl)
.Case("CXXDestructor", CXCursor_FunctionDecl)
.Case("CXXConversion", CXCursor_FunctionDecl)
.Case("Var", CXCursor_VarDecl)
.Case("ParmVar", CXCursor_ParmDecl)
.Case("ObjCInterface", CXCursor_ObjCInterfaceDecl)
.Case("ObjCCategory", CXCursor_ObjCCategoryDecl)
.Case("ObjCProtocol", CXCursor_ObjCProtocolDecl)
.Case("ObjCProperty", CXCursor_ObjCPropertyDecl)
.Case("ObjCIvar", CXCursor_ObjCIvarDecl)
.Case("ObjCInstanceMethod", CXCursor_ObjCInstanceMethodDecl)
.Case("ObjCClassMethod", CXCursor_ObjCClassMethodDecl)
.Default(CXCursor_NotImplemented);
static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd,
unsigned &Value) {
if (Memory + sizeof(unsigned) > MemoryEnd)
return true;
memmove(&Value, Memory, sizeof(unsigned));
Memory += sizeof(unsigned);
return false;
}
void clang_codeComplete(CXIndex CIdx,
@ -1248,80 +1230,28 @@ void clang_codeComplete(CXIndex CIdx,
using llvm::StringRef;
if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) {
StringRef Buffer = F->getBuffer();
do {
StringRef::size_type CompletionIdx = Buffer.find("COMPLETION:");
StringRef::size_type OverloadIdx = Buffer.find("OVERLOAD:");
if (CompletionIdx == StringRef::npos && OverloadIdx == StringRef::npos)
for (const char *Str = Buffer.data(), *StrEnd = Str + Buffer.size();
Str < StrEnd;) {
unsigned KindValue;
if (ReadUnsigned(Str, StrEnd, KindValue))
break;
if (OverloadIdx < CompletionIdx) {
// Parse an overload result.
Buffer = Buffer.substr(OverloadIdx);
// Skip past the OVERLOAD:
Buffer = Buffer.substr(Buffer.find(':') + 1);
// Find the entire completion string.
StringRef::size_type EOL = Buffer.find_first_of("\n\r");
if (EOL == StringRef::npos)
continue;
StringRef Line = Buffer.substr(0, EOL);
Buffer = Buffer.substr(EOL + 1);
CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line);
if (!CCStr || CCStr->empty())
continue;
CodeCompletionString *CCStr
= CodeCompletionString::Deserialize(Str, StrEnd);
if (!CCStr)
continue;
if (!CCStr->empty()) {
// Vend the code-completion result to the caller.
CXCompletionResult Result;
Result.CursorKind = CXCursor_NotImplemented;
Result.CursorKind = (CXCursorKind)KindValue;
Result.CompletionString = CCStr;
if (completion_iterator)
completion_iterator(&Result, client_data);
delete CCStr;
continue;
}
// Parse a completion result.
Buffer = Buffer.substr(CompletionIdx);
// Skip past the COMPLETION:
Buffer = Buffer.substr(Buffer.find(':') + 1);
// Get the rank
unsigned Rank = 0;
StringRef::size_type AfterRank = Buffer.find(':');
Buffer.substr(0, AfterRank).getAsInteger(10, Rank);
Buffer = Buffer.substr(AfterRank + 1);
// Get the kind of result.
StringRef::size_type AfterKind = Buffer.find(':');
StringRef Kind = Buffer.substr(0, AfterKind);
Buffer = Buffer.substr(AfterKind + 1);
// Skip over any whitespace.
Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
// Find the entire completion string.
StringRef::size_type EOL = Buffer.find_first_of("\n\r");
if (EOL == StringRef::npos)
continue;
StringRef Line = Buffer.substr(0, EOL);
Buffer = Buffer.substr(EOL + 1);
CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line);
if (!CCStr || CCStr->empty())
continue;
// Vend the code-completion result to the caller.
CXCompletionResult Result;
Result.CursorKind = parseResultKind(Kind);
Result.CompletionString = CCStr;
if (completion_iterator)
completion_iterator(&Result, client_data);
delete CCStr;
} while (true);
};
delete F;
}