]> granicus.if.org Git - clang/commitdiff
Tweak the code-completion results ranking and formation, so that
authorDouglas Gregor <dgregor@apple.com>
Tue, 22 Sep 2009 23:15:58 +0000 (23:15 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 22 Sep 2009 23:15:58 +0000 (23:15 +0000)
members found in base classes have the same ranking as members found
in derived classes. However, we will introduce an informative note for
members found in base classes, showing (as a nested-name-specifier)
the qualification to name the base class, to make it clear which
members are from bases.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82586 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/CodeCompleteConsumer.h
lib/Sema/CodeCompleteConsumer.cpp
lib/Sema/SemaCodeComplete.cpp
test/CodeCompletion/member-access.cpp
test/CodeCompletion/namespace-alias.cpp
test/CodeCompletion/operator.cpp
test/CodeCompletion/tag.cpp
test/CodeCompletion/using-namespace.cpp
test/CodeCompletion/using.cpp

index 68e07e9608eed06371bfc1be88af3d1e3d6bed36..6a10b28be918c87605eb09c62782c6f73e679efe 100644 (file)
@@ -48,7 +48,10 @@ public:
     CK_Optional,
     /// \brief A string that acts as a placeholder for, e.g., a function 
     /// call argument.
-    CK_Placeholder
+    CK_Placeholder,
+    /// \brief A piece of text that describes something about the result but
+    /// should not be inserted into the buffer.
+    CK_Informative
   };
   
   /// \brief One piece of the code completion string.
@@ -58,7 +61,8 @@ public:
     ChunkKind Kind;
     
     union {
-      /// \brief The text string associated with a CK_Text chunk.
+      /// \brief The text string associated with a CK_Text, CK_Placeholder,
+      /// or CK_Informative chunk.
       /// The string is owned by the chunk and will be deallocated 
       /// (with delete[]) when the chunk is destroyed.
       const char *Text;
@@ -67,13 +71,14 @@ public:
       /// The optional code completion string is owned by the chunk, and will
       /// be deallocated (with delete) when the chunk is destroyed.
       CodeCompletionString *Optional;
-      
-      /// \brief Placeholder text associated with a CK_Placeholder chunk.
-      /// The string is owned by the chunk and will be deallocated (with 
-      /// delete[]) when the chunk is destroyed.
-      const char *Placeholder;
     };
     
+    Chunk() : Kind(CK_Text), Text(0) { }
+    
+  private:
+    Chunk(ChunkKind Kind, const char *Text);
+          
+  public:
     /// \brief Create a new text chunk.
     static Chunk CreateText(const char *Text);
 
@@ -82,7 +87,10 @@ public:
 
     /// \brief Create a new placeholder chunk.
     static Chunk CreatePlaceholder(const char *Placeholder);
-    
+
+    /// \brief Create a new informative chunk.
+    static Chunk CreateInformative(const char *Informative);
+
     /// \brief Destroy this chunk, deallocating any memory it owns.
     void Destroy();
   };
@@ -118,6 +126,12 @@ public:
   void AddPlaceholderChunk(const char *Placeholder) {
     Chunks.push_back(Chunk::CreatePlaceholder(Placeholder));
   }
+
+  /// \brief Add a new informative chunk.
+  /// The text will be copied.
+  void AddInformativeChunk(const char *Text) {
+    Chunks.push_back(Chunk::CreateInformative(Text));
+  }
   
   /// \brief Retrieve a string representation of the code completion string,
   /// which is mainly useful for debugging.
@@ -156,19 +170,26 @@ public:
     /// \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.
+    /// \brief Whether this result was found via lookup into a base class.
+    bool QualifierIsInformative : 1;
+    
+    /// \brief If the result should have a nested-name-specifier, this is it.
+    /// When \c QualifierIsInformative, the nested-name-specifier is 
+    /// informative rather than required.
     NestedNameSpecifier *Qualifier;
     
     /// \brief Build a result that refers to a declaration.
     Result(NamedDecl *Declaration, unsigned Rank, 
-           NestedNameSpecifier *Qualifier = 0)
+           NestedNameSpecifier *Qualifier = 0,
+           bool QualifierIsInformative = false)
       : Kind(RK_Declaration), Declaration(Declaration), Rank(Rank), 
