forked from OSchip/llvm-project
Code completion for "case" statements within a switch on an expression
of enumeration type, providing the various unused enumerators as options. llvm-svn: 82467
This commit is contained in:
parent
48ade83e60
commit
d328d57c39
|
@ -2217,12 +2217,17 @@ public:
|
|||
/// This code completion action is invoked when the code-completion
|
||||
/// token is found after a tag keyword (struct, union, enum, or class).
|
||||
///
|
||||
/// \para, S the scope in which the tag reference occurs.
|
||||
/// \param S the scope in which the tag reference occurs.
|
||||
///
|
||||
/// \param TagSpec an instance of DeclSpec::TST, indicating what kind of tag
|
||||
/// this is (struct/union/enum/class).
|
||||
virtual void CodeCompleteTag(Scope *S, unsigned TagSpec) { }
|
||||
|
||||
/// \brief Code completion for a case statement.
|
||||
///
|
||||
/// \brief S the scope in which the case statement occurs.
|
||||
virtual void CodeCompleteCase(Scope *S) { }
|
||||
|
||||
/// \brief Code completion for a C++ nested-name-specifier that precedes a
|
||||
/// qualified-id of some form.
|
||||
///
|
||||
|
|
|
@ -261,6 +261,11 @@ Parser::OwningStmtResult Parser::ParseCaseStatement() {
|
|||
do {
|
||||
SourceLocation CaseLoc = ConsumeToken(); // eat the 'case'.
|
||||
|
||||
if (Tok.is(tok::code_completion)) {
|
||||
Actions.CodeCompleteCase(CurScope);
|
||||
ConsumeToken();
|
||||
}
|
||||
|
||||
OwningExprResult LHS(ParseConstantExpression());
|
||||
if (LHS.isInvalid()) {
|
||||
SkipUntil(tok::colon);
|
||||
|
|
|
@ -3633,6 +3633,7 @@ public:
|
|||
SourceLocation OpLoc,
|
||||
bool IsArrow);
|
||||
virtual void CodeCompleteTag(Scope *S, unsigned TagSpec);
|
||||
virtual void CodeCompleteCase(Scope *S);
|
||||
virtual void CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
|
||||
bool EnteringContext);
|
||||
virtual void CodeCompleteUsing(Scope *S);
|
||||
|
|
|
@ -883,6 +883,79 @@ void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) {
|
|||
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
|
||||
}
|
||||
|
||||
void Sema::CodeCompleteCase(Scope *S) {
|
||||
if (getSwitchStack().empty() || !CodeCompleter)
|
||||
return;
|
||||
|
||||
SwitchStmt *Switch = getSwitchStack().back();
|
||||
if (!Switch->getCond()->getType()->isEnumeralType())
|
||||
return;
|
||||
|
||||
// Code-complete the cases of a switch statement over an enumeration type
|
||||
// by providing the list of
|
||||
EnumDecl *Enum = Switch->getCond()->getType()->getAs<EnumType>()->getDecl();
|
||||
|
||||
// Determine which enumerators we have already seen in the switch statement.
|
||||
// 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;
|
||||
for (SwitchCase *SC = Switch->getSwitchCaseList(); SC;
|
||||
SC = SC->getNextSwitchCase()) {
|
||||
CaseStmt *Case = dyn_cast<CaseStmt>(SC);
|
||||
if (!Case)
|
||||
continue;
|
||||
|
||||
Expr *CaseVal = Case->getLHS()->IgnoreParenCasts();
|
||||
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CaseVal))
|
||||
if (EnumConstantDecl *Enumerator
|
||||
= dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
|
||||
// We look into the AST of the case statement to determine which
|
||||
// enumerator was named. Alternatively, we could compute the value of
|
||||
// the integral constant expression, then compare it against the
|
||||
// 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);
|
||||
|
||||
// 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.,
|
||||
//
|
||||
// switch (TagD.getKind()) {
|
||||
// case TagDecl::TK_enum:
|
||||
// break;
|
||||
// case XXX
|
||||
//
|
||||
// At the XXX, we would like our completions to be TagDecl::TK_union,
|
||||
// TagDecl::TK_struct, and TagDecl::TK_class, rather than TK_union,
|
||||
// TK_struct, and TK_class.
|
||||
}
|
||||
}
|
||||
|
||||
// Add any enumerators that have not yet been mentioned.
|
||||
ResultBuilder Results(*this);
|
||||
Results.EnterNewScope();
|
||||
for (EnumDecl::enumerator_iterator E = Enum->enumerator_begin(),
|
||||
EEnd = Enum->enumerator_end();
|
||||
E != EEnd; ++E) {
|
||||
if (EnumeratorsSeen.count(*E))
|
||||
continue;
|
||||
|
||||
Results.MaybeAddResult(CodeCompleteConsumer::Result(*E, 0));
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
void Sema::CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
|
||||
bool EnteringContext) {
|
||||
if (!SS.getScopeRep() || !CodeCompleter)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
|
||||
// RUN: true
|
||||
|
||||
enum Color {
|
||||
Red,
|
||||
Orange,
|
||||
Yellow,
|
||||
Green,
|
||||
Blue,
|
||||
Indigo,
|
||||
Violet
|
||||
};
|
||||
|
||||
void test(enum Color color) {
|
||||
switch (color) {
|
||||
case Red:
|
||||
break;
|
||||
|
||||
case Yellow:
|
||||
break;
|
||||
|
||||
// CHECK-CC1: Blue : 0
|
||||
// CHECK-NEXT-CC1: Green : 0
|
||||
// CHECK-NEXT-CC1: Indigo : 0
|
||||
// CHECK-NEXT-CC1: Orange : 0
|
||||
// CHECK-NEXT-CC1: Violet : 0
|
||||
case
|
Loading…
Reference in New Issue