//===--- CodeCompleteConsumer.cpp - Code Completion Interface ---*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the CodeCompleteConsumer class. // //===----------------------------------------------------------------------===// #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/AST/DeclCXX.h" #include "clang/Parse/Scope.h" #include "clang/Lex/Preprocessor.h" #include "Sema.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace clang; using llvm::StringRef; //===----------------------------------------------------------------------===// // Code completion string implementation //===----------------------------------------------------------------------===// CodeCompletionString::Chunk::Chunk(ChunkKind Kind, llvm::StringRef Text) : Kind(Kind), Text("") { switch (Kind) { case CK_TypedText: case CK_Text: case CK_Placeholder: case CK_Informative: case CK_CurrentParameter: { char *New = new char [Text.size() + 1]; std::memcpy(New, Text.data(), Text.size()); New[Text.size()] = '\0'; this->Text = New; break; } case CK_Optional: llvm::llvm_unreachable("Optional strings cannot be created from text"); break; case CK_LeftParen: this->Text = "("; break; case CK_RightParen: this->Text = ")"; break; case CK_LeftBracket: this->Text = "["; break; case CK_RightBracket: this->Text = "]"; break; case CK_LeftBrace: this->Text = "{"; break; case CK_RightBrace: this->Text = "}"; break; case CK_LeftAngle: this->Text = "<"; break; case CK_RightAngle: this->Text = ">"; break; case CK_Comma: this->Text = ", "; break; } } CodeCompletionString::Chunk CodeCompletionString::Chunk::CreateText(StringRef Text) { return Chunk(CK_Text, Text); } CodeCompletionString::Chunk CodeCompletionString::Chunk::CreateOptional( std::auto_ptr Optional) { Chunk Result; Result.Kind = CK_Optional; Result.Optional = Optional.release(); return Result; } CodeCompletionString::Chunk CodeCompletionString::Chunk::CreatePlaceholder(StringRef Placeholder) { return Chunk(CK_Placeholder, Placeholder); } CodeCompletionString::Chunk CodeCompletionString::Chunk::CreateInformative(StringRef Informative) { return Chunk(CK_Informative, Informative); } CodeCompletionString::Chunk CodeCompletionString::Chunk::CreateCurrentParameter( StringRef CurrentParameter) { return Chunk(CK_CurrentParameter, CurrentParameter); } CodeCompletionString::Chunk CodeCompletionString::Chunk::Clone() const { switch (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: return Chunk(Kind, Text); case CK_Optional: { std::auto_ptr Opt(Optional->Clone()); return CreateOptional(Opt); } } // Silence GCC warning. return Chunk(); } void CodeCompletionString::Chunk::Destroy() { switch (Kind) { case CK_Optional: delete Optional; break; case CK_TypedText: case CK_Text: case CK_Placeholder: case CK_Informative: case CK_CurrentParameter: delete [] Text; 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: break; } } CodeCompletionString::~CodeCompletionString() { std::for_each(Chunks.begin(), Chunks.end(), std::mem_fun_ref(&Chunk::Destroy)); } std::string CodeCompletionString::getAsString() const { std::string Result; llvm::raw_string_ostream OS(Result); for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) { switch (C->Kind) { case CK_Optional: OS << "{#" << C->Optional->getAsString() << "#}"; break; case CK_Placeholder: OS << "<#" << C->Text << "#>"; break; case CK_Informative: OS << "[#" << C->Text << "#]"; break; case CK_CurrentParameter: OS << "<#" << C->Text << "#>"; break; default: OS << C->Text; break; } } OS.flush(); return Result; } const char *CodeCompletionString::getTypedText() const { for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) if (C->Kind == CK_TypedText) return C->Text; return 0; } CodeCompletionString *CodeCompletionString::Clone() const { CodeCompletionString *Result = new CodeCompletionString; for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) Result->AddChunk(C->Clone()); 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); // 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(); } } void CodeCompletionString::Serialize(llvm::raw_ostream &OS) const { for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) { 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) << ""; break; case CK_CurrentParameter: OS << "" << EscapedString(C->Text) << ""; 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(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(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); // 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)); Result->AddOptionalChunk(Optional); } 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; } } // Remove this tag. Str = Str.substr(AfterTag); } while (!Str.empty()); return Result; } void CodeCompleteConsumer::Result::Destroy() { if (Kind == RK_Pattern) { delete Pattern; Pattern = 0; } } //===----------------------------------------------------------------------===// // Code completion overload candidate implementation //===----------------------------------------------------------------------===// FunctionDecl * CodeCompleteConsumer::OverloadCandidate::getFunction() const { if (getKind() == CK_Function) return Function; else if (getKind() == CK_FunctionTemplate) return FunctionTemplate->getTemplatedDecl(); else return 0; } const FunctionType * CodeCompleteConsumer::OverloadCandidate::getFunctionType() const { switch (Kind) { case CK_Function: return Function->getType()->getAs(); case CK_FunctionTemplate: return FunctionTemplate->getTemplatedDecl()->getType() ->getAs(); case CK_FunctionType: return Type; } return 0; } //===----------------------------------------------------------------------===// // Code completion consumer implementation //===----------------------------------------------------------------------===// CodeCompleteConsumer::~CodeCompleteConsumer() { } void PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef, Result *Results, unsigned NumResults) { // Print the results. for (unsigned I = 0; I != NumResults; ++I) { OS << "COMPLETION: "; switch (Results[I].Kind) { case Result::RK_Declaration: OS << Results[I].Declaration->getNameAsString() << " : " << Results[I].Rank; if (Results[I].Hidden) OS << " (Hidden)"; if (CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef)) { OS << " : " << CCS->getAsString(); delete CCS; } OS << '\n'; break; case Result::RK_Keyword: OS << Results[I].Keyword << " : " << Results[I].Rank << '\n'; break; case Result::RK_Macro: { OS << Results[I].Macro->getName() << " : " << Results[I].Rank; if (CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef)) { OS << " : " << CCS->getAsString(); delete CCS; } OS << '\n'; break; } case Result::RK_Pattern: { OS << "Pattern : " << Results[I].Rank << " : " << Results[I].Pattern->getAsString() << '\n'; break; } } } // Once we've printed the code-completion results, suppress remaining // diagnostics. // FIXME: Move this somewhere else! SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics(); } void PrintingCodeCompleteConsumer::ProcessOverloadCandidates(Sema &SemaRef, unsigned CurrentArg, OverloadCandidate *Candidates, unsigned NumCandidates) { for (unsigned I = 0; I != NumCandidates; ++I) { if (CodeCompletionString *CCS = Candidates[I].CreateSignatureString(CurrentArg, SemaRef)) { OS << "OVERLOAD: " << CCS->getAsString() << "\n"; delete CCS; } } // Once we've printed the code-completion results, suppress remaining // diagnostics. // FIXME: Move this somewhere else! SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics(); } void CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef, Result *Results, unsigned NumResults) { // Print the results. for (unsigned I = 0; I != NumResults; ++I) { OS << "COMPLETION:" << Results[I].Rank << ":"; 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() << ""; } OS << '\n'; break; case Result::RK_Keyword: OS << "Keyword:" << 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 << "" << Results[I].Macro->getName() << ""; } OS << '\n'; break; } case Result::RK_Pattern: { OS << "Pattern:"; Results[I].Pattern->Serialize(OS); OS << '\n'; break; } } } // Once we've printed the code-completion results, suppress remaining // diagnostics. // FIXME: Move this somewhere else! SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics(); } void CIndexCodeCompleteConsumer::ProcessOverloadCandidates(Sema &SemaRef, unsigned CurrentArg, 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; } } // Once we've printed the code-completion results, suppress remaining // diagnostics. // FIXME: Move this somewhere else! SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics(); }