-        Hidden(false), Qualifier(Qualifier) { }
+        Hidden(false), QualifierIsInformative(QualifierIsInformative), 
+        Qualifier(Qualifier) { }
     
     /// \brief Build a result that refers to a keyword or symbol.
     Result(const char *Keyword, unsigned Rank)
-      : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false) { }
+      : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false),
+        QualifierIsInformative(0), Qualifier(0) { }
     
     /// \brief Retrieve the declaration stored in this result.
     NamedDecl *getDeclaration() const {
index 1e505090fb99c5136ad7a0f1087203d61a4d741c..f490a2b5235a8334b980aed9ed59b9a78953bcf0 100644 (file)
@@ -26,14 +26,19 @@ using namespace clang;
 //===----------------------------------------------------------------------===//
 // Code completion string implementation
 //===----------------------------------------------------------------------===//
-CodeCompletionString::Chunk
-CodeCompletionString::Chunk::CreateText(const char *Text) {
-  Chunk Result;
-  Result.Kind = CK_Text;
+CodeCompletionString::Chunk::Chunk(ChunkKind Kind, const char *Text) 
+  : Kind(Kind), Text(0)
+{
+  assert((Kind == CK_Text || Kind == CK_Placeholder || Kind == CK_Informative)
+         && "Invalid text chunk kind");
   char *New = new char [std::strlen(Text) + 1];
   std::strcpy(New, Text);
-  Result.Text = New;
-  return Result;  
+  this->Text = New;
+}
+
+CodeCompletionString::Chunk
+CodeCompletionString::Chunk::CreateText(const char *Text) {
+  return Chunk(CK_Text, Text);
 }
 
 CodeCompletionString::Chunk 
@@ -47,20 +52,26 @@ CodeCompletionString::Chunk::CreateOptional(
 
 CodeCompletionString::Chunk 
 CodeCompletionString::Chunk::CreatePlaceholder(const char *Placeholder) {
-  Chunk Result;
-  Result.Kind = CK_Placeholder;
-  char *New = new char [std::strlen(Placeholder) + 1];
-  std::strcpy(New, Placeholder);
-  Result.Placeholder = New;
-  return Result;
+  return Chunk(CK_Placeholder, Placeholder);
+}
+
+CodeCompletionString::Chunk 
+CodeCompletionString::Chunk::CreateInformative(const char *Informative) {
+  return Chunk(CK_Informative, Informative);
 }
 
 void
 CodeCompletionString::Chunk::Destroy() {
   switch (Kind) {
-  case CK_Text: delete [] Text; break;
-  case CK_Optional: delete Optional; break;
-  case CK_Placeholder: delete [] Placeholder; break;
+  case CK_Optional: 
+    delete Optional; 
+    break;
+      
+  case CK_Text: 
+  case CK_Placeholder:
+  case CK_Informative:
+    delete [] Text; 
+    break;
   }
 }
 
@@ -77,7 +88,8 @@ std::string CodeCompletionString::getAsString() const {
     switch (C->Kind) {
     case CK_Text: OS << C->Text; break;
     case CK_Optional: OS << "{#" << C->Optional->getAsString() << "#}"; break;
-    case CK_Placeholder: OS << "<#" << C->Placeholder << "#>"; break;
+    case CK_Placeholder: OS << "<#" << C->Text << "#>"; break;
+    case CK_Informative: OS << "[#" << C->Text << "#]"; break;
     }
   }
   
index b19fda216d5c272c5d7ccba30fcb94d4501176eb..f57480e13e8106d7b229ae5edab63852428f3c1d 100644 (file)
@@ -197,8 +197,8 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) {
   
   // Look through using declarations.
   if (UsingDecl *Using = dyn_cast<UsingDecl>(R.Declaration))
-    return MaybeAddResult(Result(Using->getTargetDecl(), R.Rank, R.Qualifier),
-                          CurContext);
+    MaybeAddResult(Result(Using->getTargetDecl(), R.Rank, R.Qualifier),
+                   CurContext);
   
   // Handle each declaration in an overload set separately.
   if (OverloadedFunctionDecl *Ovl 
@@ -281,6 +281,7 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) {
                                  I->second.first)) {
         // Note that this result was hidden.
         R.Hidden = true;
+        R.QualifierIsInformative = false;
         
         if (!R.Qualifier)
           R.Qualifier = getRequiredQualification(SemaRef.Context, 
@@ -300,6 +301,18 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) {
   if (!AllDeclsFound.insert(CanonDecl))
     return;
   
+  // If this result is supposed to have an informative qualifier, add one.
+  if (R.QualifierIsInformative && !R.Qualifier) {
+    DeclContext *Ctx = R.Declaration->getDeclContext();
+    if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Ctx))
+      R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, 0, Namespace);
+    else if (TagDecl *Tag = dyn_cast<TagDecl>(Ctx))
+      R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, 0, false, 
+                             SemaRef.Context.getTypeDeclType(Tag).getTypePtr());
+    else
+      R.QualifierIsInformative = false;
+  }
+  
   // Insert this result into the set of results and into the current shadow
   // map.
   SMap.insert(std::make_pair(R.Declaration->getDeclName(),
@@ -398,9 +411,7 @@ static DeclContext *findOuterContext(Scope *S) {
 ///
 /// \param Ctx the declaration context from which we will gather results.
 ///
-/// \param InitialRank the initial rank given to results in this declaration
-/// context. Larger rank values will be used for, e.g., members found in
-/// base classes.
+/// \param Rank the rank given to results in this declaration context.
 ///
 /// \param Visited the set of declaration contexts that have already been
 /// visited. Declaration contexts will only be visited once.
@@ -408,18 +419,22 @@ static DeclContext *findOuterContext(Scope *S) {
 /// \param Results the result set that will be extended with any results
 /// found within this declaration context (and, for a C++ class, its bases).
 ///
+/// \param InBaseClass whether we are in a base class.
+///
 /// \returns the next higher rank value, after considering all of the
 /// names within this declaration context.
 static unsigned CollectMemberLookupResults(DeclContext *Ctx, 
-                                           unsigned InitialRank,
+                                           unsigned Rank,
                                            DeclContext *CurContext,
                                  llvm::SmallPtrSet<DeclContext *, 16> &Visited,
-                                           ResultBuilder &Results) {
+                                           ResultBuilder &Results,
+                                           bool InBaseClass = false) {
   // Make sure we don't visit the same context twice.
   if (!Visited.insert(Ctx->getPrimaryContext()))
-    return InitialRank;
+    return Rank;
   
   // Enumerate all of the results in this context.
+  typedef CodeCompleteConsumer::Result Result;
   Results.EnterNewScope();
   for (DeclContext *CurCtx = Ctx->getPrimaryContext(); CurCtx; 
        CurCtx = CurCtx->getNextContext()) {
@@ -427,13 +442,11 @@ static unsigned CollectMemberLookupResults(DeclContext *Ctx,
          DEnd = CurCtx->decls_end();
          D != DEnd; ++D) {
       if (NamedDecl *ND = dyn_cast<NamedDecl>(*D))
-        Results.MaybeAddResult(CodeCompleteConsumer::Result(ND, InitialRank),
-                               CurContext);
+        Results.MaybeAddResult(Result(ND, Rank, 0, InBaseClass), CurContext);
     }
   }
   
   // Traverse the contexts of inherited classes.
-  unsigned NextRank = InitialRank;
   if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx)) {
     for (CXXRecordDecl::base_class_iterator B = Record->bases_begin(),
          BEnd = Record->bases_end();
@@ -468,19 +481,15 @@ static unsigned CollectMemberLookupResults(DeclContext *Ctx,
       //   c->A::member
       
       // Collect results from this base class (and its bases).
-      NextRank = std::max(NextRank, 
-                          CollectMemberLookupResults(Record->getDecl(), 
-                                                     InitialRank + 1,
-                                                     CurContext,
-                                                     Visited,
-                                                     Results));
+      CollectMemberLookupResults(Record->getDecl(), Rank, CurContext, Visited,
+                                 Results, /*InBaseClass=*/true);
     }
   }
   
   // FIXME: Look into base classes in Objective-C!
   
   Results.ExitScope();
-  return NextRank;
+  return Rank + 1;
 }
 
 /// \brief Collect the results of searching for members within the given
@@ -735,6 +744,7 @@ static void AddTemplateParameterChunks(ASTContext &Context,
 /// provided nested-name-specifier is non-NULL.
 void AddQualifierToCompletionString(CodeCompletionString *Result, 
                                     NestedNameSpecifier *Qualifier, 
+                                    bool QualifierIsInformative,
                                     ASTContext &Context) {
   if (!Qualifier)
     return;
@@ -744,7 +754,10 @@ void AddQualifierToCompletionString(CodeCompletionString *Result,
     llvm::raw_string_ostream OS(PrintedNNS);
     Qualifier->print(OS, Context.PrintingPolicy);
   }
-  Result->AddTextChunk(PrintedNNS.c_str());
+  if (QualifierIsInformative)
+    Result->AddInformativeChunk(PrintedNNS.c_str());
+  else
+    Result->AddTextChunk(PrintedNNS.c_str());
 }
 
 /// \brief If possible, create a new code completion string for the given
@@ -762,7 +775,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
   
   if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) {
     CodeCompletionString *Result = new CodeCompletionString;
-    AddQualifierToCompletionString(Result, Qualifier, S.Context);
+    AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, 
+                                   S.Context);
     Result->AddTextChunk(Function->getNameAsString().c_str());
     Result->AddTextChunk("(");
     AddFunctionParameterChunks(S.Context, Function, Result);
@@ -772,7 +786,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
   
   if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) {
     CodeCompletionString *Result = new CodeCompletionString;
-    AddQualifierToCompletionString(Result, Qualifier, S.Context);
+    AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, 
+                                   S.Context);
     FunctionDecl *Function = FunTmpl->getTemplatedDecl();
     Result->AddTextChunk(Function->getNameAsString().c_str());
     
@@ -825,7 +840,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
   
   if (TemplateDecl *Template = dyn_cast<TemplateDecl>(ND)) {
     CodeCompletionString *Result = new CodeCompletionString;
-    AddQualifierToCompletionString(Result, Qualifier, S.Context);
+    AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, 
+                                   S.Context);
     Result->AddTextChunk(Template->getNameAsString().c_str());
     Result->AddTextChunk("<");
     AddTemplateParameterChunks(S.Context, Template, Result);
@@ -835,7 +851,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
   
   if (Qualifier) {
     CodeCompletionString *Result = new CodeCompletionString;
-    AddQualifierToCompletionString(Result, Qualifier, S.Context);
+    AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, 
+                                   S.Context);
     Result->AddTextChunk(ND->getNameAsString().c_str());
     return Result;
   }
index cbd19db1a5cd3dacfd68c794fc8405592ac92a84..b23436d8c8e4ce1610fbfff37e36a6c0cac0a5bb 100644 (file)
@@ -28,15 +28,15 @@ public:
 void test(const Proxy &p) {
   p->
   // RUN: clang-cc -fsyntax-only -code-completion-at=%s:29:6 %s -o - | FileCheck -check-prefix=CC1 %s &&
+  // CHECK-CC1: member1 : 0 : [#Base1::#]member1
+  // CHECK-CC1: member1 : 0 : [#Base2::#]member1
+  // CHECK-CC1: member2 : 0 : [#Base1::#]member2
+  // CHECK-CC1: member3 : 0
   // CHECK-CC1: member4 : 0
-  // CHECK-CC1: memfun3 : 0
-  // CHECK-CC1: memfun1 : 1
-  // CHECK-CC1: memfun1 : 1
-  // CHECK-CC1: memfun2 : 1
-  // CHECK-CC1: member1 : 2
-  // CHECK-CC1: member1 : 2
-  // CHECK-CC1: member2 : 2
-  // CHECK-CC1: member3 : 2
-  // CHECK-CC1: memfun1 : 2 (Hidden) : Base2::memfun1(<#int#>)
+  // CHECK-CC1: memfun1 : 0 : [#Base3::#]memfun1(<#float#>)
+  // CHECK-CC1: memfun1 : 0 : [#Base3::#]memfun1(<#double#>)
+  // CHECK-CC1: memfun2 : 0 : [#Base3::#]memfun2(<#int#>)
+  // CHECK-CC1: memfun3 : 0 : memfun3(<#int#>)
+  // CHECK-CC1: memfun1 : 0 (Hidden) : Base2::memfun1(<#int#>)
   // RUN: true
   
index cae3d561d113f8329ffb9757b04c8d1b4fa775d2..c92e554e5fc63e24cea1316b704660f296cacc54 100644 (file)
@@ -15,7 +15,7 @@ namespace N2 {
   // CHECK-CC1: I1 : 1
   // CHECK-CC1: I4 : 1
   // CHECK-CC1: I5 : 1
-  // CHECK-CC1: N2 : 2
-  // CHECK-NEXT-CC1: N4 : 2
+  // CHECK-CC1: N2 : 3
+  // CHECK-NEXT-CC1: N4 : 3
   // RUN: true
   
\ No newline at end of file
index 72a3f6bb719435df73b190598cb320a1e1522ea8..a3950f6b89137b11c7886d0bfc071473ce87a39b 100644 (file)
@@ -14,5 +14,5 @@ void f() {
   // CHECK-CC1: short : 0
   // CHECK-CC1: Integer : 2
   // CHECK-CC1: T : 2
-  // CHECK-CC1: N : 5
+  // CHECK-CC1: N : 6
   // RUN: true
index 201aec4dd38139eb32c8ee95da2c63e26dbf1a4c..2642b7c73176ebdc3b3ad131552da52d4a90994f 100644 (file)
@@ -18,9 +18,9 @@ namespace N {
     // RUN: clang-cc -fsyntax-only -code-completion-at=%s:17:10 %s -o - | FileCheck -check-prefix=CC1 %s &&
     // CHECK-CC1: Y : 2
     // CHECK-CC1: Z : 2
-    // CHECK-CC1: A : 3
-    // CHECK-CC1: X : 3
-    // CHECK-CC1: Y : 3
-    // CHECK-CC1: M : 6
-    // CHECK-CC1: N : 6
+    // CHECK-CC1: A : 4
+    // CHECK-CC1: X : 4
+    // CHECK-CC1: Y : 4
+    // CHECK-CC1: M : 9
+    // CHECK-CC1: N : 9
     // RUN: true
index 95bff9b5eebc249510bffb1dc8ea696a8631707b..3e8cd53723e9d31b0a13c438f70c00e3a6349273 100644 (file)
@@ -16,6 +16,6 @@ namespace N2 {
     // CHECK-CC1: I1 : 2
     // CHECK-CC1: I4 : 2
     // CHECK-CC1: I5 : 2
-    // CHECK-CC1: N2 : 3
-    // CHECK-NEXT-CC1: N4 : 3
+    // CHECK-CC1: N2 : 4
+    // CHECK-NEXT-CC1: N4 : 4
     // RUN: true
index 27b85fc7661b8c1e92e32ea3a9dce1e93f5d7001..dac556e151a741b141e395ae37dc8a0ac79de6d2 100644 (file)
@@ -18,8 +18,8 @@ namespace N2 {
     // CHECK-CC1: I1 : 2
     // CHECK-CC1: I4 : 2
     // CHECK-CC1: I5 : 2
-    // CHECK-CC1: N2 : 3
-    // CHECK-CC1: N3 : 3
-    // CHECK-NEXT-CC1: N4 : 3
+    // CHECK-CC1: N2 : 4
+    // CHECK-CC1: N3 : 4
+    // CHECK-NEXT-CC1: N4 : 4
     // RUN: true