[cfe-commits] r82474 - in /cfe/trunk: include/clang/Sema/CodeCompleteConsumer.h lib/Sema/SemaCodeComplete.cpp test/CodeCompletion/enum-switch-case-qualified.cpp test/CodeCompletion/enum-switch-case.cpp

Douglas Gregor dgregor at apple.com
Mon Sep 21 12:57:38 PDT 2009


Author: dgregor
Date: Mon Sep 21 14:57:38 2009
New Revision: 82474

URL: http://llvm.org/viewvc/llvm-project?rev=82474&view=rev
Log:
Enhance "case" code completion in C++ to suggest qualified names for
enumerators when either the user intentionally wrote a qualified name
(in which case we just use that nested-name-specifier to match
the user's code) or when this is the first "case" statement and we
need a qualified name to refer to an enumerator in a different scope.


Added:
    cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp
    cfe/trunk/test/CodeCompletion/enum-switch-case.cpp
Modified:
    cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
    cfe/trunk/lib/Sema/SemaCodeComplete.cpp

Modified: cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h?rev=82474&r1=82473&r2=82474&view=diff

==============================================================================
--- cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h (original)
+++ cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h Mon Sep 21 14:57:38 2009
@@ -24,6 +24,7 @@
 namespace clang {
   
 class NamedDecl;
+class NestedNameSpecifier;
 class Sema;
 
 /// \brief A "string" used to describe how code completion can
@@ -155,10 +156,15 @@
     /// \brief Whether this result is hidden by another name.
     bool Hidden : 1;
     
+    /// \brief If the result requires a nested-name-specifier for name lookup
+    /// to function properly, this is the nested-name-specifier.
+    NestedNameSpecifier *Qualifier;
+    
     /// \brief Build a result that refers to a declaration.
-    Result(NamedDecl *Declaration, unsigned Rank)
+    Result(NamedDecl *Declaration, unsigned Rank, 
+           NestedNameSpecifier *Qualifier = 0)
       : Kind(RK_Declaration), Declaration(Declaration), Rank(Rank), 
-        Hidden(false) { }
+        Hidden(false), Qualifier(Qualifier) { }
     
     /// \brief Build a result that refers to a keyword or symbol.
     Result(const char *Keyword, unsigned Rank)

Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=82474&r1=82473&r2=82474&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Mon Sep 21 14:57:38 2009
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 #include "Sema.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/AST/ExprCXX.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include <list>
 #include <map>
@@ -328,6 +329,53 @@
   return 0;
 }
 
+/// \brief Compute the qualification required to get from the current context
+/// (\p CurContext) to the target context (\p TargetContext).
+///
+/// \param Context the AST context in which the qualification will be used.
+///
+/// \param CurContext the context where an entity is being named, which is
+/// typically based on the current scope.
+///
+/// \param TargetContext the context in which the named entity actually 
+/// resides.
+///
+/// \returns a nested name specifier that refers into the target context, or
+/// NULL if no qualification is needed.
+static NestedNameSpecifier *
+getRequiredQualification(ASTContext &Context,
+                         DeclContext *CurContext,
+                         DeclContext *TargetContext) {
+  llvm::SmallVector<DeclContext *, 4> TargetParents;
+  
+  for (DeclContext *CommonAncestor = TargetContext;
+       CommonAncestor && !CommonAncestor->Encloses(CurContext);
+       CommonAncestor = CommonAncestor->getLookupParent()) {
+    if (CommonAncestor->isTransparentContext() ||
+        CommonAncestor->isFunctionOrMethod())
+      continue;
+
+    TargetParents.push_back(CommonAncestor);
+  }
+  
+  NestedNameSpecifier *Result = 0;
+  while (!TargetParents.empty()) {
+    DeclContext *Parent = TargetParents.back();
+    TargetParents.pop_back();
+    
+    if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent))
+      Result = NestedNameSpecifier::Create(Context, Result, Namespace);
+    else if (TagDecl *TD = dyn_cast<TagDecl>(Parent))
+      Result = NestedNameSpecifier::Create(Context, Result,
+                                           false,
+                                    Context.getTypeDeclType(TD).getTypePtr());
+    else
+      assert(Parent->isTranslationUnit());
+  }
+  
+  return Result;
+}
+
 /// \brief Collect the results of searching for members within the given
 /// declaration context.
 ///
@@ -651,6 +699,22 @@
   }    
 }
 
+/// \brief Add a qualifier to the given code-completion string, if the
+/// provided nested-name-specifier is non-NULL.
+void AddQualifierToCompletionString(CodeCompletionString *Result, 
+                                    NestedNameSpecifier *Qualifier, 
+                                    ASTContext &Context) {
+  if (!Qualifier)
+    return;
+  
+  std::string PrintedNNS;
+  {
+    llvm::raw_string_ostream OS(PrintedNNS);
+    Qualifier->print(OS, Context.PrintingPolicy);
+  }
+  Result->AddTextChunk(PrintedNNS.c_str());
+}
+
 /// \brief If possible, create a new code completion string for the given
 /// result.
 ///
