From: Ilya Biryukov Date: Thu, 16 May 2019 16:06:57 +0000 (+0000) Subject: [CodeComplete] Complete enumerators when preferred type is an enum X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=60744a8e7fc455adee29f920df5b20f57793abc7;p=clang [CodeComplete] Complete enumerators when preferred type is an enum Reviewers: kadircet Reviewed By: kadircet Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D62010 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@360912 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 2a62eca11b..0f33442e4a 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -4078,6 +4078,36 @@ struct Sema::CodeCompleteExpressionData { SmallVector IgnoreDecls; }; +namespace { +/// Information that allows to avoid completing redundant enumerators. +struct CoveredEnumerators { + llvm::SmallPtrSet Seen; + NestedNameSpecifier *SuggestedQualifier = nullptr; +}; +} // namespace + +static void AddEnumerators(ResultBuilder &Results, ASTContext &Context, + EnumDecl *Enum, DeclContext *CurContext, + const CoveredEnumerators &Enumerators) { + NestedNameSpecifier *Qualifier = Enumerators.SuggestedQualifier; + if (Context.getLangOpts().CPlusPlus && !Qualifier && Enumerators.Seen.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); + } + + Results.EnterNewScope(); + for (auto *E : Enum->enumerators()) { + if (Enumerators.Seen.count(E)) + continue; + + CodeCompletionResult R(E, CCP_EnumInCase, Qualifier); + Results.AddResult(R, CurContext, nullptr, false); + } + Results.ExitScope(); +} + /// Perform code-completion in an expression context when we know what /// type we're looking for. void Sema::CodeCompleteExpression(Scope *S, @@ -4118,10 +4148,19 @@ void Sema::CodeCompleteExpression(Scope *S, Results.ExitScope(); bool PreferredTypeIsPointer = false; - if (!Data.PreferredType.isNull()) + if (!Data.PreferredType.isNull()) { PreferredTypeIsPointer = Data.PreferredType->isAnyPointerType() || Data.PreferredType->isMemberPointerType() || Data.PreferredType->isBlockPointerType(); + if (Data.PreferredType->isEnumeralType()) { + EnumDecl *Enum = Data.PreferredType->castAs()->getDecl(); + if (auto *Def = Enum->getDefinition()) + Enum = Def; + // FIXME: collect covered enumerators in cases like: + // if (x == my_enum::one) { ... } else if (x == ^) {} + AddEnumerators(Results, Context, Enum, CurContext, CoveredEnumerators()); + } + } if (S->getFnParent() && !Data.ObjCCollection && !Data.IntegralConstantExpression) @@ -4719,8 +4758,7 @@ void Sema::CodeCompleteCase(Scope *S) { // FIXME: Ideally, we would also be able to look *past* the code-completion // 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 EnumeratorsSeen; - NestedNameSpecifier *Qualifier = nullptr; + CoveredEnumerators Enumerators; for (SwitchCase *SC = Switch->getSwitchCaseList(); SC; SC = SC->getNextSwitchCase()) { CaseStmt *Case = dyn_cast(SC); @@ -4737,7 +4775,7 @@ void Sema::CodeCompleteCase(Scope *S) { // values of each enumerator. However, value-based approach would not // work as well with C++ templates where enumerators declared within a // template are type- and value-dependent. - EnumeratorsSeen.insert(Enumerator); + Enumerators.Seen.insert(Enumerator); // 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., @@ -4750,30 +4788,15 @@ void Sema::CodeCompleteCase(Scope *S) { // 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. - Qualifier = DRE->getQualifier(); + Enumerators.SuggestedQualifier = DRE->getQualifier(); } } - if (getLangOpts().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); - } - // Add any enumerators that have not yet been mentioned. ResultBuilder Results(*this, CodeCompleter->getAllocator(), CodeCompleter->getCodeCompletionTUInfo(), CodeCompletionContext::CCC_Expression); - Results.EnterNewScope(); - for (auto *E : Enum->enumerators()) { - if (EnumeratorsSeen.count(E)) - continue; - - CodeCompletionResult R(E, CCP_EnumInCase, Qualifier); - Results.AddResult(R, CurContext, nullptr, false); - } - Results.ExitScope(); + AddEnumerators(Results, Context, Enum, CurContext, Enumerators); if (CodeCompleter->includeMacros()) { AddMacroResults(PP, Results, CodeCompleter->loadExternal(), false); diff --git a/test/CodeCompletion/enum-preferred-type.cpp b/test/CodeCompletion/enum-preferred-type.cpp new file mode 100644 index 0000000000..7ff4be9048 --- /dev/null +++ b/test/CodeCompletion/enum-preferred-type.cpp @@ -0,0 +1,24 @@ +namespace N { + enum Color { + Red, + Blue, + Orange, + }; +} + +void test(N::Color color) { + color = N::Color::Red; + test(N::Color::Red); + if (color == N::Color::Red) {} + // FIXME: ideally, we should not show 'Red' on the next line. + else if (color == N::Color::Blue) {} + + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:10:11 %s -o - | FileCheck %s + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:11:8 %s -o - | FileCheck %s + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:12:16 %s -o - | FileCheck %s + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:14:21 %s -o - | FileCheck %s + // CHECK: Blue : [#N::Color#]N::Blue + // CHECK: color : [#N::Color#]color + // CHECK: Orange : [#N::Color#]N::Orange + // CHECK: Red : [#N::Color#]N::Red +}