From: Douglas Gregor Date: Mon, 21 Sep 2009 18:10:23 +0000 (+0000) Subject: Code completion for "case" statements within a switch on an expression X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3e1005f085006dfb3545f0c54ac5e22483137c7d;p=clang Code completion for "case" statements within a switch on an expression of enumeration type, providing the various unused enumerators as options. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82467 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 2d37b86a28..fb7f641f31 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -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. /// diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index edb0018d20..4fdcf2a077 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -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); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index d2bbc4cbb5..aa35a60ee6 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 0032ab5ff7..c3efcdd05b 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -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()->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 EnumeratorsSeen; + for (SwitchCase *SC = Switch->getSwitchCaseList(); SC; + SC = SC->getNextSwitchCase()) { + CaseStmt *Case = dyn_cast(SC); + if (!Case) + continue; + + Expr *CaseVal = Case->getLHS()->IgnoreParenCasts(); + if (DeclRefExpr *DRE = dyn_cast(CaseVal)) + if (EnumConstantDecl *Enumerator + = dyn_cast(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) diff --git a/test/CodeCompletion/enum-switch-case.c b/test/CodeCompletion/enum-switch-case.c new file mode 100644 index 0000000000..08488f75c1 --- /dev/null +++ b/test/CodeCompletion/enum-switch-case.c @@ -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