Author: Hippolyte Melica
Date: 2026-03-22T22:25:54Z
New Revision: 98f84f9bf2dfdf8a77c025303e594ede8336eb8e

URL: 
https://github.com/llvm/llvm-project/commit/98f84f9bf2dfdf8a77c025303e594ede8336eb8e
DIFF: 
https://github.com/llvm/llvm-project/commit/98f84f9bf2dfdf8a77c025303e594ede8336eb8e.diff

LOG: [clangd] Code completion for declaration of class method (#165916)

Code completion previously could not tell apart the declaration of
a method from a call to it, and provided call-like behaviour even
in declaration contexts. This included things like not offering 
completion for private methods, and inserting placeholders for
the parameters as though the user was going to fill in arguments.

This patch adds support to Parser and SemaCodeComplete to
detect and provide dedicated behaviour for declaration contexts.
In these contexts, the flag CodeCompletionResult::DeclaringEntity
is set, and createCodeCompletionString() is adjusted to handle this
flag, e.g. by inserting parameter declarations as text chunks rather
than placeholder chunks.

The DeclaringEntity flag is also available for consumers of
SemaCodeComplete, such as clangd, to customize their behaviour.

In addition, the patch tweaks the conditions under which the
existing CodeCompletionResult::FunctionCanBeCall flag is set to
be more accurate, excluding declaration contexts and cases where
the address of the function is likely being taken.

Fixes clangd/clangd#753
Fixes clangd/clangd#880
Fixes clangd/clangd#1752

Added: 
    

Modified: 
    clang-tools-extra/clangd/CodeComplete.cpp
    clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/SemaCodeCompletion.h
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Parse/ParseExpr.cpp
    clang/lib/Parse/ParseExprCXX.cpp
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/SemaCodeComplete.cpp
    clang/test/CodeCompletion/cpp23-explicit-object.cpp
    clang/test/CodeCompletion/member-access.cpp
    clang/test/Index/complete-qualified.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index 7c390f9c8219d..28f81cd5267d5 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -477,10 +477,12 @@ struct CodeCompletionBuilder {
     BundledEntry &S = Bundled.back();
     bool IsConcept = false;
     if (C.SemaResult) {
-      getSignature(*SemaCCS, &S.Signature, &S.SnippetSuffix, 
C.SemaResult->Kind,
-                   C.SemaResult->CursorKind,
-                   
/*IncludeFunctionArguments=*/C.SemaResult->FunctionCanBeCall,
-                   /*RequiredQualifiers=*/&Completion.RequiredQualifier);
+      getSignature(
+          *SemaCCS, &S.Signature, &S.SnippetSuffix, C.SemaResult->Kind,
+          C.SemaResult->CursorKind,
+          /*IncludeFunctionArguments=*/C.SemaResult->FunctionCanBeCall ||
+              C.SemaResult->DeclaringEntity,
+          /*RequiredQualifiers=*/&Completion.RequiredQualifier);
       S.ReturnType = getReturnType(*SemaCCS);
       if (C.SemaResult->Kind == CodeCompletionResult::RK_Declaration)
         if (const auto *D = C.SemaResult->getDeclaration())

diff  --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp 
b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 31f2d8bd68703..386ffb54924a7 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -531,19 +531,26 @@ TEST(CompletionTest, 
HeuristicsForMemberFunctionCompletion) {
 
   Annotations Code(R"cpp(
       struct Foo {
-        static int staticMethod(int);
-        int method(int) const;
+        static int staticMethod(int name);
+        int method(int name) const;
         template <typename T, typename U, typename V = int>
-        T generic(U, V);
+        T generic(U nameU, V nameV);
         template <typename T, int U>
         static T staticGeneric();
         Foo() {
-          this->$canBeCall^
+          this->$canBeCallNoStatic^
           $canBeCall^
           Foo::$canBeCall^
         }
       };
 
+      int Foo::$isDefinition^ {
+      }
+      ;
+
+      int i = Foo::$canBeCallStaticOnly^
+      ;
+
       struct Derived : Foo {
         using Foo::method;
         using Foo::generic;
@@ -556,9 +563,10 @@ TEST(CompletionTest, 
HeuristicsForMemberFunctionCompletion) {
         OtherClass() {
           Foo f;
           Derived d;
-          f.$canBeCall^
+          f.$canBeCallNoStatic^
           ; // Prevent parsing as 'f.f'
           f.Foo::$canBeCall^
+          ;
           &Foo::$canNotBeCall^
           ;
           d.Foo::$canBeCall^
@@ -573,6 +581,7 @@ TEST(CompletionTest, HeuristicsForMemberFunctionCompletion) 
{
         f.$canBeCall^
         ; // Prevent parsing as 'f.f'
         f.Foo::$canBeCall^
+        ;
         &Foo::$canNotBeCall^
         ;
         d.Foo::$canBeCall^
@@ -585,39 +594,129 @@ TEST(CompletionTest, 
HeuristicsForMemberFunctionCompletion) {
   for (const auto &P : Code.points("canNotBeCall")) {
     auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
     EXPECT_THAT(Results.Completions,
-                Contains(AllOf(named("method"), signature("(int) const"),
+                Contains(AllOf(named("method"), signature("(int name) const"),
                                snippetSuffix(""))));
     // We don't have any arguments to deduce against if this isn't a call.
-    // Thus, we should emit these deducible template arguments explicitly.
     EXPECT_THAT(
         Results.Completions,
         Contains(AllOf(named("generic"),
-                       signature("<typename T, typename U>(U, V)"),
+                       signature("<typename T, typename U>(U nameU, V nameV)"),
                        snippetSuffix("<${1:typename T}, ${2:typename U}>"))));
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(named("staticMethod"), signature("(int name)"),
+                               snippetSuffix(""))));
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(
+                    named("staticGeneric"), signature("<typename T, int U>()"),
+                    snippetSuffix("<${1:typename T}, ${2:int U}>"))));
   }
 
   for (const auto &P : Code.points("canBeCall")) {
     auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
     EXPECT_THAT(Results.Completions,
-                Contains(AllOf(named("method"), signature("(int) const"),
-                               snippetSuffix("(${1:int})"))));
+                Contains(AllOf(named("method"), signature("(int name) const"),
+                               snippetSuffix("(${1:int name})"))));
     EXPECT_THAT(
         Results.Completions,
-        Contains(AllOf(named("generic"), signature("<typename T>(U, V)"),
-                       snippetSuffix("<${1:typename T}>(${2:U}, ${3:V})"))));
+        Contains(AllOf(
+            named("generic"), signature("<typename T>(U nameU, V nameV)"),
+            snippetSuffix("<${1:typename T}>(${2:U nameU}, ${3:V nameV})"))));
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(named("staticMethod"), signature("(int name)"),
+                               snippetSuffix("(${1:int name})"))));
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(
+                    named("staticGeneric"), signature("<typename T, int U>()"),
+                    snippetSuffix("<${1:typename T}, ${2:int U}>()"))));
   }
 
-  // static method will always keep the snippet
-  for (const auto &P : Code.points()) {
+  for (const auto &P : Code.points("canBeCallNoStatic")) {
     auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
     EXPECT_THAT(Results.Completions,
-                Contains(AllOf(named("staticMethod"), signature("(int)"),
-                               snippetSuffix("(${1:int})"))));
+                Contains(AllOf(named("method"), signature("(int name) const"),
+                               snippetSuffix("(${1:int name})"))));
+    EXPECT_THAT(
+        Results.Completions,
+        Contains(AllOf(
+            named("generic"), signature("<typename T>(U nameU, V nameV)"),
+            snippetSuffix("<${1:typename T}>(${2:U nameU}, ${3:V nameV})"))));
+  }
+
+  for (const auto &P : Code.points("canBeCallStaticOnly")) {
+    auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(named("method"), signature("(int name) const"),
+                               snippetSuffix(""))));
+    EXPECT_THAT(
+        Results.Completions,
+        Contains(AllOf(named("generic"),
+                       signature("<typename T, typename U>(U nameU, V nameV)"),
+                       snippetSuffix("<${1:typename T}, ${2:typename U}>"))));
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(named("staticMethod"), signature("(int name)"),
+                               snippetSuffix("(${1:int name})"))));
     EXPECT_THAT(Results.Completions,
                 Contains(AllOf(
                     named("staticGeneric"), signature("<typename T, int U>()"),
                     snippetSuffix("<${1:typename T}, ${2:int U}>()"))));
   }
