Skip to content

Commit

Permalink
[CodeComplete] Complete enumerators when preferred type is an enum
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ilya-biryukov committed May 16, 2019
1 parent 130af81 commit 60744a8
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 21 deletions.
65 changes: 44 additions & 21 deletions lib/Sema/SemaCodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4078,6 +4078,36 @@ struct Sema::CodeCompleteExpressionData {
SmallVector<Decl *, 4> IgnoreDecls;
};

namespace {
/// Information that allows to avoid completing redundant enumerators.
struct CoveredEnumerators {
llvm::SmallPtrSet<EnumConstantDecl *, 8> 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,
Expand Down Expand Up @@ -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<EnumType>()->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)
Expand Down Expand Up @@ -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<EnumConstantDecl *, 8> EnumeratorsSeen;
NestedNameSpecifier *Qualifier = nullptr;
CoveredEnumerators Enumerators;
for (SwitchCase *SC = Switch->getSwitchCaseList(); SC;
SC = SC->getNextSwitchCase()) {
CaseStmt *Case = dyn_cast<CaseStmt>(SC);
Expand All @@ -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.,
Expand All @@ -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);
Expand Down
24 changes: 24 additions & 0 deletions test/CodeCompletion/enum-preferred-type.cpp
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 60744a8

Please sign in to comment.