[clang] [clang-tools-extra] [clangd] show lambda name instead of operator() in signature help (PR #101857)

Timothy Akintilo via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 11 18:28:15 PDT 2024


https://github.com/tilobyte updated https://github.com/llvm/llvm-project/pull/101857

>From c1afe853ccacae1605fecfe552bb9a263c6b8c1d Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Sat, 27 Jul 2024 16:17:46 -0500
Subject: [PATCH 1/9] use lambda name instead of operator()

---
 clang-tools-extra/clangd/CodeComplete.cpp     |  2 ++
 .../clangd/unittests/CodeCompleteTests.cpp    |  9 +++++++
 .../include/clang/Sema/CodeCompleteConsumer.h | 18 ++++++++++++++
 clang/include/clang/Sema/Overload.h           |  5 ++++
 clang/include/clang/Sema/Sema.h               | 22 ++++++++---------
 clang/lib/Sema/CodeCompleteConsumer.cpp       | 10 +++++++-
 clang/lib/Sema/SemaCodeComplete.cpp           | 17 ++++++++++---
 clang/lib/Sema/SemaLookup.cpp                 |  3 ++-
 clang/lib/Sema/SemaOverload.cpp               | 24 +++++++++----------
 9 files changed, 82 insertions(+), 28 deletions(-)

diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 89eee392837af4..4f8a53aa7aae7e 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1139,6 +1139,8 @@ class SignatureHelpCollector final : public CodeCompleteConsumer {
           switch (K) {
           case OC::CK_Aggregate:
             return 0;
+          case OC::CK_Lambda:
+            [[fallthrough]];
           case OC::CK_Function:
             return 1;
           case OC::CK_FunctionType:
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 96d1ee1f0add73..4f748168d75aa7 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1437,6 +1437,15 @@ TEST(SignatureHelpTest, Overloads) {
   EXPECT_EQ(0, Results.activeParameter);
 }
 
+TEST(SignatureHelpTest, ShowLambdaNameInsteadOfOperatorParens) {
+  auto const Results = signatures(R"cpp(
+    auto foo = [](int x, int y){};
+    int main() { foo(^); }
+  )cpp");
+  EXPECT_THAT(Results.signatures,
+              UnorderedElementsAre(sig("foo([[int x]], [[int y]]) -> void")));
+}
+
 TEST(SignatureHelpTest, FunctionPointers) {
   llvm::StringLiteral Tests[] = {
       // Variable of function pointer type
diff --git a/clang/include/clang/Sema/CodeCompleteConsumer.h b/clang/include/clang/Sema/CodeCompleteConsumer.h
index 0924dc27af82b5..a6530c3c93d912 100644
--- a/clang/include/clang/Sema/CodeCompleteConsumer.h
+++ b/clang/include/clang/Sema/CodeCompleteConsumer.h
@@ -1028,6 +1028,9 @@ class CodeCompleteConsumer {
       /// The candidate is a function declaration.
       CK_Function,
 
+      // The candidate is a lambda operator().
+      CK_Lambda,
+
       /// The candidate is a function template, arguments are being completed.
       CK_FunctionTemplate,
 
@@ -1055,6 +1058,13 @@ class CodeCompleteConsumer {
       /// Kind == CK_Function.
       FunctionDecl *Function;
 
+      /// The lambda operator() candidate paired with the
+      /// lambda variable, available when Kind == CK_Lambda.
+      struct {
+        FunctionDecl *OperatorParens;
+        VarDecl *Var;
+      } Lambda;
+
       /// The function template overload candidate, available when
       /// Kind == CK_FunctionTemplate.
       FunctionTemplateDecl *FunctionTemplate;
@@ -1082,6 +1092,12 @@ class CodeCompleteConsumer {
       assert(Function != nullptr);
     }
 
+    OverloadCandidate(FunctionDecl *LambdaOperatorParens, VarDecl *LambdaVar)
+        : Kind(CK_Lambda), Lambda{LambdaOperatorParens, LambdaVar} {
+      assert(Lambda.OperatorParens != nullptr);
+      assert(Lambda.Var != nullptr);
+    }
+
     OverloadCandidate(FunctionTemplateDecl *FunctionTemplateDecl)
         : Kind(CK_FunctionTemplate), FunctionTemplate(FunctionTemplateDecl) {
       assert(FunctionTemplateDecl != nullptr);
@@ -1112,6 +1128,8 @@ class CodeCompleteConsumer {
     /// function declaration for a function template.
     FunctionDecl *getFunction() const;
 
+    VarDecl *getLambdaVarDecl() const;
+
     /// Retrieve the function template overload candidate.
     FunctionTemplateDecl *getFunctionTemplate() const {
       assert(getKind() == CK_FunctionTemplate && "Not a function template");
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index d6a6cee62a7528..7c4e82f07de02e 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -876,6 +876,11 @@ class Sema;
     /// function pointer or reference (C++ [over.call.object]).
     FunctionDecl *Function;
 
+    /// LambdaName - When the OverloadCandidate is for a
+    /// lambda's operator(), points to the declaration of
+    /// the lambda variable.
+    VarDecl *LambdaName{nullptr};
+
     /// FoundDecl - The original declaration that was looked up /
     /// invented / otherwise found, together with its access.
     /// Might be a UsingShadowDecl or a FunctionTemplateDecl.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2ec6367eccea01..2cf1827c2d8585 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9147,7 +9147,8 @@ class Sema final : public SemaBase {
   ///
   /// \returns true if lookup succeeded, false if it failed.
   bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
-                           bool InUnqualifiedLookup = false);
+                           bool InUnqualifiedLookup = false,
+                           IdentifierInfo const *IdentifierOverride = nullptr);
 
   /// Performs qualified name lookup or special type of lookup for
   /// "__super::" scope specifier.
@@ -10186,7 +10187,7 @@ class Sema final : public SemaBase {
       OverloadCandidateSet &CandidateSet,
       TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr,
       bool SuppressUserConversions = false, bool PartialOverloading = false,
-      bool FirstArgumentIsBase = false);
+      bool FirstArgumentIsBase = false, VarDecl *LambdaName = nullptr);
 
   /// AddMethodCandidate - Adds a named decl (which is some kind of
   /// method) as a method candidate to the given overload set.
@@ -10204,15 +10205,14 @@ class Sema final : public SemaBase {
   /// both @c a1 and @c a2. If @p SuppressUserConversions, then don't
   /// allow user-defined conversions via constructors or conversion
   /// operators.
-  void
-  AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
-                     CXXRecordDecl *ActingContext, QualType ObjectType,
-                     Expr::Classification ObjectClassification,
-                     ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
-                     bool SuppressUserConversions = false,
-                     bool PartialOverloading = false,
-                     ConversionSequenceList EarlyConversions = std::nullopt,
-                     OverloadCandidateParamOrder PO = {});
+  void AddMethodCandidate(
+      CXXMethodDecl *Method, DeclAccessPair FoundDecl,
+      CXXRecordDecl *ActingContext, QualType ObjectType,
+      Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
+      OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
+      bool PartialOverloading = false,
+      ConversionSequenceList EarlyConversions = std::nullopt,
+      OverloadCandidateParamOrder PO = {}, VarDecl *LambdaName = nullptr);
 
   /// Add a C++ member function template as a candidate to the candidate
   /// set, using template argument deduction to produce an appropriate member
diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp
index 91713d71786ee5..c2cb966bff3aff 100644
--- a/clang/lib/Sema/CodeCompleteConsumer.cpp
+++ b/clang/lib/Sema/CodeCompleteConsumer.cpp
@@ -504,15 +504,23 @@ FunctionDecl *CodeCompleteConsumer::OverloadCandidate::getFunction() const {
     return Function;
   else if (getKind() == CK_FunctionTemplate)
     return FunctionTemplate->getTemplatedDecl();
-  else
+  else if (getKind() == CK_Lambda) {
+    return Lambda.OperatorParens;
+  } else
     return nullptr;
 }
 
+VarDecl *CodeCompleteConsumer::OverloadCandidate::getLambdaVarDecl() const {
+  return (getKind() == CK_Lambda) ? Lambda.Var : nullptr;
+}
+
 const FunctionType *
 CodeCompleteConsumer::OverloadCandidate::getFunctionType() const {
   switch (Kind) {
   case CK_Function:
     return Function->getType()->getAs<FunctionType>();
+  case CK_Lambda:
+    return Lambda.OperatorParens->getType()->getAs<FunctionType>();
 
   case CK_FunctionTemplate:
     return FunctionTemplate->getTemplatedDecl()
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 88d4732c7d5c6a..2bdd1924613586 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -4022,7 +4022,10 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
 
     std::string Name;
     llvm::raw_string_ostream OS(Name);
-    FDecl->getDeclName().print(OS, Policy);
+    auto const DeclName = (getKind() == CK_Lambda)
+                              ? getLambdaVarDecl()->getDeclName()
+                              : FDecl->getDeclName();
+    DeclName.print(OS, Policy);
     Result.AddTextChunk(Result.getAllocator().CopyString(Name));
   } else {
     // Function without a declaration. Just give the return type.
@@ -6123,7 +6126,10 @@ static void mergeCandidatesWithResults(
         continue;
     }
     if (Candidate.Viable)
-      Results.push_back(ResultCandidate(Candidate.Function));
+      Results.push_back(
+          Candidate.LambdaName == nullptr
+              ? ResultCandidate(Candidate.Function)
+              : ResultCandidate(Candidate.Function, Candidate.LambdaName));
   }
 }
 
@@ -6292,11 +6298,16 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
         SmallVector<Expr *, 12> ArgExprs(1, NakedFn);
         ArgExprs.append(ArgsWithoutDependentTypes.begin(),
                         ArgsWithoutDependentTypes.end());
+        auto *const LambdaName =
+            DC->isLambda() ? cast<VarDecl>(NakedFn->getReferencedDeclOfCallee())
+                           : nullptr;
         SemaRef.AddFunctionCandidates(R.asUnresolvedSet(), ArgExprs,
                                       CandidateSet,
                                       /*ExplicitArgs=*/nullptr,
                                       /*SuppressUserConversions=*/false,
-                                      /*PartialOverloading=*/true);
+                                      /*PartialOverloading=*/true,
+                                      /*FirstArgumentIsBase=*/false,
+                                      /*LambdaName=*/LambdaName);
       }
     } else {
       // Lastly we check whether expression's type is function pointer or
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 7a6a64529f52ec..1262950ab43f62 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -2390,7 +2390,8 @@ static bool LookupQualifiedNameInUsingDirectives(Sema &S, LookupResult &R,
 }
 
 bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
-                               bool InUnqualifiedLookup) {
+                               bool InUnqualifiedLookup,
+                               IdentifierInfo const *IdentifierOverride) {
   assert(LookupCtx && "Sema::LookupQualifiedName requires a lookup context");
 
   if (!R.getLookupName())
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c5f56ac62b458c..bfae0216d4c32f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7295,7 +7295,8 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
                                  TemplateArgumentListInfo *ExplicitTemplateArgs,
                                  bool SuppressUserConversions,
                                  bool PartialOverloading,
-                                 bool FirstArgumentIsBase) {
+                                 bool FirstArgumentIsBase,
+                                 VarDecl *LambdaName) {
   for (UnresolvedSetIterator F = Fns.begin(), E = Fns.end(); F != E; ++F) {
     NamedDecl *D = F.getDecl()->getUnderlyingDecl();
     ArrayRef<Expr *> FunctionArgs = Args;
@@ -7331,7 +7332,8 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
         AddMethodCandidate(cast<CXXMethodDecl>(FD), F.getPair(),
                            cast<CXXMethodDecl>(FD)->getParent(), ObjectType,
                            ObjectClassification, FunctionArgs, CandidateSet,
-                           SuppressUserConversions, PartialOverloading);
+                           SuppressUserConversions, PartialOverloading,
+                           std::nullopt, {}, LambdaName);
       }
     } else {
       // This branch handles both standalone functions and static methods.
@@ -7383,16 +7385,13 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType,
   }
 }
 
-void
-Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
-                         CXXRecordDecl *ActingContext, QualType ObjectType,
-                         Expr::Classification ObjectClassification,
-                         ArrayRef<Expr *> Args,
-                         OverloadCandidateSet &CandidateSet,
-                         bool SuppressUserConversions,
-                         bool PartialOverloading,
-                         ConversionSequenceList EarlyConversions,
-                         OverloadCandidateParamOrder PO) {
+void Sema::AddMethodCandidate(
+    CXXMethodDecl *Method, DeclAccessPair FoundDecl,
+    CXXRecordDecl *ActingContext, QualType ObjectType,
+    Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
+    OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
+    bool PartialOverloading, ConversionSequenceList EarlyConversions,
+    OverloadCandidateParamOrder PO, VarDecl *LambdaName) {
   const FunctionProtoType *Proto
     = dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());
   assert(Proto && "Methods without a prototype cannot be overloaded");
@@ -7418,6 +7417,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
       CandidateSet.addCandidate(Args.size() + 1, EarlyConversions);
   Candidate.FoundDecl = FoundDecl;
   Candidate.Function = Method;
+  Candidate.LambdaName = LambdaName;
   Candidate.RewriteKind =
       CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
   Candidate.TookAddressOfOverload =

>From 99c1b280dfc50d6219ae4645c99dd61717238832 Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Sun, 11 Aug 2024 13:02:10 -0500
Subject: [PATCH 2/9] rename OperatorParens -> CallOperator

---
 clang/include/clang/Sema/CodeCompleteConsumer.h | 8 ++++----
 clang/lib/Sema/CodeCompleteConsumer.cpp         | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Sema/CodeCompleteConsumer.h b/clang/include/clang/Sema/CodeCompleteConsumer.h
index a6530c3c93d912..d592fd9860ec34 100644
--- a/clang/include/clang/Sema/CodeCompleteConsumer.h
+++ b/clang/include/clang/Sema/CodeCompleteConsumer.h
@@ -1061,7 +1061,7 @@ class CodeCompleteConsumer {
       /// The lambda operator() candidate paired with the
       /// lambda variable, available when Kind == CK_Lambda.
       struct {
-        FunctionDecl *OperatorParens;
+        FunctionDecl *CallOperator;
         VarDecl *Var;
       } Lambda;
 
@@ -1092,9 +1092,9 @@ class CodeCompleteConsumer {
       assert(Function != nullptr);
     }
 
-    OverloadCandidate(FunctionDecl *LambdaOperatorParens, VarDecl *LambdaVar)
-        : Kind(CK_Lambda), Lambda{LambdaOperatorParens, LambdaVar} {
-      assert(Lambda.OperatorParens != nullptr);
+    OverloadCandidate(FunctionDecl *LambdaCallOperator, VarDecl *LambdaVar)
+        : Kind(CK_Lambda), Lambda{LambdaCallOperator, LambdaVar} {
+      assert(Lambda.CallOperator != nullptr);
       assert(Lambda.Var != nullptr);
     }
 
diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp
index c2cb966bff3aff..aac56e8dc68101 100644
--- a/clang/lib/Sema/CodeCompleteConsumer.cpp
+++ b/clang/lib/Sema/CodeCompleteConsumer.cpp
@@ -505,7 +505,7 @@ FunctionDecl *CodeCompleteConsumer::OverloadCandidate::getFunction() const {
   else if (getKind() == CK_FunctionTemplate)
     return FunctionTemplate->getTemplatedDecl();
   else if (getKind() == CK_Lambda) {
-    return Lambda.OperatorParens;
+    return Lambda.CallOperator;
   } else
     return nullptr;
 }
@@ -520,7 +520,7 @@ CodeCompleteConsumer::OverloadCandidate::getFunctionType() const {
   case CK_Function:
     return Function->getType()->getAs<FunctionType>();
   case CK_Lambda:
-    return Lambda.OperatorParens->getType()->getAs<FunctionType>();
+    return Lambda.CallOperator->getType()->getAs<FunctionType>();
 
   case CK_FunctionTemplate:
     return FunctionTemplate->getTemplatedDecl()

>From 65b11f7cd91f051653aedd980504b980718b388a Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <3729883+tilobyte at users.noreply.github.com>
Date: Sat, 7 Sep 2024 15:04:14 -0500
Subject: [PATCH 3/9] remove [[fallthrough]]

Co-authored-by: Younan Zhang <zyn7109 at gmail.com>
---
 clang-tools-extra/clangd/CodeComplete.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 4f8a53aa7aae7e..7b068951119739 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1140,7 +1140,6 @@ class SignatureHelpCollector final : public CodeCompleteConsumer {
           case OC::CK_Aggregate:
             return 0;
           case OC::CK_Lambda:
-            [[fallthrough]];
           case OC::CK_Function:
             return 1;
           case OC::CK_FunctionType:

>From 56c2d38fcd46f3f4179433073ebb4812b6b884c9 Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Sat, 7 Sep 2024 20:03:05 -0500
Subject: [PATCH 4/9] remove unnecessary IdentifierOverride

---
 clang/include/clang/Sema/Sema.h | 3 +--
 clang/lib/Sema/SemaLookup.cpp   | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2cf1827c2d8585..3c50f9324fc3ac 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9147,8 +9147,7 @@ class Sema final : public SemaBase {
   ///
   /// \returns true if lookup succeeded, false if it failed.
   bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
-                           bool InUnqualifiedLookup = false,
-                           IdentifierInfo const *IdentifierOverride = nullptr);
+                           bool InUnqualifiedLookup = false);
 
   /// Performs qualified name lookup or special type of lookup for
   /// "__super::" scope specifier.
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 1262950ab43f62..7a6a64529f52ec 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -2390,8 +2390,7 @@ static bool LookupQualifiedNameInUsingDirectives(Sema &S, LookupResult &R,
 }
 
 bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
-                               bool InUnqualifiedLookup,
-                               IdentifierInfo const *IdentifierOverride) {
+                               bool InUnqualifiedLookup) {
   assert(LookupCtx && "Sema::LookupQualifiedName requires a lookup context");
 
   if (!R.getLookupName())

>From b2d865325b22aa5f5223fafcafb833e924c1a6b3 Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Sun, 8 Sep 2024 20:12:27 -0500
Subject: [PATCH 5/9] add tests for lambda returned from function and
 immediately-invoked lambda

---
 .../clangd/unittests/CodeCompleteTests.cpp    | 21 ++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 4f748168d75aa7..9e9792f074d2e7 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1438,7 +1438,7 @@ TEST(SignatureHelpTest, Overloads) {
 }
 
 TEST(SignatureHelpTest, ShowLambdaNameInsteadOfOperatorParens) {
-  auto const Results = signatures(R"cpp(
+  const auto Results = signatures(R"cpp(
     auto foo = [](int x, int y){};
     int main() { foo(^); }
   )cpp");
@@ -1446,6 +1446,25 @@ TEST(SignatureHelpTest, ShowLambdaNameInsteadOfOperatorParens) {
               UnorderedElementsAre(sig("foo([[int x]], [[int y]]) -> void")));
 }
 
+TEST(SignatureHelpTest, ShowOperatorParensForImmediatelyInvokedLambda) {
+  const auto Results = signatures(R"cpp(
+    auto foo = [](int x, int y){}(^);
+  )cpp");
+  EXPECT_THAT(Results.signatures,
+              UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
+}
+
+TEST(SignatureHelpTest, ShowOperatorParensForLambdaReturnedFromFunction) {
+  const auto Results = signatures(R"cpp(
+    auto returnsLambda() {
+      return [](int x, int y){};
+    }
+    int main() { returnsLambda()(^); }
+  )cpp");
+  EXPECT_THAT(Results.signatures,
+              UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
+}
+
 TEST(SignatureHelpTest, FunctionPointers) {
   llvm::StringLiteral Tests[] = {
       // Variable of function pointer type

>From 197845663959309123729fc5b573a63922347114 Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Sun, 8 Sep 2024 20:29:09 -0500
Subject: [PATCH 6/9] get LambdaName using dyn_cast_if_present

---
 clang/lib/Sema/SemaCodeComplete.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 2bdd1924613586..56745c01b19360 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -6299,7 +6299,7 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
         ArgExprs.append(ArgsWithoutDependentTypes.begin(),
                         ArgsWithoutDependentTypes.end());
         auto *const LambdaName =
-            DC->isLambda() ? cast<VarDecl>(NakedFn->getReferencedDeclOfCallee())
+            DC->isLambda() ? dyn_cast_if_present<VarDecl>(NakedFn->getReferencedDeclOfCallee())
                            : nullptr;
         SemaRef.AddFunctionCandidates(R.asUnresolvedSet(), ArgExprs,
                                       CandidateSet,

>From d8140f9013f47094a159fd2918128d9252ca3280 Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Sun, 8 Sep 2024 20:40:58 -0500
Subject: [PATCH 7/9] rename LambdaName -> LambdaDecl

---
 clang/include/clang/Sema/Overload.h | 4 ++--
 clang/include/clang/Sema/Sema.h     | 4 ++--
 clang/lib/Sema/SemaCodeComplete.cpp | 8 ++++----
 clang/lib/Sema/SemaOverload.cpp     | 8 ++++----
 4 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 7c4e82f07de02e..e1bac1876817fa 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -876,10 +876,10 @@ class Sema;
     /// function pointer or reference (C++ [over.call.object]).
     FunctionDecl *Function;
 
-    /// LambdaName - When the OverloadCandidate is for a
+    /// LambdaDecl - When the OverloadCandidate is for a
     /// lambda's operator(), points to the declaration of
     /// the lambda variable.
-    VarDecl *LambdaName{nullptr};
+    VarDecl *LambdaDecl{nullptr};
 
     /// FoundDecl - The original declaration that was looked up /
     /// invented / otherwise found, together with its access.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3c50f9324fc3ac..5f1217c9de3e4e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10186,7 +10186,7 @@ class Sema final : public SemaBase {
       OverloadCandidateSet &CandidateSet,
       TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr,
       bool SuppressUserConversions = false, bool PartialOverloading = false,
-      bool FirstArgumentIsBase = false, VarDecl *LambdaName = nullptr);
+      bool FirstArgumentIsBase = false, VarDecl *LambdaDecl = nullptr);
 
   /// AddMethodCandidate - Adds a named decl (which is some kind of
   /// method) as a method candidate to the given overload set.
@@ -10211,7 +10211,7 @@ class Sema final : public SemaBase {
       OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
       bool PartialOverloading = false,
       ConversionSequenceList EarlyConversions = std::nullopt,
-      OverloadCandidateParamOrder PO = {}, VarDecl *LambdaName = nullptr);
+      OverloadCandidateParamOrder PO = {}, VarDecl *LambdaDecl = nullptr);
 
   /// Add a C++ member function template as a candidate to the candidate
   /// set, using template argument deduction to produce an appropriate member
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 56745c01b19360..36a98a6ec06445 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -6127,9 +6127,9 @@ static void mergeCandidatesWithResults(
     }
     if (Candidate.Viable)
       Results.push_back(
-          Candidate.LambdaName == nullptr
+          Candidate.LambdaDecl == nullptr
               ? ResultCandidate(Candidate.Function)
-              : ResultCandidate(Candidate.Function, Candidate.LambdaName));
+              : ResultCandidate(Candidate.Function, Candidate.LambdaDecl));
   }
 }
 
@@ -6298,7 +6298,7 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
         SmallVector<Expr *, 12> ArgExprs(1, NakedFn);
         ArgExprs.append(ArgsWithoutDependentTypes.begin(),
                         ArgsWithoutDependentTypes.end());
-        auto *const LambdaName =
+        auto *const LambdaDecl =
             DC->isLambda() ? dyn_cast_if_present<VarDecl>(NakedFn->getReferencedDeclOfCallee())
                            : nullptr;
         SemaRef.AddFunctionCandidates(R.asUnresolvedSet(), ArgExprs,
@@ -6307,7 +6307,7 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
                                       /*SuppressUserConversions=*/false,
                                       /*PartialOverloading=*/true,
                                       /*FirstArgumentIsBase=*/false,
-                                      /*LambdaName=*/LambdaName);
+                                      /*LambdaDecl=*/LambdaDecl);
       }
     } else {
       // Lastly we check whether expression's type is function pointer or
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index bfae0216d4c32f..6d1f29fea7d46b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7296,7 +7296,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
                                  bool SuppressUserConversions,
                                  bool PartialOverloading,
                                  bool FirstArgumentIsBase,
-                                 VarDecl *LambdaName) {
+                                 VarDecl *LambdaDecl) {
   for (UnresolvedSetIterator F = Fns.begin(), E = Fns.end(); F != E; ++F) {
     NamedDecl *D = F.getDecl()->getUnderlyingDecl();
     ArrayRef<Expr *> FunctionArgs = Args;
@@ -7333,7 +7333,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
                            cast<CXXMethodDecl>(FD)->getParent(), ObjectType,
                            ObjectClassification, FunctionArgs, CandidateSet,
                            SuppressUserConversions, PartialOverloading,
-                           std::nullopt, {}, LambdaName);
+                           std::nullopt, {}, LambdaDecl);
       }
     } else {
       // This branch handles both standalone functions and static methods.
@@ -7391,7 +7391,7 @@ void Sema::AddMethodCandidate(
     Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
     OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
     bool PartialOverloading, ConversionSequenceList EarlyConversions,
-    OverloadCandidateParamOrder PO, VarDecl *LambdaName) {
+    OverloadCandidateParamOrder PO, VarDecl *LambdaDecl) {
   const FunctionProtoType *Proto
     = dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());
   assert(Proto && "Methods without a prototype cannot be overloaded");
@@ -7417,7 +7417,7 @@ void Sema::AddMethodCandidate(
       CandidateSet.addCandidate(Args.size() + 1, EarlyConversions);
   Candidate.FoundDecl = FoundDecl;
   Candidate.Function = Method;
-  Candidate.LambdaName = LambdaName;
+  Candidate.LambdaDecl = LambdaDecl;
   Candidate.RewriteKind =
       CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
   Candidate.TookAddressOfOverload =

>From c6e12d02d8735009e7b968275cfcc624d36a9c02 Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Wed, 11 Sep 2024 20:23:59 -0500
Subject: [PATCH 8/9] add test for lambda in parameter pack

---
 .../clangd/unittests/CodeCompleteTests.cpp            | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 9e9792f074d2e7..a1317d413c6a76 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1465,6 +1465,17 @@ TEST(SignatureHelpTest, ShowOperatorParensForLambdaReturnedFromFunction) {
               UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
 }
 
+TEST(SignatureHelpTest, ShowOperatorParensForLambdaInParameterPack) {
+  const auto Results = signatures(R"cpp(
+    template <typename... Types>
+    auto foo(Types... args) {}
+    
+    foo([](int x, int y){}(^))
+  )cpp");
+  EXPECT_THAT(Results.signatures,
+              UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
+}
+
 TEST(SignatureHelpTest, FunctionPointers) {
   llvm::StringLiteral Tests[] = {
       // Variable of function pointer type

>From 0e0a198c1f70e88aa546d34fb0657d3c31a8d71f Mon Sep 17 00:00:00 2001
From: Timothy Akintilo <timtobi1 at gmail.com>
Date: Wed, 11 Sep 2024 20:26:05 -0500
Subject: [PATCH 9/9] format

format
---
 .../clangd/unittests/CodeCompleteTests.cpp        | 15 +++++++++------
 clang/lib/Sema/SemaCodeComplete.cpp               |  7 ++++---
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index a1317d413c6a76..fd896e2098f0ec 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1450,8 +1450,9 @@ TEST(SignatureHelpTest, ShowOperatorParensForImmediatelyInvokedLambda) {
   const auto Results = signatures(R"cpp(
     auto foo = [](int x, int y){}(^);
   )cpp");
-  EXPECT_THAT(Results.signatures,
-              UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
+  EXPECT_THAT(
+      Results.signatures,
+      UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
 }
 
 TEST(SignatureHelpTest, ShowOperatorParensForLambdaReturnedFromFunction) {
@@ -1461,8 +1462,9 @@ TEST(SignatureHelpTest, ShowOperatorParensForLambdaReturnedFromFunction) {
     }
     int main() { returnsLambda()(^); }
   )cpp");
-  EXPECT_THAT(Results.signatures,
-              UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
+  EXPECT_THAT(
+      Results.signatures,
+      UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
 }
 
 TEST(SignatureHelpTest, ShowOperatorParensForLambdaInParameterPack) {
@@ -1472,8 +1474,9 @@ TEST(SignatureHelpTest, ShowOperatorParensForLambdaInParameterPack) {
     
     foo([](int x, int y){}(^))
   )cpp");
-  EXPECT_THAT(Results.signatures,
-              UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
+  EXPECT_THAT(
+      Results.signatures,
+      UnorderedElementsAre(sig("operator()([[int x]], [[int y]]) -> void")));
 }
 
 TEST(SignatureHelpTest, FunctionPointers) {
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 36a98a6ec06445..935a5597bbb6f7 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -6298,9 +6298,10 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
         SmallVector<Expr *, 12> ArgExprs(1, NakedFn);
         ArgExprs.append(ArgsWithoutDependentTypes.begin(),
                         ArgsWithoutDependentTypes.end());
-        auto *const LambdaDecl =
-            DC->isLambda() ? dyn_cast_if_present<VarDecl>(NakedFn->getReferencedDeclOfCallee())
-                           : nullptr;
+        auto *const LambdaDecl = DC->isLambda()
+                                     ? dyn_cast_if_present<VarDecl>(
+                                           NakedFn->getReferencedDeclOfCallee())
+                                     : nullptr;
         SemaRef.AddFunctionCandidates(R.asUnresolvedSet(), ArgExprs,
                                       CandidateSet,
                                       /*ExplicitArgs=*/nullptr,



More information about the cfe-commits mailing list