@@ -666,6 +730,7 @@
   
   if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) {
     CodeCompletionString *Result = new CodeCompletionString;
+    AddQualifierToCompletionString(Result, Qualifier, S.Context);
     Result->AddTextChunk(Function->getNameAsString().c_str());
     Result->AddTextChunk("(");
     AddFunctionParameterChunks(S.Context, Function, Result);
@@ -675,6 +740,7 @@
   
   if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) {
     CodeCompletionString *Result = new CodeCompletionString;
+    AddQualifierToCompletionString(Result, Qualifier, S.Context);
     FunctionDecl *Function = FunTmpl->getTemplatedDecl();
     Result->AddTextChunk(Function->getNameAsString().c_str());
     
@@ -727,6 +793,7 @@
   
   if (TemplateDecl *Template = dyn_cast<TemplateDecl>(ND)) {
     CodeCompletionString *Result = new CodeCompletionString;
+    AddQualifierToCompletionString(Result, Qualifier, S.Context);
     Result->AddTextChunk(Template->getNameAsString().c_str());
     Result->AddTextChunk("<");
     AddTemplateParameterChunks(S.Context, Template, Result);
@@ -734,6 +801,13 @@
     return Result;
   }
   
+  if (Qualifier) {
+    CodeCompletionString *Result = new CodeCompletionString;
+    AddQualifierToCompletionString(Result, Qualifier, S.Context);
+    Result->AddTextChunk(ND->getNameAsString().c_str());
+    return Result;
+  }
+  
   return 0;
 }
 
@@ -900,6 +974,7 @@
   // 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 = 0;
   for (SwitchCase *SC = Switch->getSwitchCaseList(); SC; 
        SC = SC->getNextSwitchCase()) {
     CaseStmt *Case = dyn_cast<CaseStmt>(SC);
@@ -918,21 +993,32 @@
         // 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.,
+        // 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.,
         //
         //   switch (TagD.getKind()) {
         //     case TagDecl::TK_enum:
         //       break;
         //     case XXX
         //
-        // At the XXX, we would like our completions to be TagDecl::TK_union,
+        // 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.
+        if (QualifiedDeclRefExpr *QDRE = dyn_cast<QualifiedDeclRefExpr>(DRE))
+          Qualifier = QDRE->getQualifier();
       }
   }
   
+  if (getLangOptions().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->getDeclContext());
+    
+    // FIXME: Scoped enums need to start with "EnumDecl" as the context!
+  }
+  
   // Add any enumerators that have not yet been mentioned.
   ResultBuilder Results(*this);
   Results.EnterNewScope();
@@ -942,17 +1028,10 @@
     if (EnumeratorsSeen.count(*E))
       continue;
     
-    Results.MaybeAddResult(CodeCompleteConsumer::Result(*E, 0));
+    Results.MaybeAddResult(CodeCompleteConsumer::Result(*E, 0, Qualifier));
   }
   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());  
 }
 

Added: cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp?rev=82474&view=auto

==============================================================================
--- cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp (added)
+++ cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp Mon Sep 21 14:57:38 2009
@@ -0,0 +1,33 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+
+namespace M {
+  
+namespace N {
+  struct C {
+    enum Color {
+      Red,
+      Orange,
+      Yellow,
+      Green,
+      Blue,
+      Indigo,
+      Violet
+    };
+  };
+}
+  
+}
+
+namespace M {
+  
+void test(enum N::C::Color color) {
+  switch (color) {
+      // CHECK-NEXT-CC1: Blue : 0 : N::C::Blue
+      // CHECK-NEXT-CC1: Green : 0 : N::C::Green
+      // CHECK-NEXT-CC1: Indigo : 0 : N::C::Indigo
+      // CHECK-NEXT-CC1: Orange : 0 : N::C::Orange
+      // CHECK-NEXT-CC1: Red : 0 : N::C::Red
+      // CHECK-NEXT-CC1: Violet : 0 : N::C::Violet
+      // CHECK-NEXT-CC1: Yellow : 0 : N::C::Yellow
+    case 

Added: cfe/trunk/test/CodeCompletion/enum-switch-case.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeCompletion/enum-switch-case.cpp?rev=82474&view=auto

==============================================================================
--- cfe/trunk/test/CodeCompletion/enum-switch-case.cpp (added)
+++ cfe/trunk/test/CodeCompletion/enum-switch-case.cpp Mon Sep 21 14:57:38 2009
@@ -0,0 +1,29 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+
+namespace N {
+  enum Color {
+    Red,
+    Orange,
+    Yellow,
+    Green,
+    Blue,
+    Indigo,
+    Violet
+  };
+}
+
+void test(enum N::Color color) {
+  switch (color) {
+    case N::Red:
+      break;
+      
+    case N::Yellow:
+      break;
+      
+      // CHECK-CC1: Blue : 0 : N::Blue
+      // CHECK-NEXT-CC1: Green : 0 : N::Green
+      // CHECK-NEXT-CC1: Indigo : 0 : N::Indigo
+      // CHECK-NEXT-CC1: Orange : 0 : N::Orange
+      // CHECK-NEXT-CC1: Violet : 0 : N::Violet
+    case 





More information about the cfe-commits mailing list