diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-09-21 19:57:38 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-09-21 19:57:38 +0000 |
commit | b9d0ef76347574a147b8456b018fe94dc1e2be26 (patch) | |
tree | fe0995cc5b39da6033b29a2636c1b8e280d7df56 | |
parent | fd02ed702e754f8dd0b4c979325ba99c2234f270 (diff) |
Enhance "case" code completion in C++ to suggest qualified names for
enumerators when either the user intentionally wrote a qualified name
(in which case we just use that nested-name-specifier to match
the user's code) or when this is the first "case" statement and we
need a qualified name to refer to an enumerator in a different scope.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82474 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Sema/CodeCompleteConsumer.h | 10 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 103 | ||||
-rw-r--r-- | test/CodeCompletion/enum-switch-case-qualified.cpp | 33 | ||||
-rw-r--r-- | test/CodeCompletion/enum-switch-case.cpp | 29 |
4 files changed, 161 insertions, 14 deletions
diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 3a7e73706b..5db369cc13 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -24,6 +24,7 @@ class raw_ostream; namespace clang { class NamedDecl; +class NestedNameSpecifier; class Sema; /// \brief A "string" used to describe how code completion can @@ -155,10 +156,15 @@ public: /// \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. + NestedNameSpecifier *Qualifier; + /// \brief Build a result that refers to a declaration. - Result(NamedDecl *Declaration, unsigned Rank) + Result(NamedDecl *Declaration, unsigned Rank, + NestedNameSpecifier *Qualifier = 0) : Kind(RK_Declaration), Declaration(Declaration), Rank(Rank), - Hidden(false) { } + Hidden(false), Qualifier(Qualifier) { } /// \brief Build a result that refers to a keyword or symbol. Result(const char *Keyword, unsigned Rank) diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index c3efcdd05b..00d7fc5466 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "Sema.h" #include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/AST/ExprCXX.h" #include "llvm/ADT/SmallPtrSet.h" #include <list> #include <map> @@ -328,6 +329,53 @@ static DeclContext *findOuterContext(Scope *S) { return 0; } +/// \brief Compute the qualification required to get from the current context +/// (\p CurContext) to the target context (\p TargetContext). +/// +/// \param Context the AST context in which the qualification will be used. +/// +/// \param CurContext the context where an entity is being named, which is +/// typically based on the current scope. +/// +/// \param TargetContext the context in which the named entity actually +/// resides. +/// +/// \returns a nested name specifier that refers into the target context, or +/// NULL if no qualification is needed. +static NestedNameSpecifier * +getRequiredQualification(ASTContext &Context, + DeclContext *CurContext, + DeclContext *TargetContext) { + llvm::SmallVector<DeclContext *, 4> TargetParents; + + for (DeclContext *CommonAncestor = TargetContext; + CommonAncestor && !CommonAncestor->Encloses(CurContext); + CommonAncestor = CommonAncestor->getLookupParent()) { + if (CommonAncestor->isTransparentContext() || + CommonAncestor->isFunctionOrMethod()) + continue; + + TargetParents.push_back(CommonAncestor); + } + + NestedNameSpecifier *Result = 0; + while (!TargetParents.empty()) { + DeclContext *Parent = TargetParents.back(); + TargetParents.pop_back(); + + if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent)) + Result = NestedNameSpecifier::Create(Context, Result, Namespace); + else if (TagDecl *TD = dyn_cast<TagDecl>(Parent)) + Result = NestedNameSpecifier::Create(Context, Result, + false, + Context.getTypeDeclType(TD).getTypePtr()); + else + assert(Parent->isTranslationUnit()); + } + + return Result; +} + /// \brief Collect the results of searching for members within the given /// declaration context. /// @@ -651,6 +699,22 @@ static void AddTemplateParameterChunks(ASTContext &Context, } } +/// \brief Add a qualifier to the given code-completion string, if the +/// provided nested-name-specifier is non-NULL. +void AddQualifierToCompletionString(CodeCompletionString *Result, + NestedNameSpecifier *Qualifier, + ASTContext &Context) { + if (!Qualifier) + return; + + std::string PrintedNNS; + { + llvm::raw_string_ostream OS(PrintedNNS); + Qualifier->print(OS, Context.PrintingPolicy); + } + Result->AddTextChunk(PrintedNNS.c_str()); +} + /// \brief If possible, create a new code completion string for the given /// result. /// @@ -666,6 +730,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) { CodeCompletionString *Result = new CodeCompletionString; + AddQualifierToCompletionString(Result, Qualifier, S.Context); Result->AddTextChunk(Function->getNameAsString().c_str()); Result->AddTextChunk("("); AddFunctionParameterChunks(S.Context, Function, Result); @@ -675,6 +740,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) { CodeCompletionString *Result = new CodeCompletionString; + AddQualifierToCompletionString(Result, Qualifier, S.Context); FunctionDecl *Function = FunTmpl->getTemplatedDecl(); Result->AddTextChunk(Function->getNameAsString().c_str()); @@ -727,6 +793,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (TemplateDecl *Template = dyn_cast<TemplateDecl>(ND)) { CodeCompletionString *Result = new CodeCompletionString; + AddQualifierToCompletionString(Result, Qualifier, S.Context); Result->AddTextChunk(Template->getNameAsString().c_str()); Result->AddTextChunk("<"); AddTemplateParameterChunks(S.Context, Template, Result); @@ -734,6 +801,13 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { return Result; } + if (Qualifier) { + CodeCompletionString *Result = new CodeCompletionString; + AddQualifierToCompletionString(Result, Qualifier, S.Context); + Result->AddTextChunk(ND->getNameAsString().c_str()); + return Result; + } + return 0; } @@ -900,6 +974,7 @@ void Sema::CodeCompleteCase(Scope *S) { // token, in case we are code-completing in the middle of the switch and not // at the end. However, we aren't able to do so at the moment. llvm::SmallPtrSet<EnumConstantDecl *, 8> EnumeratorsSeen; + NestedNameSpecifier *Qualifier = 0; for (SwitchCase *SC = Switch->getSwitchCaseList(); SC; SC = SC->getNextSwitchCase()) { CaseStmt *Case = dyn_cast<CaseStmt>(SC); @@ -918,21 +993,32 @@ void Sema::CodeCompleteCase(Scope *S) { // template are type- and value-dependent. EnumeratorsSeen.insert(Enumerator); - // FIXME: If this is a qualified-id, should we keep track of the - // nested-name-specifier so we can reproduce it as part of code - // completion? e.g., + // If this is a qualified-id, keep track of the nested-name-specifier + // so that we can reproduce it as part of code completion, e.g., // // switch (TagD.getKind()) { // case TagDecl::TK_enum: // break; // case XXX // - // At the XXX, we would like our completions to be TagDecl::TK_union, + // At the XXX, our completions are TagDecl::TK_union, // TagDecl::TK_struct, and TagDecl::TK_class, rather than TK_union, // TK_struct, and TK_class. + if (QualifiedDeclRefExpr *QDRE = dyn_cast<QualifiedDeclRefExpr>(DRE)) + Qualifier = QDRE->getQualifier(); } } + if (getLangOptions().CPlusPlus && !Qualifier && EnumeratorsSeen.empty()) { + // If there are no prior enumerators in C++, check whether we have to + // qualify the names of the enumerators that we suggest, because they + // may not be visible in this scope. + Qualifier = getRequiredQualification(Context, CurContext, + Enum->getDeclContext()); + + // FIXME: Scoped enums need to start with "EnumDecl" as the context! + } + // Add any enumerators that have not yet been mentioned. ResultBuilder Results(*this); Results.EnterNewScope(); @@ -942,17 +1028,10 @@ void Sema::CodeCompleteCase(Scope *S) { if (EnumeratorsSeen.count(*E)) continue; - Results.MaybeAddResult(CodeCompleteConsumer::Result(*E, 0)); + Results.MaybeAddResult(CodeCompleteConsumer::Result(*E, 0, Qualifier)); } Results.ExitScope(); - // In C++, add nested-name-specifiers. - if (getLangOptions().CPlusPlus) { - Results.setFilter(&ResultBuilder::IsNestedNameSpecifier); - CollectLookupResults(S, Context.getTranslationUnitDecl(), 1, - Results); - } - HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size()); } diff --git a/test/CodeCompletion/enum-switch-case-qualified.cpp b/test/CodeCompletion/enum-switch-case-qualified.cpp new file mode 100644 index 0000000000..fc1b0ea075 --- /dev/null +++ b/test/CodeCompletion/enum-switch-case-qualified.cpp @@ -0,0 +1,33 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true + +namespace M { + +namespace N { + struct C { + enum Color { + Red, + Orange, + Yellow, + Green, + Blue, + Indigo, + Violet + }; + }; +} + +} + +namespace M { + +void test(enum N::C::Color color) { + switch (color) { + // CHECK-NEXT-CC1: Blue : 0 : N::C::Blue + // CHECK-NEXT-CC1: Green : 0 : N::C::Green + // CHECK-NEXT-CC1: Indigo : 0 : N::C::Indigo + // CHECK-NEXT-CC1: Orange : 0 : N::C::Orange + // CHECK-NEXT-CC1: Red : 0 : N::C::Red + // CHECK-NEXT-CC1: Violet : 0 : N::C::Violet + // CHECK-NEXT-CC1: Yellow : 0 : N::C::Yellow + case diff --git a/test/CodeCompletion/enum-switch-case.cpp b/test/CodeCompletion/enum-switch-case.cpp new file mode 100644 index 0000000000..49b33c830a --- /dev/null +++ b/test/CodeCompletion/enum-switch-case.cpp @@ -0,0 +1,29 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true + +namespace N { + enum Color { + Red, + Orange, + Yellow, + Green, + Blue, + Indigo, + Violet + }; +} + +void test(enum N::Color color) { + switch (color) { + case N::Red: + break; + + case N::Yellow: + break; + + // CHECK-CC1: Blue : 0 : N::Blue + // CHECK-NEXT-CC1: Green : 0 : N::Green + // CHECK-NEXT-CC1: Indigo : 0 : N::Indigo + // CHECK-NEXT-CC1: Orange : 0 : N::Orange + // CHECK-NEXT-CC1: Violet : 0 : N::Violet + case |