+
+  for (const auto &P : Code.points("isDefinition")) {
+    auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
+
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(named("method"), signature("(int name) const"),
+                               snippetSuffix("(int name) const"))));
+    EXPECT_THAT(
+        Results.Completions,
+        Contains(AllOf(named("generic"),
+                       signature("<typename T, typename U>(U nameU, V nameV)"),
+                       snippetSuffix("(U nameU, V nameV)"))));
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(named("staticMethod"), signature("(int name)"),
+                               snippetSuffix("(int name)"))));
+    EXPECT_THAT(Results.Completions,
+                Contains(AllOf(named("staticGeneric"),
+                               signature("<typename T, int U>()"),
+                               snippetSuffix("()"))));
+  }
+}
+
+TEST(CompletionTest, PrivateMemberDefinition) {
+  clangd::CodeCompleteOptions Opts;
+  Opts.EnableSnippets = true;
+  auto Results = completions(
+      R"cpp(
+    class Foo {
+        int func(int a, int b);
+    };
+    int Foo::func^
+  )cpp",
+      /*IndexSymbols=*/{}, Opts);
+  EXPECT_THAT(Results.Completions,
+              Contains(AllOf(named("func"), signature("(int a, int b)"),
+                             snippetSuffix("(int a, int b)"))));
+}
+
+TEST(CompletionTest, DefaultArgsWithValues) {
+  clangd::CodeCompleteOptions Opts;
+  Opts.EnableSnippets = true;
+  auto Results = completions(
+      R"cpp(
+    struct Arg {
+        Arg(int a, int b);
+    };
+    struct Foo {
+      void foo(int x = 42, int y = 0, Arg arg = Arg(42,  0));
+    };
+    void Foo::foo^
+  )cpp",
+      /*IndexSymbols=*/{}, Opts);
+  EXPECT_THAT(Results.Completions,
+              Contains(AllOf(
+                  named("foo"),
+                  signature("(int x = 42, int y = 0, Arg arg = Arg(42,  0))"),
+                  snippetSuffix("(int x, int y, Arg arg)"))));
 }
 
 TEST(CompletionTest, NoSnippetsInUsings) {

diff  --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 5ae02e2b4e8ad..08a3d88ee6a36 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -354,9 +354,20 @@ class Parser : public CodeCompletionHandler {
   /// Note that this routine emits an error if you call it with ::new or
   /// ::delete as the current tokens, so only call it in contexts where these
   /// are invalid.
+  ///
+  /// \param IsAddressOfOperand A hint indicating whether the current token
+  /// sequence is likely part of an address-of operation. Used by code
+  /// completion to filter results; may not be set by all callers.
   bool
   TryAnnotateTypeOrScopeToken(ImplicitTypenameContext AllowImplicitTypename =
-                                  ImplicitTypenameContext::No);
+                                  ImplicitTypenameContext::No,
+                              bool IsAddressOfOperand = false);
+
+  bool TryAnnotateTypeOrScopeToken(bool IsAddressOfOperand) {
+    return TryAnnotateTypeOrScopeToken(
+        /*AllowImplicitTypename=*/ImplicitTypenameContext::No,
+        /*IsAddressOfOperand=*/IsAddressOfOperand);
+  }
 
   /// Try to annotate a type or scope token, having already parsed an
   /// optional scope specifier. \p IsNewScope should be \c true unless the 
scope
@@ -3849,6 +3860,9 @@ class Parser : public CodeCompletionHandler {
   /// of address-of gets special treatment due to member pointers. NotCastExpr
   /// is set to true if the token is not the start of a cast-expression, and no
   /// diagnostic is emitted in this case and no tokens are consumed.
+  /// In addition, isAddressOfOperand is propagated to SemaCodeCompletion
+  /// as a heuristic for function completions (to provide 
diff erent behavior
+  /// when the user is likely taking the address of a function vs. calling it).
   ///
   /// \verbatim
   ///       cast-expression: [C99 6.5.4]
@@ -4561,6 +4575,14 @@ class Parser : public CodeCompletionHandler {
   ///
   /// \param OnlyNamespace If true, only considers namespaces in lookup.
   ///
+  /// \param IsAddressOfOperand A hint indicating the expression is part of
+  /// an address-of operation (e.g. '&'). Used by code completion to filter
+  /// results; may not be set by all callers.
+  ///
+  /// \param IsInDeclarationContext A hint indicating whether the current
+  /// context is likely a declaration. Used by code completion to filter
+  /// results; may not be set by all callers.
+  ///
   ///
   /// \returns true if there was an error parsing a scope specifier
   bool ParseOptionalCXXScopeSpecifier(
@@ -4568,7 +4590,23 @@ class Parser : public CodeCompletionHandler {
       bool EnteringContext, bool *MayBePseudoDestructor = nullptr,
       bool IsTypename = false, const IdentifierInfo **LastII = nullptr,
       bool OnlyNamespace = false, bool InUsingDeclaration = false,
-      bool Disambiguation = false);
+      bool Disambiguation = false, bool IsAddressOfOperand = false,
+      bool IsInDeclarationContext = false);
+
+  bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, ParsedType ObjectType,
+                                      bool ObjectHasErrors,
+                                      bool EnteringContext,
+                                      bool IsAddressOfOperand) {
+    return ParseOptionalCXXScopeSpecifier(
+        SS, ObjectType, ObjectHasErrors, EnteringContext,
+        /*MayBePseudoDestructor=*/nullptr,
+        /*IsTypename=*/false,
+        /*LastII=*/nullptr,
+        /*OnlyNamespace=*/false,
+        /*InUsingDeclaration=*/false,
+        /*Disambiguation=*/false,
+        /*IsAddressOfOperand=*/IsAddressOfOperand);
+  }
 
   
//===--------------------------------------------------------------------===//
   // C++11 5.1.2: Lambda expressions

diff  --git a/clang/include/clang/Sema/SemaCodeCompletion.h 
b/clang/include/clang/Sema/SemaCodeCompletion.h
index 3029e56e5cfe2..abdfb51900318 100644
--- a/clang/include/clang/Sema/SemaCodeCompletion.h
+++ b/clang/include/clang/Sema/SemaCodeCompletion.h
@@ -101,9 +101,11 @@ class SemaCodeCompletion : public SemaBase {
                             bool AllowNestedNameSpecifiers);
 
   struct CodeCompleteExpressionData;
-  void CodeCompleteExpression(Scope *S, const CodeCompleteExpressionData 
&Data);
+  void CodeCompleteExpression(Scope *S, const CodeCompleteExpressionData &Data,
+                              bool IsAddressOfOperand = false);
   void CodeCompleteExpression(Scope *S, QualType PreferredType,
-                              bool IsParenthesized = false);
+                              bool IsParenthesized = false,
+                              bool IsAddressOfOperand = false);
   void CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base, Expr *OtherOpBase,
                                        SourceLocation OpLoc, bool IsArrow,
                                        bool IsBaseExprStatement,
@@ -156,7 +158,8 @@ class SemaCodeCompletion : public SemaBase {
   void CodeCompleteAfterIf(Scope *S, bool IsBracedThen);
 
   void CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS, bool 
EnteringContext,
-                               bool IsUsingDeclaration, QualType BaseType,
+                               bool IsUsingDeclaration, bool 
IsAddressOfOperand,
+                               bool IsInDeclarationContext, QualType BaseType,
                                QualType PreferredType);
   void CodeCompleteUsing(Scope *S);
   void CodeCompleteUsingDirective(Scope *S);

diff  --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 72935f427b7f8..d448cb8a552bc 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -6415,7 +6415,9 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
                                        /*IsTypename=*/false, 
/*LastII=*/nullptr,
                                        /*OnlyNamespace=*/false,
                                        /*InUsingDeclaration=*/false,
-                                       /*Disambiguation=*/EnteringContext) ||
+                                       /*Disambiguation=*/EnteringContext,
+                                       /*IsAddressOfOperand=*/false,
+                                       /*IsInDeclarationContext=*/true) ||
 
         SS.isEmpty() || SS.isInvalid() || !EnteringContext ||
         Tok.is(tok::star)) {

diff  --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index be6c7824cdbae..9c4dfe83fd622 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -923,7 +923,7 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool 
isAddressOfOperand,
                Next.isOneOf(tok::coloncolon, tok::less, tok::l_paren,
                             tok::l_brace)) {
         // If TryAnnotateTypeOrScopeToken annotates the token, tail recurse.
-        if (TryAnnotateTypeOrScopeToken())
+        if (TryAnnotateTypeOrScopeToken(isAddressOfOperand))
           return ExprError();
         if (!Tok.is(tok::identifier))
           return ParseCastExpression(ParseKind, isAddressOfOperand, 
NotCastExpr,
@@ -1560,7 +1560,8 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool 
isAddressOfOperand,
   case tok::code_completion: {
     cutOffParsing();
     Actions.CodeCompletion().CodeCompleteExpression(
-        getCurScope(), PreferredType.get(Tok.getLocation()));
+        getCurScope(), PreferredType.get(Tok.getLocation()),
+        /*IsParenthesized=*/false, /*IsAddressOfOperand=*/isAddressOfOperand);
     return ExprError();
   }
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:

diff  --git a/clang/lib/Parse/ParseExprCXX.cpp 
b/clang/lib/Parse/ParseExprCXX.cpp
index b3d50daf66b10..d0d9def01a4c2 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -108,7 +108,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
     CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
     bool EnteringContext, bool *MayBePseudoDestructor, bool IsTypename,
     const IdentifierInfo **LastII, bool OnlyNamespace, bool InUsingDeclaration,
-    bool Disambiguation) {
+    bool Disambiguation, bool IsAddressOfOperand, bool IsInDeclarationContext) 
{
   assert(getLangOpts().CPlusPlus &&
          "Call sites of this function should be guarded by checking for C++");
 
@@ -237,7 +237,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
         // completion token follows the '::'.
         Actions.CodeCompletion().CodeCompleteQualifiedId(
             getCurScope(), SS, EnteringContext, InUsingDeclaration,
-            ObjectType.get(), SavedType.get(SS.getBeginLoc()));
+            IsAddressOfOperand, IsInDeclarationContext, ObjectType.get(),
+            SavedType.get(SS.getBeginLoc()));
         // Include code completion token into the range of the scope otherwise
         // when we try to annotate the scope tokens the dangling code 
