mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-02 19:06:06 +00:00

definitions, e.g., after - or - (id) we'll find all of the "likely" instance methods that one would want to declare or define at this point. In the latter case, we only produce results whose return types match "id". llvm-svn: 100587
628 lines
16 KiB
C++
628 lines
16 KiB
C++
//===--- 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 "clang-c/Index.h"
|
|
#include "Sema.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <functional>
|
|
|
|
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_ResultType:
|
|
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_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;
|
|
|
|
case CK_Colon:
|
|
this->Text = ":";
|
|
break;
|
|
|
|
case CK_SemiColon:
|
|
this->Text = ";";
|
|
break;
|
|
|
|
case CK_Equal:
|
|
this->Text = " = ";
|
|
break;
|
|
|
|
case CK_HorizontalSpace:
|
|
this->Text = " ";
|
|
break;
|
|
|
|
case CK_VerticalSpace:
|
|
this->Text = "\n";
|
|
break;
|
|
}
|
|
}
|
|
|
|
CodeCompletionString::Chunk
|
|
CodeCompletionString::Chunk::CreateText(StringRef Text) {
|
|
return Chunk(CK_Text, Text);
|
|
}
|
|
|
|
CodeCompletionString::Chunk
|
|
CodeCompletionString::Chunk::CreateOptional(
|
|
std::auto_ptr<CodeCompletionString> 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::CreateResultType(StringRef ResultType) {
|
|
return Chunk(CK_ResultType, ResultType);
|
|
}
|
|
|
|
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_ResultType:
|
|
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:
|
|
case CK_Colon:
|
|
case CK_SemiColon:
|
|
case CK_Equal:
|
|
case CK_HorizontalSpace:
|
|
case CK_VerticalSpace:
|
|
return Chunk(Kind, Text);
|
|
|
|
case CK_Optional: {
|
|
std::auto_ptr<CodeCompletionString> 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_ResultType:
|
|
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:
|
|
case CK_Colon:
|
|
case CK_SemiColon:
|
|
case CK_Equal:
|
|
case CK_HorizontalSpace:
|
|
case CK_VerticalSpace:
|
|
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:
|
|
case CK_ResultType:
|
|
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;
|
|
}
|
|
|
|
static void WriteUnsigned(llvm::raw_ostream &OS, unsigned Value) {
|
|
OS.write((const char *)&Value, sizeof(unsigned));
|
|
}
|
|
|
|
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:
|
|
case CK_Text:
|
|
case CK_Placeholder:
|
|
case CK_Informative:
|
|
case CK_ResultType:
|
|
case CK_CurrentParameter: {
|
|
const char *Text = C->Text;
|
|
unsigned StrLen = strlen(Text);
|
|
WriteUnsigned(OS, StrLen);
|
|
OS.write(Text, StrLen);
|
|
break;
|
|
}
|
|
|
|
case CK_Optional:
|
|
C->Optional->Serialize(OS);
|
|
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:
|
|
case CK_Colon:
|
|
case CK_SemiColon:
|
|
case CK_Equal:
|
|
case CK_HorizontalSpace:
|
|
case CK_VerticalSpace:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CodeCompletionString *CodeCompletionString::Deserialize(const char *&Str,
|
|
const char *StrEnd) {
|
|
if (Str == StrEnd || *Str == 0)
|
|
return 0;
|
|
|
|
CodeCompletionString *Result = new CodeCompletionString;
|
|
unsigned NumBlocks;
|
|
if (ReadUnsigned(Str, StrEnd, NumBlocks))
|
|
return Result;
|
|
|
|
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_ResultType:
|
|
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;
|
|
}
|
|
|
|
case CK_Optional: {
|
|
std::auto_ptr<CodeCompletionString> Optional(Deserialize(Str, StrEnd));
|
|
Result->AddOptionalChunk(Optional);
|
|
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:
|
|
case CK_Colon:
|
|
case CK_SemiColon:
|
|
case CK_Equal:
|
|
case CK_HorizontalSpace:
|
|
case CK_VerticalSpace:
|
|
Result->AddChunk(Chunk(Kind));
|
|
break;
|
|
}
|
|
};
|
|
|
|
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<FunctionType>();
|
|
|
|
case CK_FunctionTemplate:
|
|
return FunctionTemplate->getTemplatedDecl()->getType()
|
|
->getAs<FunctionType>();
|
|
|
|
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() ;
|
|
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 << '\n';
|
|
break;
|
|
|
|
case Result::RK_Macro: {
|
|
OS << Results[I].Macro->getName();
|
|
if (CodeCompletionString *CCS
|
|
= Results[I].CreateCodeCompletionString(SemaRef)) {
|
|
OS << " : " << CCS->getAsString();
|
|
delete CCS;
|
|
}
|
|
OS << '\n';
|
|
break;
|
|
}
|
|
|
|
case Result::RK_Pattern: {
|
|
OS << "Pattern : "
|
|
<< 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) {
|
|
CXCursorKind Kind = CXCursor_NotImplemented;
|
|
|
|
switch (Results[I].Kind) {
|
|
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 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_ObjCImplementationDecl;
|
|
break;
|
|
|
|
case Decl::ObjCCategoryImpl:
|
|
Kind = CXCursor_ObjCCategoryImplDecl;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Result::RK_Macro:
|
|
Kind = CXCursor_MacroDefinition;
|
|
break;
|
|
|
|
case Result::RK_Keyword:
|
|
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
|
|
// 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) {
|
|
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
|
|
// diagnostics.
|
|
// FIXME: Move this somewhere else!
|
|
SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics();
|
|
}
|