From 2b4074f1d3919e77cb33ca49c960521dea27afab Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 1 Dec 2009 05:55:20 +0000 Subject: [PATCH] Switch the clang-to-CIndex interface for code-completion to a binary format, for a massive speedup git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90209 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../clang/Basic/DiagnosticFrontendKinds.td | 2 + include/clang/Sema/CodeCompleteConsumer.h | 22 +- lib/Frontend/CompilerInstance.cpp | 7 + lib/Sema/CodeCompleteConsumer.cpp | 491 ++++++------------ lib/Sema/SemaCodeComplete.cpp | 37 +- tools/CIndex/CIndex.cpp | 108 +--- 6 files changed, 234 insertions(+), 433 deletions(-) diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index e9b351ffe9..cbc287c58c 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -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< diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index ed747613c3..84c179f789 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -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, diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 1cb5993fae..1083d5ef1c 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -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() { diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index b0a7a664f5..91b16d3774 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -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 << "<"; break; - case '>': OS << ">"; break; - case '&': OS << "&"; break; - case '"': OS << """; break; - case '\'': OS << "'"; 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(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 << "" << EscapedString(C->Text) << ""; - break; case CK_Text: - OS << "" << EscapedString(C->Text) << ""; - break; - case CK_Optional: - OS << ""; - C->Optional->Serialize(OS); - OS << ""; - break; case CK_Placeholder: - OS << "" << EscapedString(C->Text) << ""; - break; case CK_Informative: - OS << "" << 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 << "" << EscapedString(C->Text) << ""; + } + + case CK_Optional: + C->Optional->Serialize(OS); break; + case CK_LeftParen: - OS << ""; - break; case CK_RightParen: - OS << ""; - break; case CK_LeftBracket: - OS << ""; - break; case CK_RightBracket: - OS << ""; - break; case CK_LeftBrace: - OS << ""; - break; case CK_RightBrace: - OS << ""; - break; case CK_LeftAngle: - OS << ""; - break; case CK_RightAngle: - OS << ""; - break; case CK_Comma: - OS << ""; break; - } + } } } -/// \brief Parse the next XML-ish tag of the form . -/// -/// \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., . -/// -/// \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; - } - - return Str.substr(StartTag + 1, EndTag - 1); -} +CodeCompletionString *CodeCompletionString::Deserialize(const char *&Str, + const char *StrEnd) { + if (Str == StrEnd || *Str == 0) + return 0; -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) + unsigned NumBlocks; + if (ReadUnsigned(Str, StrEnd, NumBlocks)) + return Result; + + for (unsigned I = 0; I != NumBlocks; ++I) { + if (Str + 1 >= StrEnd) break; - - // Figure out what kind of chunk we have. - const unsigned UnknownKind = 10000; - unsigned Kind = llvm::StringSwitch(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) + + // 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; - - // Consume the tag. - Str = Str.substr(AfterTag); - - // 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; } - - if (Kind == CK_Optional) { - // Deserialize the optional code-completion string. - std::auto_ptr Optional(Deserialize(Str)); + + case CK_Optional: { + std::auto_ptr 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(Results[I].Declaration)) { - if (Record->isStruct()) - OS << "Struct:"; - else if (Record->isUnion()) - OS << "Union:"; - else - OS << "Class:"; - } else if (ObjCMethodDecl *Method - = dyn_cast(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 << "" - << Results[I].Declaration->getNameAsString() - << ""; - } + case Result::RK_Declaration: + switch (Results[I].Declaration->getKind()) { + case Decl::Record: + case Decl::CXXRecord: + case Decl::ClassTemplateSpecialization: { + RecordDecl *Record = cast(Results[I].Declaration); + if (Record->isStruct()) + Kind = CXCursor_StructDecl; + else if (Record->isUnion()) + Kind = CXCursor_UnionDecl; + else + Kind = CXCursor_ClassDecl; + break; + } + + case Decl::ObjCMethod: { + ObjCMethodDecl *Method = cast(Results[I].Declaration); + if (Method->isInstanceMethod()) + Kind = CXCursor_ObjCInstanceMethodDecl; + else + Kind = CXCursor_ObjCClassMethodDecl; + break; + } - OS << '\n'; + case Decl::Typedef: + Kind = CXCursor_TypedefDecl; break; - case Result::RK_Keyword: - OS << "Keyword:" << Results[I].Keyword << "\n"; + case Decl::Enum: + Kind = CXCursor_EnumDecl; break; - case Result::RK_Macro: { - OS << "Macro:"; - if (CodeCompletionString *CCS - = Results[I].CreateCodeCompletionString(SemaRef)) { - CCS->Serialize(OS); - delete CCS; - } else { - OS << "" << Results[I].Macro->getName() << ""; - } - OS << '\n'; + 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 Result::RK_Pattern: { - OS << "Pattern:"; - Results[I].Pattern->Serialize(OS); - OS << '\n'; + 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 diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index e1a5790a8b..b386adb9df 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -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(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(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(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(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 * diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index 1f456eb24f..4681b939ce 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -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(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; } -- 2.40.0