completion
         // token will cause assertion in

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 5d18414b1a746..c4f745612e06c 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1853,7 +1853,7 @@ bool Parser::TryKeywordIdentFallback(bool DisableKeyword) 
{
 }
 
 bool Parser::TryAnnotateTypeOrScopeToken(
-    ImplicitTypenameContext AllowImplicitTypename) {
+    ImplicitTypenameContext AllowImplicitTypename, bool IsAddressOfOperand) {
   assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
           Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) ||
           Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) ||
@@ -1969,9 +1969,11 @@ bool Parser::TryAnnotateTypeOrScopeToken(
 
   CXXScopeSpec SS;
   if (getLangOpts().CPlusPlus)
-    if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
-                                       /*ObjectHasErrors=*/false,
-                                       /*EnteringContext*/ false))
+    if (ParseOptionalCXXScopeSpecifier(
+            SS, /*ObjectType=*/nullptr,
+            /*ObjectHasErrors=*/false,
+            /*EnteringContext=*/false,
+            /*IsAddressOfOperand=*/IsAddressOfOperand))
       return true;
 
   return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation,

diff  --git a/clang/lib/Sema/SemaCodeComplete.cpp 
b/clang/lib/Sema/SemaCodeComplete.cpp
index 98d78d2a461f1..0cd9819dc2964 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -369,7 +369,8 @@ class ResultBuilder {
   /// \param BaseExprType the type of expression that precedes the "." or "->"
   /// in a member access expression.
   void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding,
-                 bool InBaseClass, QualType BaseExprType);
+                 bool InBaseClass, QualType BaseExprType,
+                 bool IsInDeclarationContext, bool IsAddressOfOperand);
 
   /// Add a new non-declaration result to this result set.
   void AddResult(Result R);
