]> granicus.if.org Git - clang/commitdiff
Code completion for "case" statements within a switch on an expression
authorDouglas Gregor <dgregor@apple.com>
Mon, 21 Sep 2009 18:10:23 +0000 (18:10 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 21 Sep 2009 18:10:23 +0000 (18:10 +0000)
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

include/clang/Parse/Action.h
lib/Parse/ParseStmt.cpp
lib/Sema/Sema.h
lib/Sema/SemaCodeComplete.cpp
test/CodeCompletion/enum-switch-case.c [new file with mode: 0644]

index 2d37b86a28297974f8e78077e82774d3c5e0783b..fb7f641f3107faf22d1634aab1c1292e6c1bce43 100644 (file)
@@ -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.
   ///
index edb0018d20d207ecd9d676f8e27575abf9c18744..4fdcf2a077f3551c0b6c76aa11d2bbd5f03766fc 100644 (file)
@@ -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);
index d2bbc4cbb5d614f5b950a87bbbfcf75b9e5d82d0..aa35a60ee65e5d9298c7ab94bb44c1e9bff4dad7 100644 (file)
@@ -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);
index 0032ab5ff75d50d01a0f119430c111a240939c1d..c3efcdd05b272d490c7de617733529dfc523cb6e 100644 (file)
@@ -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)
diff --git a/test/CodeCompletion/enum-switch-case.c b/test/CodeCompletion/enum-switch-case.c
new file mode 100644 (file)
index 0000000..08488f7
--- /dev/null
@@ -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