CK_Optional,
/// \brief A string that acts as a placeholder for, e.g., a function
/// call argument.
- CK_Placeholder
+ CK_Placeholder,
+ /// \brief A piece of text that describes something about the result but
+ /// should not be inserted into the buffer.
+ CK_Informative
};
/// \brief One piece of the code completion string.
ChunkKind Kind;
union {
- /// \brief The text string associated with a CK_Text chunk.
+ /// \brief The text string associated with a CK_Text, CK_Placeholder,
+ /// or CK_Informative chunk.
/// The string is owned by the chunk and will be deallocated
/// (with delete[]) when the chunk is destroyed.
const char *Text;
/// 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;
};
+ Chunk() : Kind(CK_Text), Text(0) { }
+
+ private:
+ Chunk(ChunkKind Kind, const char *Text);
+
+ public:
/// \brief Create a new text chunk.
static Chunk CreateText(const char *Text);
/// \brief Create a new placeholder chunk.
static Chunk CreatePlaceholder(const char *Placeholder);
-
+
+ /// \brief Create a new informative chunk.
+ static Chunk CreateInformative(const char *Informative);
+
/// \brief Destroy this chunk, deallocating any memory it owns.
void Destroy();
};
void AddPlaceholderChunk(const char *Placeholder) {
Chunks.push_back(Chunk::CreatePlaceholder(Placeholder));
}
+
+ /// \brief Add a new informative chunk.
+ /// The text will be copied.
+ void AddInformativeChunk(const char *Text) {
+ Chunks.push_back(Chunk::CreateInformative(Text));
+ }
/// \brief Retrieve a string representation of the code completion string,
/// which is mainly useful for debugging.
/// \brief Whether this result is hidden by another name.
bool Hidden : 1;
- /// \brief If the result requires a nested-name-specifier for name lookup
- /// to function properly, this is the nested-name-specifier.
+ /// \brief Whether this result was found via lookup into a base class.
+ bool QualifierIsInformative : 1;
+
+ /// \brief If the result should have a nested-name-specifier, this is it.
+ /// When \c QualifierIsInformative, the nested-name-specifier is
+ /// informative rather than required.
NestedNameSpecifier *Qualifier;
/// \brief Build a result that refers to a declaration.
Result(NamedDecl *Declaration, unsigned Rank,
- NestedNameSpecifier *Qualifier = 0)
+ NestedNameSpecifier *Qualifier = 0,
+ bool QualifierIsInformative = false)
: Kind(RK_Declaration), Declaration(Declaration), Rank(Rank),
- Hidden(false), Qualifier(Qualifier) { }
+ Hidden(false), QualifierIsInformative(QualifierIsInformative),
+ Qualifier(Qualifier) { }
/// \brief Build a result that refers to a keyword or symbol.
Result(const char *Keyword, unsigned Rank)
- : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false) { }
+ : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false),
+ QualifierIsInformative(0), Qualifier(0) { }
/// \brief Retrieve the declaration stored in this result.
NamedDecl *getDeclaration() const {
//===----------------------------------------------------------------------===//
// Code completion string implementation
//===----------------------------------------------------------------------===//
-CodeCompletionString::Chunk
-CodeCompletionString::Chunk::CreateText(const char *Text) {
- Chunk Result;
- Result.Kind = CK_Text;
+CodeCompletionString::Chunk::Chunk(ChunkKind Kind, const char *Text)
+ : Kind(Kind), Text(0)
+{
+ assert((Kind == CK_Text || Kind == CK_Placeholder || Kind == CK_Informative)
+ && "Invalid text chunk kind");
char *New = new char [std::strlen(Text) + 1];
std::strcpy(New, Text);
- Result.Text = New;
- return Result;
+ this->Text = New;
+}
+
+CodeCompletionString::Chunk
+CodeCompletionString::Chunk::CreateText(const char *Text) {
+ return Chunk(CK_Text, Text);
}
CodeCompletionString::Chunk
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;
+ return Chunk(CK_Placeholder, Placeholder);
+}
+
+CodeCompletionString::Chunk
+CodeCompletionString::Chunk::CreateInformative(const char *Informative) {
+ return Chunk(CK_Informative, Informative);
}
void
CodeCompletionString::Chunk::Destroy() {
switch (Kind) {
- case CK_Text: delete [] Text; break;
- case CK_Optional: delete Optional; break;
- case CK_Placeholder: delete [] Placeholder; break;
+ case CK_Optional:
+ delete Optional;
+ break;
+
+ case CK_Text:
+ case CK_Placeholder:
+ case CK_Informative:
+ delete [] Text;
+ break;
}
}
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;
+ case CK_Placeholder: OS << "<#" << C->Text << "#>"; break;
+ case CK_Informative: OS << "[#" << C->Text << "#]"; break;
}
}
// Look through using declarations.
if (UsingDecl *Using = dyn_cast<UsingDecl>(R.Declaration))
- return MaybeAddResult(Result(Using->getTargetDecl(), R.Rank, R.Qualifier),
- CurContext);
+ MaybeAddResult(Result(Using->getTargetDecl(), R.Rank, R.Qualifier),
+ CurContext);
// Handle each declaration in an overload set separately.
if (OverloadedFunctionDecl *Ovl
I->second.first)) {
// Note that this result was hidden.
R.Hidden = true;
+ R.QualifierIsInformative = false;
if (!R.Qualifier)
R.Qualifier = getRequiredQualification(SemaRef.Context,
if (!AllDeclsFound.insert(CanonDecl))
return;
+ // If this result is supposed to have an informative qualifier, add one.
+ if (R.QualifierIsInformative && !R.Qualifier) {
+ DeclContext *Ctx = R.Declaration->getDeclContext();
+ if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Ctx))
+ R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, 0, Namespace);
+ else if (TagDecl *Tag = dyn_cast<TagDecl>(Ctx))
+ R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, 0, false,
+ SemaRef.Context.getTypeDeclType(Tag).getTypePtr());
+ else
+ R.QualifierIsInformative = false;
+ }
+
// Insert this result into the set of results and into the current shadow
// map.
SMap.insert(std::make_pair(R.Declaration->getDeclName(),
///
/// \param Ctx the declaration context from which we will gather results.
///
-/// \param InitialRank the initial rank given to results in this declaration
-/// context. Larger rank values will be used for, e.g., members found in
-/// base classes.
+/// \param Rank the rank given to results in this declaration context.
///
/// \param Visited the set of declaration contexts that have already been
/// visited. Declaration contexts will only be visited once.
/// \param Results the result set that will be extended with any results
/// found within this declaration context (and, for a C++ class, its bases).
///
+/// \param InBaseClass whether we are in a base class.
+///
/// \returns the next higher rank value, after considering all of the
/// names within this declaration context.
static unsigned CollectMemberLookupResults(DeclContext *Ctx,
- unsigned InitialRank,
+ unsigned Rank,
DeclContext *CurContext,
llvm::SmallPtrSet<DeclContext *, 16> &Visited,
- ResultBuilder &Results) {
+ ResultBuilder &Results,
+ bool InBaseClass = false) {
// Make sure we don't visit the same context twice.
if (!Visited.insert(Ctx->getPrimaryContext()))
- return InitialRank;
+ return Rank;
// Enumerate all of the results in this context.
+ typedef CodeCompleteConsumer::Result Result;
Results.EnterNewScope();
for (DeclContext *CurCtx = Ctx->getPrimaryContext(); CurCtx;
CurCtx = CurCtx->getNextContext()) {
DEnd = CurCtx->decls_end();
D != DEnd; ++D) {
if (NamedDecl *ND = dyn_cast<NamedDecl>(*D))
- Results.MaybeAddResult(CodeCompleteConsumer::Result(ND, InitialRank),
- CurContext);
+ Results.MaybeAddResult(Result(ND, Rank, 0, InBaseClass), CurContext);
}
}
// Traverse the contexts of inherited classes.
- unsigned NextRank = InitialRank;
if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx)) {
for (CXXRecordDecl::base_class_iterator B = Record->bases_begin(),
BEnd = Record->bases_end();
// c->A::member
// Collect results from this base class (and its bases).
- NextRank = std::max(NextRank,
- CollectMemberLookupResults(Record->getDecl(),
- InitialRank + 1,
- CurContext,
- Visited,
- Results));
+ CollectMemberLookupResults(Record->getDecl(), Rank, CurContext, Visited,
+ Results, /*InBaseClass=*/true);
}
}
// FIXME: Look into base classes in Objective-C!
Results.ExitScope();
- return NextRank;
+ return Rank + 1;
}
/// \brief Collect the results of searching for members within the given
/// provided nested-name-specifier is non-NULL.
void AddQualifierToCompletionString(CodeCompletionString *Result,
NestedNameSpecifier *Qualifier,
+ bool QualifierIsInformative,
ASTContext &Context) {
if (!Qualifier)
return;
llvm::raw_string_ostream OS(PrintedNNS);
Qualifier->print(OS, Context.PrintingPolicy);
}
- Result->AddTextChunk(PrintedNNS.c_str());
+ if (QualifierIsInformative)
+ Result->AddInformativeChunk(PrintedNNS.c_str());
+ else
+ Result->AddTextChunk(PrintedNNS.c_str());
}
/// \brief If possible, create a new code completion string for the given
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
- AddQualifierToCompletionString(Result, Qualifier, S.Context);
+ AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
+ S.Context);
Result->AddTextChunk(Function->getNameAsString().c_str());
Result->AddTextChunk("(");
AddFunctionParameterChunks(S.Context, Function, Result);
if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
- AddQualifierToCompletionString(Result, Qualifier, S.Context);
+ AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
+ S.Context);
FunctionDecl *Function = FunTmpl->getTemplatedDecl();
Result->AddTextChunk(Function->getNameAsString().c_str());
if (TemplateDecl *Template = dyn_cast<TemplateDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
- AddQualifierToCompletionString(Result, Qualifier, S.Context);
+ AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
+ S.Context);
Result->AddTextChunk(Template->getNameAsString().c_str());
Result->AddTextChunk("<");
AddTemplateParameterChunks(S.Context, Template, Result);
if (Qualifier) {
CodeCompletionString *Result = new CodeCompletionString;
- AddQualifierToCompletionString(Result, Qualifier, S.Context);
+ AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
+ S.Context);
Result->AddTextChunk(ND->getNameAsString().c_str());
return Result;
}
void test(const Proxy &p) {
p->
// RUN: clang-cc -fsyntax-only -code-completion-at=%s:29:6 %s -o - | FileCheck -check-prefix=CC1 %s &&
+ // CHECK-CC1: member1 : 0 : [#Base1::#]member1
+ // CHECK-CC1: member1 : 0 : [#Base2::#]member1
+ // CHECK-CC1: member2 : 0 : [#Base1::#]member2
+ // CHECK-CC1: member3 : 0
// CHECK-CC1: member4 : 0
- // CHECK-CC1: memfun3 : 0
- // CHECK-CC1: memfun1 : 1
- // CHECK-CC1: memfun1 : 1
- // CHECK-CC1: memfun2 : 1
- // CHECK-CC1: member1 : 2
- // CHECK-CC1: member1 : 2
- // CHECK-CC1: member2 : 2
- // CHECK-CC1: member3 : 2
- // CHECK-CC1: memfun1 : 2 (Hidden) : Base2::memfun1(<#int#>)
+ // CHECK-CC1: memfun1 : 0 : [#Base3::#]memfun1(<#float#>)
+ // CHECK-CC1: memfun1 : 0 : [#Base3::#]memfun1(<#double#>)
+ // CHECK-CC1: memfun2 : 0 : [#Base3::#]memfun2(<#int#>)
+ // CHECK-CC1: memfun3 : 0 : memfun3(<#int#>)
+ // CHECK-CC1: memfun1 : 0 (Hidden) : Base2::memfun1(<#int#>)
// RUN: true
// CHECK-CC1: I1 : 1
// CHECK-CC1: I4 : 1
// CHECK-CC1: I5 : 1
- // CHECK-CC1: N2 : 2
- // CHECK-NEXT-CC1: N4 : 2
+ // CHECK-CC1: N2 : 3
+ // CHECK-NEXT-CC1: N4 : 3
// RUN: true
\ No newline at end of file
// CHECK-CC1: short : 0
// CHECK-CC1: Integer : 2
// CHECK-CC1: T : 2
- // CHECK-CC1: N : 5
+ // CHECK-CC1: N : 6
// RUN: true
// RUN: clang-cc -fsyntax-only -code-completion-at=%s:17:10 %s -o - | FileCheck -check-prefix=CC1 %s &&
// CHECK-CC1: Y : 2
// CHECK-CC1: Z : 2
- // CHECK-CC1: A : 3
- // CHECK-CC1: X : 3
- // CHECK-CC1: Y : 3
- // CHECK-CC1: M : 6
- // CHECK-CC1: N : 6
+ // CHECK-CC1: A : 4
+ // CHECK-CC1: X : 4
+ // CHECK-CC1: Y : 4
+ // CHECK-CC1: M : 9
+ // CHECK-CC1: N : 9
// RUN: true
// CHECK-CC1: I1 : 2
// CHECK-CC1: I4 : 2
// CHECK-CC1: I5 : 2
- // CHECK-CC1: N2 : 3
- // CHECK-NEXT-CC1: N4 : 3
+ // CHECK-CC1: N2 : 4
+ // CHECK-NEXT-CC1: N4 : 4
// RUN: true
// CHECK-CC1: I1 : 2
// CHECK-CC1: I4 : 2
// CHECK-CC1: I5 : 2
- // CHECK-CC1: N2 : 3
- // CHECK-CC1: N3 : 3
- // CHECK-NEXT-CC1: N4 : 3
+ // CHECK-CC1: N2 : 4
+ // CHECK-CC1: N3 : 4
+ // CHECK-NEXT-CC1: N4 : 4
// RUN: true