diff options
-rw-r--r-- | include/clang/Sema/CodeCompleteConsumer.h | 101 | ||||
-rw-r--r-- | lib/Sema/CodeCompleteConsumer.cpp | 133 | ||||
-rw-r--r-- | test/CodeCompletion/functions.cpp | 9 |
3 files changed, 241 insertions, 2 deletions
diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 821678b22a..a8facd8fdc 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -16,8 +16,11 @@ #include "clang/AST/DeclarationName.h" #include "clang/AST/Type.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" #include <list> #include <map> +#include <memory> +#include <string> #include <vector> namespace llvm { @@ -31,6 +34,103 @@ class DeclContext; class NamedDecl; class Scope; class Sema; + +/// \brief A "string" used to describe how code completion can +/// be performed for an entity. +/// +/// A code completion string typically shows how a particular entity can be +/// used. For example, the code completion string for a function would show +/// the syntax to call it, including the parentheses, placeholders for the +/// arguments, etc. +class CodeCompletionString { +public: + /// \brief The different kinds of "chunks" that can occur within a code + /// completion string. + enum ChunkKind { + /// \brief A piece of text that should be placed in the buffer, e.g., + /// parentheses or a comma in a function call. + CK_Text, + /// \brief A code completion string that is entirely optional. For example, + /// an optional code completion string that describes the default arguments + /// in a function call. + CK_Optional, + /// \brief A string that acts as a placeholder for, e.g., a function + /// call argument. + CK_Placeholder + }; + + /// \brief One piece of the code completion string. + struct Chunk { + /// \brief The kind of data stored in this piece of the code completion + /// string. + ChunkKind Kind; + + union { + /// \brief The text string associated with a CK_Text chunk. + /// The string is owned by the chunk and will be deallocated + /// (with delete[]) when the chunk is destroyed. + const char *Text; + + /// \brief The code completion string associated with a CK_Optional chunk. + /// The optional code completion string is owned by the chunk, and will + /// be deallocated (with delete) when the chunk is destroyed. + CodeCompletionString *Optional; + + /// \brief Placeholder text associated with a CK_Placeholder chunk. + /// The string is owned by the chunk and will be deallocated (with + /// delete[]) when the chunk is destroyed. + const char *Placeholder; + }; + + /// \brief Create a new text chunk. + static Chunk CreateText(const char *Text); + + /// \brief Create a new optional chunk. + static Chunk CreateOptional(std::auto_ptr<CodeCompletionString> Optional); + + /// \brief Create a new placeholder chunk. + static Chunk CreatePlaceholder(const char *Placeholder); + + /// \brief Destroy this chunk. + void Destroy(); + }; + +private: + /// \brief The chunks stored in this string. + llvm::SmallVector<Chunk, 4> Chunks; + + CodeCompletionString(const CodeCompletionString &); // DO NOT IMPLEMENT + CodeCompletionString &operator=(const CodeCompletionString &); // DITTO + +public: + CodeCompletionString() { } + ~CodeCompletionString(); + + typedef llvm::SmallVector<Chunk, 4>::const_iterator iterator; + iterator begin() const { return Chunks.begin(); } + iterator end() const { return Chunks.end(); } + + /// \brief Add a new text chunk. + /// The text string will be copied. + void AddTextChunk(const char *Text) { + Chunks.push_back(Chunk::CreateText(Text)); + } + + /// \brief Add a new optional chunk. + void AddOptionalChunk(std::auto_ptr<CodeCompletionString> Optional) { + Chunks.push_back(Chunk::CreateOptional(Optional)); + } + + /// \brief Add a new placeholder chunk. + /// The placeholder text will be copied. + void AddPlaceholderChunk(const char *Placeholder) { + Chunks.push_back(Chunk::CreatePlaceholder(Placeholder)); + } + + /// \brief Retrieve a string representation of the code completion string, + /// which is mainly useful for debugging. + std::string getAsString() const; +}; /// \brief Abstract interface for a consumer of code-completion /// information. @@ -264,6 +364,7 @@ public: //@{ bool canHiddenResultBeFound(NamedDecl *Hidden, NamedDecl *Visible); void AddTypeSpecifierResults(unsigned Rank, ResultSet &Results); + CodeCompletionString *CreateCodeCompletionString(Result R); //@} }; diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index d82047d912..9bf9e15253 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -19,9 +19,75 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> -#include <string.h> +#include <cstring> +#include <functional> using namespace clang; +//===----------------------------------------------------------------------===// +// Code completion string implementation +//===----------------------------------------------------------------------===// +CodeCompletionString::Chunk +CodeCompletionString::Chunk::CreateText(const char *Text) { + Chunk Result; + Result.Kind = CK_Text; + char *New = new char [std::strlen(Text) + 1]; + std::strcpy(New, Text); + Result.Text = New; + return Result; +} + +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(const char *Placeholder) { + Chunk Result; + Result.Kind = CK_Placeholder; + char *New = new char [std::strlen(Placeholder) + 1]; + std::strcpy(New, Placeholder); + Result.Placeholder = New; + return Result; +} + +void +CodeCompletionString::Chunk::Destroy() { + switch (Kind) { + case CK_Text: delete [] Text; break; + case CK_Optional: delete Optional; break; + case CK_Placeholder: delete [] Placeholder; 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_Text: OS << C->Text; break; + case CK_Optional: OS << "{#" << C->Optional->getAsString() << "#}"; break; + case CK_Placeholder: OS << "<#" << C->Placeholder << "#>"; break; + } + } + + return Result; +} + +//===----------------------------------------------------------------------===// +// Code completion consumer implementation +//===----------------------------------------------------------------------===// + CodeCompleteConsumer::CodeCompleteConsumer(Sema &S) : SemaRef(S) { SemaRef.setCodeCompleteConsumer(this); } @@ -180,7 +246,7 @@ void CodeCompleteConsumer::CodeCompleteOperatorName(Scope *S) { // Add the names of overloadable operators. #define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ - if (strcmp(Spelling, "?")) \ + if (std::strcmp(Spelling, "?")) \ Results.MaybeAddResult(Result(Spelling, 0)); #include "clang/Basic/OperatorKinds.def" @@ -663,6 +729,64 @@ void CodeCompleteConsumer::AddTypeSpecifierResults(unsigned Rank, } } +/// \brief Add function parameter chunks to the given code completion string. +static void AddFunctionParameterChunks(ASTContext &Context, + FunctionDecl *Function, + CodeCompletionString *Result) { + CodeCompletionString *CCStr = Result; + + for (unsigned P = 0, N = Function->getNumParams(); P != N; ++P) { + ParmVarDecl *Param = Function->getParamDecl(P); + + if (Param->hasDefaultArg()) { + // When we see an optional default argument, put that argument and + // the remaining default arguments into a new, optional string. + CodeCompletionString *Opt = new CodeCompletionString; + CCStr->AddOptionalChunk(std::auto_ptr<CodeCompletionString>(Opt)); + CCStr = Opt; + } + + if (P != 0) + CCStr->AddTextChunk(", "); + + // Format the placeholder string. + std::string PlaceholderStr; + if (Param->getIdentifier()) + PlaceholderStr = Param->getIdentifier()->getName(); + + Param->getType().getAsStringInternal(PlaceholderStr, + Context.PrintingPolicy); + + // Add the placeholder string. + CCStr->AddPlaceholderChunk(PlaceholderStr.c_str()); + } +} + +/// \brief If possible, create a new code completion string for the given +/// result. +/// +/// \returns Either a new, heap-allocated code completion string describing +/// how to use this result, or NULL to indicate that the string or name of the +/// result is all that is needed. +CodeCompletionString * +CodeCompleteConsumer::CreateCodeCompletionString(Result R) { + if (R.Kind != Result::RK_Declaration) + return 0; + + NamedDecl *ND = R.Declaration; + + if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) { + CodeCompletionString *Result = new CodeCompletionString; + Result->AddTextChunk(Function->getNameAsString().c_str()); + Result->AddTextChunk("("); + AddFunctionParameterChunks(getSema().Context, Function, Result); + Result->AddTextChunk(")"); + return Result; + } + + return 0; +} + void PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Result *Results, unsigned NumResults) { @@ -677,6 +801,11 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Result *Results, << Results[I].Rank; if (Results[I].Hidden) OS << " (Hidden)"; + if (CodeCompletionString *CCS = CreateCodeCompletionString(Results[I])) { + OS << " : " << CCS->getAsString(); + delete CCS; + } + OS << '\n'; break; diff --git a/test/CodeCompletion/functions.cpp b/test/CodeCompletion/functions.cpp new file mode 100644 index 0000000000..f722e9a07e --- /dev/null +++ b/test/CodeCompletion/functions.cpp @@ -0,0 +1,9 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true +void f(int i, int j = 2, int k = 5); +void f(float x, float y); + +void test() { + // CHECK-CC1: f(<#int i#>{#, <#int j#>{#, <#int k#>#}#}) + // CHECK-CC1: f(<#float x#>, <#float y#>) + :: |