@@ -1365,7 +1366,9 @@ bool ResultBuilder::canFunctionBeCalled(const NamedDecl 
*ND,
 
 void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
                               NamedDecl *Hiding, bool InBaseClass = false,
-                              QualType BaseExprType = QualType()) {
+                              QualType BaseExprType = QualType(),
+                              bool IsInDeclarationContext = false,
+                              bool IsAddressOfOperand = false) {
   if (R.Kind != Result::RK_Declaration) {
     // For non-declaration results, just add the result.
     Results.push_back(R);
@@ -1503,8 +1506,13 @@ void ResultBuilder::AddResult(Result R, DeclContext 
*CurContext,
         }
         OverloadSet.Add(Method, Results.size());
       }
-
-  R.FunctionCanBeCall = canFunctionBeCalled(R.getDeclaration(), BaseExprType);
+  R.DeclaringEntity = IsInDeclarationContext;
+  R.FunctionCanBeCall =
+      canFunctionBeCalled(R.getDeclaration(), BaseExprType) &&
+      // If the user wrote `&` before the function name, assume the
+      // user is more likely to take the address of the function rather
+      // than call it and take the address of the result.
+      !IsAddressOfOperand;
 
   // Insert this result into the set of results.
   Results.push_back(R);
@@ -1755,6 +1763,9 @@ class CodeCompletionDeclConsumer : public 
VisibleDeclConsumer {
   CXXRecordDecl *NamingClass;
   QualType BaseType;
   std::vector<FixItHint> FixIts;
+  bool IsInDeclarationContext;
+  // Completion is invoked after an identifier preceded by '&'.
+  bool IsAddressOfOperand;
 
 public:
   CodeCompletionDeclConsumer(
@@ -1762,7 +1773,8 @@ class CodeCompletionDeclConsumer : public 
VisibleDeclConsumer {
       QualType BaseType = QualType(),
       std::vector<FixItHint> FixIts = std::vector<FixItHint>())
       : Results(Results), InitialLookupCtx(InitialLookupCtx),
-        FixIts(std::move(FixIts)) {
+        FixIts(std::move(FixIts)), IsInDeclarationContext(false),
+        IsAddressOfOperand(false) {
     NamingClass = llvm::dyn_cast<CXXRecordDecl>(InitialLookupCtx);
     // If BaseType was not provided explicitly, emulate implicit 'this->'.
     if (BaseType.isNull()) {
@@ -1777,13 +1789,22 @@ class CodeCompletionDeclConsumer : public 
VisibleDeclConsumer {
     this->BaseType = BaseType;
   }
 
+  void setIsInDeclarationContext(bool IsInDeclarationContext) {
+    this->IsInDeclarationContext = IsInDeclarationContext;
+  }
+
+  void setIsAddressOfOperand(bool IsAddressOfOperand) {
+    this->IsAddressOfOperand = IsAddressOfOperand;
+  }
+
   void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
                  bool InBaseClass) override {
     ResultBuilder::Result Result(ND, Results.getBasePriority(ND),
                                  /*Qualifier=*/std::nullopt,
                                  /*QualifierIsInformative=*/false,
                                  IsAccessible(ND, Ctx), FixIts);
-    Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass, BaseType);
+    Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass, BaseType,
+                      IsInDeclarationContext, IsAddressOfOperand);
   }
 
   void EnteredContext(DeclContext *Ctx) override {
@@ -3273,18 +3294,19 @@ static std::string GetDefaultValueString(const 
ParmVarDecl *Param,
 }
 
 /// Add function parameter chunks to the given code completion string.
-static void AddFunctionParameterChunks(Preprocessor &PP,
-                                       const PrintingPolicy &Policy,
-                                       const FunctionDecl *Function,
-                                       CodeCompletionBuilder &Result,
-                                       unsigned Start = 0,
-                                       bool InOptional = false) {
+static void AddFunctionParameterChunks(
+    Preprocessor &PP, const PrintingPolicy &Policy,
+    const FunctionDecl *Function, CodeCompletionBuilder &Result,
+    unsigned Start = 0, bool InOptional = false, bool FunctionCanBeCall = true,
+    bool IsInDeclarationContext = false) {
   bool FirstParameter = true;
+  bool AsInformativeChunk = !(FunctionCanBeCall || IsInDeclarationContext);
 
   for (unsigned P = Start, N = Function->getNumParams(); P != N; ++P) {
     const ParmVarDecl *Param = Function->getParamDecl(P);
 
-    if (Param->hasDefaultArg() && !InOptional) {
+    if (Param->hasDefaultArg() && !InOptional && !IsInDeclarationContext &&
+        !AsInformativeChunk) {
       // When we see an optional default argument, put that argument and
       // the remaining default arguments into a new, optional string.
       CodeCompletionBuilder Opt(Result.getAllocator(),
@@ -3305,23 +3327,42 @@ static void AddFunctionParameterChunks(Preprocessor &PP,
 
     if (FirstParameter)
       FirstParameter = false;
-    else
-      Result.AddChunk(CodeCompletionString::CK_Comma);
+    else {
+      if (AsInformativeChunk)
+        Result.AddInformativeChunk(", ");
+      else
+        Result.AddChunk(CodeCompletionString::CK_Comma);
+    }
 
     InOptional = false;
 
     // Format the placeholder string.
     std::string PlaceholderStr = FormatFunctionParameter(Policy, Param);
-    if (Param->hasDefaultArg())
-      PlaceholderStr +=
-          GetDefaultValueString(Param, PP.getSourceManager(), 
PP.getLangOpts());
+    std::string DefaultValue;
+    if (Param->hasDefaultArg()) {
+      if (IsInDeclarationContext)
+        DefaultValue = GetDefaultValueString(Param, PP.getSourceManager(),
+                                             PP.getLangOpts());
+      else
+        PlaceholderStr += GetDefaultValueString(Param, PP.getSourceManager(),
+                                                PP.getLangOpts());
+    }
 
     if (Function->isVariadic() && P == N - 1)
       PlaceholderStr += ", ...";
 
     // Add the placeholder string.
-    Result.AddPlaceholderChunk(
-        Result.getAllocator().CopyString(PlaceholderStr));
+    if (AsInformativeChunk)
+      Result.AddInformativeChunk(
+          Result.getAllocator().CopyString(PlaceholderStr));
+    else if (IsInDeclarationContext) { // No placeholders in declaration 
context
+      Result.AddTextChunk(Result.getAllocator().CopyString(PlaceholderStr));
+      if (DefaultValue.length() != 0)
+        Result.AddInformativeChunk(
+            Result.getAllocator().CopyString(DefaultValue));
+    } else
+      Result.AddPlaceholderChunk(
+          Result.getAllocator().CopyString(PlaceholderStr));
   }
 
   if (const auto *Proto = Function->getType()->getAs<FunctionProtoType>())
@@ -3337,7 +3378,8 @@ static void AddFunctionParameterChunks(Preprocessor &PP,
 static void AddTemplateParameterChunks(
     ASTContext &Context, const PrintingPolicy &Policy,
     const TemplateDecl *Template, CodeCompletionBuilder &Result,
-    unsigned MaxParameters = 0, unsigned Start = 0, bool InDefaultArg = false) 
{
+    unsigned MaxParameters = 0, unsigned Start = 0, bool InDefaultArg = false,
+    bool AsInformativeChunk = false) {
   bool FirstParameter = true;
 
   // Prefer to take the template parameter names from the first declaration of
@@ -3388,7 +3430,7 @@ static void AddTemplateParameterChunks(
       HasDefaultArg = TTP->hasDefaultArgument();
     }
 
-    if (HasDefaultArg && !InDefaultArg) {
+    if (HasDefaultArg && !InDefaultArg && !AsInformativeChunk) {
       // When we see an optional default argument, put that argument and
       // the remaining default arguments into a new, optional string.
       CodeCompletionBuilder Opt(Result.getAllocator(),
@@ -3405,12 +3447,19 @@ static void AddTemplateParameterChunks(
 
     if (FirstParameter)
       FirstParameter = false;
-    else
-      Result.AddChunk(CodeCompletionString::CK_Comma);
+    else {
+      if (AsInformativeChunk)
+        Result.AddInformativeChunk(", ");
+      else
+        Result.AddChunk(CodeCompletionString::CK_Comma);
+    }
 
-    // Add the placeholder string.
-    Result.AddPlaceholderChunk(
-        Result.getAllocator().CopyString(PlaceholderStr));
+    if (AsInformativeChunk)
+      Result.AddInformativeChunk(
+          Result.getAllocator().CopyString(PlaceholderStr));
+    else // Add the placeholder string.
+      Result.AddPlaceholderChunk(
+          Result.getAllocator().CopyString(PlaceholderStr));
   }
 }
 
@@ -3436,22 +3485,32 @@ static void 
AddQualifierToCompletionString(CodeCompletionBuilder &Result,
 }
 
 static void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
-                                 const Qualifiers Quals) {
+                                 const Qualifiers Quals,
+                                 bool AsInformativeChunk = true) {
   // FIXME: Add ref-qualifier!
 
   // Handle single qualifiers without copying
   if (Quals.hasOnlyConst()) {
-    Result.AddInformativeChunk(" const");
+    if (AsInformativeChunk)
+      Result.AddInformativeChunk(" const");
+    else
+      Result.AddTextChunk(" const");
     return;
   }
 
   if (Quals.hasOnlyVolatile()) {
-    Result.AddInformativeChunk(" volatile");
+    if (AsInformativeChunk)
+      Result.AddInformativeChunk(" volatile");
+    else
+      Result.AddTextChunk(" volatile");
     return;
   }
 
   if (Quals.hasOnlyRestrict()) {
-    Result.AddInformativeChunk(" restrict");
+    if (AsInformativeChunk)
+      Result.AddInformativeChunk(" restrict");
+    else
+      Result.AddTextChunk(" restrict");
     return;
   }
 
@@ -3463,12 +3522,17 @@ static void AddFunctionTypeQuals(CodeCompletionBuilder 
&Result,
     QualsStr += " volatile";
   if (Quals.hasRestrict())
     QualsStr += " restrict";
-  Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
+
+  if (AsInformativeChunk)
+    Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
+  else
+    Result.AddTextChunk(Result.getAllocator().CopyString(QualsStr));
 }
 
 static void
 AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
-                                       const FunctionDecl *Function) {
+                                       const FunctionDecl *Function,
+                                       bool AsInformativeChunks = true) {
   if (auto *CxxMethodDecl = llvm::dyn_cast_if_present<CXXMethodDecl>(Function);
       CxxMethodDecl && CxxMethodDecl->hasCXXExplicitFunctionObjectParameter()) 
{
     // if explicit object method, infer quals from the object parameter
@@ -3476,13 +3540,13 @@ 
AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
     if (!Quals.hasQualifiers())
       return;
 
-    AddFunctionTypeQuals(Result, Quals.getQualifiers());
+    AddFunctionTypeQuals(Result, Quals.getQualifiers(), AsInformativeChunks);
   } else {
     const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
     if (!Proto || !Proto->getMethodQuals())
       return;
 
-    AddFunctionTypeQuals(Result, Proto->getMethodQuals());
+    AddFunctionTypeQuals(Result, Proto->getMethodQuals(), AsInformativeChunks);
   }
 }
 
@@ -3774,10 +3838,21 @@ CodeCompletionString 
*CodeCompletionResult::createCodeCompletionStringForDecl(
     AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
                                    Ctx, Policy);
     AddTypedNameChunk(Ctx, Policy, ND, Result);
-    Result.AddChunk(CodeCompletionString::CK_LeftParen);
-    AddFunctionParameterChunks(PP, Policy, Function, Result);
-    Result.AddChunk(CodeCompletionString::CK_RightParen);
-    AddFunctionTypeQualsToCompletionString(Result, Function);
+    bool InsertParameters = FunctionCanBeCall || DeclaringEntity;
+    if (InsertParameters)
+      Result.AddChunk(CodeCompletionString::CK_LeftParen);
+    else
+      Result.AddInformativeChunk("(");
+    AddFunctionParameterChunks(PP, Policy, Function, Result, /*Start=*/0,
+                               /*InOptional=*/false,
+                               /*FunctionCanBeCall=*/FunctionCanBeCall,
+                               /*IsInDeclarationContext=*/DeclaringEntity);
+    if (InsertParameters)
+      Result.AddChunk(CodeCompletionString::CK_RightParen);
+    else
+      Result.AddInformativeChunk(")");
+    AddFunctionTypeQualsToCompletionString(
+        Result, Function, /*AsInformativeChunks=*/!DeclaringEntity);
   };
 
   if (const auto *Function = dyn_cast<FunctionDecl>(ND)) {
@@ -3849,16 +3924,35 @@ CodeCompletionString 
*CodeCompletionResult::createCodeCompletionStringForDecl(
       // e.g.,
       // template <class T> void foo(T);
       // void (*f)(int) = foo;
-      Result.AddChunk(CodeCompletionString::CK_LeftAngle);
-      AddTemplateParameterChunks(Ctx, Policy, FunTmpl, Result,
-                                 LastDeducibleArgument);
-      Result.AddChunk(CodeCompletionString::CK_RightAngle);
+      if (!DeclaringEntity)
+        Result.AddChunk(CodeCompletionString::CK_LeftAngle);
+      else
+        Result.AddInformativeChunk("<");
+      AddTemplateParameterChunks(
+          Ctx, Policy, FunTmpl, Result, LastDeducibleArgument, /*Start=*/0,
+          /*InDefaultArg=*/false, /*AsInformativeChunk=*/DeclaringEntity);
+      // Only adds template arguments as informative chunks in declaration
+      // context.
+      if (!DeclaringEntity)
+        Result.AddChunk(CodeCompletionString::CK_RightAngle);
+      else
+        Result.AddInformativeChunk(">");
     }
 
     // Add the function parameters
-    Result.AddChunk(CodeCompletionString::CK_LeftParen);
-    AddFunctionParameterChunks(PP, Policy, Function, Result);
-    Result.AddChunk(CodeCompletionString::CK_RightParen);
+    bool InsertParameters = FunctionCanBeCall || DeclaringEntity;
+    if (InsertParameters)
+      Result.AddChunk(CodeCompletionString::CK_LeftParen);
+    else
+      Result.AddInformativeChunk("(");
+    AddFunctionParameterChunks(PP, Policy, Function, Result, /*Start=*/0,
+                               /*InOptional=*/false,
+                               /*FunctionCanBeCall=*/FunctionCanBeCall,
+                               /*IsInDeclarationContext=*/DeclaringEntity);
+    if (InsertParameters)
+      Result.AddChunk(CodeCompletionString::CK_RightParen);
+    else
+      Result.AddInformativeChunk(")");
     AddFunctionTypeQualsToCompletionString(Result, Function);
     return Result.TakeString();
   }
@@ -5073,7 +5167,7 @@ static void AddLambdaCompletion(ResultBuilder &Results,
 /// Perform code-completion in an expression context when we know what
 /// type we're looking for.
 void SemaCodeCompletion::CodeCompleteExpression(
-    Scope *S, const CodeCompleteExpressionData &Data) {
+    Scope *S, const CodeCompleteExpressionData &Data, bool IsAddressOfOperand) 
{
   ResultBuilder Results(
       SemaRef, CodeCompleter->getAllocator(),
       CodeCompleter->getCodeCompletionTUInfo(),
@@ -5101,6 +5195,7 @@ void SemaCodeCompletion::CodeCompleteExpression(
     Results.Ignore(Data.IgnoreDecls[I]);
 
   CodeCompletionDeclConsumer Consumer(Results, SemaRef.CurContext);
+  Consumer.setIsAddressOfOperand(IsAddressOfOperand);
   SemaRef.LookupVisibleDecls(S, Sema::LookupOrdinaryName, Consumer,
                              CodeCompleter->includeGlobals(),
                              CodeCompleter->loadExternal());
@@ -5144,9 +5239,11 @@ void SemaCodeCompletion::CodeCompleteExpression(
 
 void SemaCodeCompletion::CodeCompleteExpression(Scope *S,
                                                 QualType PreferredType,
-                                                bool IsParenthesized) {
+                                                bool IsParenthesized,
+                                                bool IsAddressOfOperand) {
   return CodeCompleteExpression(
-      S, CodeCompleteExpressionData(PreferredType, IsParenthesized));
+      S, CodeCompleteExpressionData(PreferredType, IsParenthesized),
+      IsAddressOfOperand);
 }
 
 void SemaCodeCompletion::CodeCompletePostfixExpression(Scope *S, ExprResult E,
@@ -6821,11 +6918,10 @@ void SemaCodeCompletion::CodeCompleteAfterIf(Scope *S, 
bool IsBracedThen) {
                             Results.size());
 }
 
-void SemaCodeCompletion::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
-                                                 bool EnteringContext,
-                                                 bool IsUsingDeclaration,
-                                                 QualType BaseType,
-                                                 QualType PreferredType) {
+void SemaCodeCompletion::CodeCompleteQualifiedId(
+    Scope *S, CXXScopeSpec &SS, bool EnteringContext, bool IsUsingDeclaration,
+    bool IsAddressOfOperand, bool IsInDeclarationContext, QualType BaseType,
+    QualType PreferredType) {
   if (SS.isEmpty() || !CodeCompleter)
     return;
 
@@ -6860,6 +6956,12 @@ void SemaCodeCompletion::CodeCompleteQualifiedId(Scope 
*S, CXXScopeSpec &SS,
   // resolves to a dependent record.
   DeclContext *Ctx = SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
 
+  std::optional<Sema::ContextRAII> SimulateContext;
+  // When completing a definition, simulate that we are in class scope to 
access
+  // private methods.
+  if (IsInDeclarationContext && Ctx != nullptr)
+    SimulateContext.emplace(SemaRef, Ctx);
+
   // Try to instantiate any non-dependent declaration contexts before
   // we look in them. Bail out if we fail.
   NestedNameSpecifier NNS = SS.getScopeRep();
@@ -6906,12 +7008,14 @@ void SemaCodeCompletion::CodeCompleteQualifiedId(Scope 
*S, CXXScopeSpec &SS,
   if (Ctx &&
       (CodeCompleter->includeNamespaceLevelDecls() || !Ctx->isFileContext())) {
     CodeCompletionDeclConsumer Consumer(Results, Ctx, BaseType);
+    Consumer.setIsInDeclarationContext(IsInDeclarationContext);
+    Consumer.setIsAddressOfOperand(IsAddressOfOperand);
     SemaRef.LookupVisibleDecls(Ctx, Sema::LookupOrdinaryName, Consumer,
                                /*IncludeGlobalScope=*/true,
                                /*IncludeDependentBases=*/true,
                                CodeCompleter->loadExternal());
   }
-
+  SimulateContext.reset();
   HandleCodeCompleteResults(&SemaRef, CodeCompleter,
                             Results.getCompletionContext(), Results.data(),
                             Results.size());

diff  --git a/clang/test/CodeCompletion/cpp23-explicit-object.cpp 
b/clang/test/CodeCompletion/cpp23-explicit-object.cpp
index ea97237ecd24b..82888b76fb918 100644
--- a/clang/test/CodeCompletion/cpp23-explicit-object.cpp
+++ b/clang/test/CodeCompletion/cpp23-explicit-object.cpp
@@ -36,9 +36,9 @@ int func3() {
   (&A::bar)
 }
 // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-3):10 
-std=c++23 %s | FileCheck -check-prefix=CHECK-CC3 %s
-// CHECK-CC3: COMPLETION: foo : [#void#]foo<<#class self:auto#>>(<#int arg#>)
+// CHECK-CC3: COMPLETION: foo : [#void#]foo<<#class self:auto#>>[#(#][#int 
arg#][#)#]
 // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-4):10 
-std=c++23 %s | FileCheck -check-prefix=CHECK-CC4 %s
-// CHECK-CC4: COMPLETION: bar : [#void#]bar(<#int arg#>)
+// CHECK-CC4: COMPLETION: bar : [#void#]bar[#(#][#int arg#][#)#]
 
 int func4() {
   // TODO (&A::foo)(

diff  --git a/clang/test/CodeCompletion/member-access.cpp 
b/clang/test/CodeCompletion/member-access.cpp
index 8526ed7273474..2de96b43ff2ba 100644
--- a/clang/test/CodeCompletion/member-access.cpp
+++ b/clang/test/CodeCompletion/member-access.cpp
@@ -171,7 +171,7 @@ class Template {
 template<typename T>
 void dependentColonColonCompletion() {
   Template<T>::staticFn();
-// CHECK-CC7: function : [#void#]function()
+// CHECK-CC7: function : [#void#]function[#(#][#)#]
 // CHECK-CC7: Nested : Nested
 // CHECK-CC7: o1 : [#BaseTemplate<int>#]o1
 // CHECK-CC7: o2 : [#BaseTemplate<T>#]o2
@@ -352,7 +352,7 @@ namespace function_can_be_call {
     &S::f
   }
   // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:352:9 %s -o - | 
FileCheck -check-prefix=CHECK_FUNCTION_CAN_BE_CALL %s
-  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#T#]foo<<#typename T#>, 
<#typename U#>>(<#U#>, <#V#>)
+  // CHECK_FUNCTION_CAN_BE_CALL: COMPLETION: foo : [#T#]foo<<#typename T#>, 
<#typename U#>>[#(#][#U#][#, #][#V#][#)#]
 }
 
 namespace deref_dependent_this {

diff  --git a/clang/test/Index/complete-qualified.cpp 
b/clang/test/Index/complete-qualified.cpp
index 11abd53b86c28..dcb06439e5778 100644
--- a/clang/test/Index/complete-qualified.cpp
+++ b/clang/test/Index/complete-qualified.cpp
@@ -16,5 +16,5 @@ void foo()
 // RUN: c-index-test -code-completion-at=%s:14:8 %s -o - | FileCheck 
-check-prefix=CHECK-CC1 %s
 // CHECK-CC1: FieldDecl:{ResultType C<Foo, class Bar>}{TypedText c} (35)
 // CHECK-CC1: ClassDecl:{TypedText Foo} (35)
-// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{LeftParen 
(}{Placeholder const Foo &}{RightParen )}
-// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{LeftParen 
(}{RightParen )} (80)
+// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{Informative 
(}{Informative const Foo &}{Informative )} (80)
+// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{Informative 
(}{Informative )} (80)


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to