https://github.com/MythreyaK updated 
https://github.com/llvm/llvm-project/pull/176635

>From 605c38e4f0e9124aae95cb964f16a5adbb62d5ea Mon Sep 17 00:00:00 2001
From: Mythreya <[email protected]>
Date: Sun, 18 Jan 2026 01:13:38 -0800
Subject: [PATCH 1/3] [clangd] Add inlay hints for forwarding direct init

---
 clang-tools-extra/clangd/AST.cpp              | 35 +++++++++++++++++--
 clang-tools-extra/clangd/AST.h                |  2 +-
 clang-tools-extra/clangd/Hover.cpp            | 10 +++++-
 clang-tools-extra/clangd/InlayHints.cpp       | 30 +++++++++++++++-
 .../clangd/unittests/InlayHintTests.cpp       | 26 ++++++++++++++
 5 files changed, 97 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 3bcc89d360cdb..c49255cf78a66 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -38,6 +38,7 @@
 #include <iterator>
 #include <optional>
 #include <string>
+#include <utility>
 #include <vector>
 
 namespace clang {
@@ -824,6 +825,14 @@ class ForwardingCallVisitor
     return !Info.has_value();
   }
 
+  bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+    auto *const Record = E->getType()->getAsCXXRecordDecl();
+    if (Record)
+      handleAggregateInit(Record, E->getInitExprs());
+
+    return true;
+  }
+
   bool VisitCXXConstructExpr(CXXConstructExpr *E) {
     auto *Callee = E->getConstructor();
     if (Callee) {
@@ -855,6 +864,8 @@ class ForwardingCallVisitor
     std::optional<FunctionDecl *> PackTarget;
   };
 
+  SmallVector<const FieldDecl *> AggregateInitFields {};
+
   // The output of this visitor
   std::optional<ForwardingInfo> Info;
 
@@ -896,6 +907,16 @@ class ForwardingCallVisitor
     Info = FI;
   }
 
+  void handleAggregateInit(const CXXRecordDecl *Record,
+                           const ArrayRef<Expr *> InitExprs) {
+    // FIXME: Handle base classes
+    if (Record->getNumFields() == InitExprs.size()) {
+      for (const auto *Field : Record->fields()) {
+        AggregateInitFields.emplace_back(Field);
+      }
+    }
+  }
+
   // Returns the beginning of the expanded pack represented by Parameters
   // in the given arguments, if it is there.
   std::optional<size_t> findPack(typename CallExpr::arg_range Args) {
@@ -977,7 +998,7 @@ class ForwardingCallVisitor
 
 } // namespace
 
-SmallVector<const ParmVarDecl *>
+std::variant<SmallVector<const ParmVarDecl *>, SmallVector<const FieldDecl *>>
 resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
   auto Parameters = D->parameters();
   // If the function has a template parameter pack
@@ -1006,9 +1027,16 @@ resolveForwardingParameters(const FunctionDecl *D, 
unsigned MaxDepth) {
       // Find call expressions involving the pack
       ForwardingCallVisitor V{Pack};
       V.TraverseStmt(CurrentFunction->getBody());
+
+      // if fields are direct-initialized, then no more forwarding
+      if (!V.AggregateInitFields.empty()) {
+        return V.AggregateInitFields;
+      }
+
       if (!V.Info) {
         break;
       }
+
       // If we found something: Fill in non-pack parameters
       auto Info = *V.Info;
       HeadIt = std::copy(Info.Head.begin(), Info.Head.end(), HeadIt);
@@ -1022,7 +1050,8 @@ resolveForwardingParameters(const FunctionDecl *D, 
unsigned MaxDepth) {
         if (const auto *Template = CurrentFunction->getPrimaryTemplate()) {
           bool NewFunction = SeenTemplates.insert(Template).second;
           if (!NewFunction) {
-            return {Parameters.begin(), Parameters.end()};
+            return SmallVector<const ParmVarDecl *>{Parameters.begin(),
+                                                    Parameters.end()};
           }
         }
       }
@@ -1032,7 +1061,7 @@ resolveForwardingParameters(const FunctionDecl *D, 
unsigned MaxDepth) {
     assert(TailIt.base() == HeadIt);
     return Result;
   }
-  return {Parameters.begin(), Parameters.end()};
+  return SmallVector<const ParmVarDecl *>{Parameters.begin(), 
Parameters.end()};
 }
 
 bool isExpandedFromParameterPack(const ParmVarDecl *D) {
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 2bb4943b6de0b..064c8c3cd0bdf 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -244,7 +244,7 @@ bool isDeeplyNested(const Decl *D, unsigned MaxDepth = 10);
 /// parameters to another function via variadic template parameters. This can
 /// for example be used to retrieve the constructor parameter ParmVarDecl for a
 /// make_unique or emplace_back call.
-llvm::SmallVector<const ParmVarDecl *>
+std::variant<SmallVector<const ParmVarDecl *>, SmallVector<const FieldDecl *>>
 resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth = 10);
 
 /// Checks whether D is instantiated from a function parameter pack
diff --git a/clang-tools-extra/clangd/Hover.cpp 
b/clang-tools-extra/clangd/Hover.cpp
index 3ce0d6258ea62..86d2b3ee83b25 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -63,6 +63,7 @@
 #include <algorithm>
 #include <optional>
 #include <string>
+#include <variant>
 #include <vector>
 
 namespace clang {
@@ -1062,7 +1063,14 @@ void maybeAddCalleeArgInfo(const SelectionTree::Node *N, 
HoverInfo &HI,
 
   HoverInfo::PassType PassType;
 
-  auto Parameters = resolveForwardingParameters(FD);
+  const auto Params = resolveForwardingParameters(FD);
+
+  auto Parameters = [&]() -> SmallVector<const ParmVarDecl *> {
+    if (std::holds_alternative<SmallVector<const ParmVarDecl *>>(Params)) {
+      return std::get<SmallVector<const ParmVarDecl *>>(Params);
+    }
+    return {};
+  }();
 
   // Find argument index for N.
   for (unsigned I = 0; I < Args.size() && I < Parameters.size(); ++I) {
diff --git a/clang-tools-extra/clangd/InlayHints.cpp 
b/clang-tools-extra/clangd/InlayHints.cpp
index 2290fbd98056d..c264af9d6ed10 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -773,11 +773,26 @@ class InlayHintVisitor : public 
RecursiveASTVisitor<InlayHintVisitor> {
     bool HasNonDefaultArgs = false;
 
     ArrayRef<const ParmVarDecl *> Params, ForwardedParams;
+
     // Resolve parameter packs to their forwarded parameter
     SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
+    // If args are direct-initialized
+    SmallVector<const FieldDecl *> CXXRecordDeclFields{};
+
     if (Callee.Decl) {
       Params = maybeDropCxxExplicitObjectParameters(Callee.Decl->parameters());
-      ForwardedParamsStorage = resolveForwardingParameters(Callee.Decl);
+
+      [&]() {
+        auto Params = resolveForwardingParameters(Callee.Decl);
+        if (std::holds_alternative<decltype(ForwardedParamsStorage)>(Params)) {
+          ForwardedParamsStorage =
+              std::get<decltype(ForwardedParamsStorage)>(Params);
+        }
+        if (std::holds_alternative<decltype(CXXRecordDeclFields)>(Params)) {
+          CXXRecordDeclFields = 
std::get<decltype(CXXRecordDeclFields)>(Params);
+        }
+      }();
+
       ForwardedParams =
           maybeDropCxxExplicitObjectParameters(ForwardedParamsStorage);
     } else {
@@ -787,6 +802,19 @@ class InlayHintVisitor : public 
RecursiveASTVisitor<InlayHintVisitor> {
 
     NameVec ParameterNames = chooseParameterNames(ForwardedParams);
 
+    if (!CXXRecordDeclFields.empty()) {
+      ParameterNames.clear();
+      for (size_t I = 0; I < Args.size(); ++I) {
+        const auto &Field = CXXRecordDeclFields[I];
+
+        addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
+                     InlayHintKind::Parameter,
+                     Field->getType()->isReferenceType() ? "&." : ".",
+                     Field->getName(), ": ");
+      }
+      return;
+    }
+
     // Exclude setters (i.e. functions with one argument whose name begins with
     // "set"), and builtins like std::move/forward/... as their parameter name
     // is also not likely to be interesting.
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp 
b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 5552aa178a354..110b368a35cb5 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1955,6 +1955,32 @@ TEST(ParameterHints, DoesntExpandAllArgs) {
       ExpectedHint{"c: ", "param3"});
 }
 
+TEST(ParameterHints, CXX20AggregateParenInitNoCtor) {
+  assertParameterHints(
+      R"cpp(
+    namespace std { 
+      // This prototype of std::forward is sufficient for clang to recognize it
+      template <typename T> T&& forward(T&);
+    }
+      
+    template<typename T, typename ...Args>
+    T* make_unique(Args&&...args) {
+      return new T(std::forward<Args>(args)...);
+    }
+
+    struct Point {
+      int& x;
+      int y;
+    };
+
+    int foo() {
+      int t = 42;
+      make_unique<Point>($px[[t]], $py[[2]]);
+    }
+  )cpp",
+      ExpectedHint{"&.x: ", "px"}, ExpectedHint{".y: ", "py"});
+}
+
 TEST(BlockEndHints, Functions) {
   assertBlockEndHints(R"cpp(
     int foo() {

>From 916cb35cee3154b8e6bf0c48fdf087eb4451ecce Mon Sep 17 00:00:00 2001
From: Mythreya <[email protected]>
Date: Sun, 18 Jan 2026 02:30:10 -0800
Subject: [PATCH 2/3] cleanup

---
 clang-tools-extra/clangd/AST.cpp              | 25 +++----------
 clang-tools-extra/clangd/AST.h                |  3 +-
 clang-tools-extra/clangd/InlayHints.cpp       | 17 ++++-----
 .../clangd/unittests/InlayHintTests.cpp       | 35 +++++++++++++++++++
 4 files changed, 51 insertions(+), 29 deletions(-)

diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index c49255cf78a66..d30968f580e2b 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -826,10 +826,7 @@ class ForwardingCallVisitor
   }
 
   bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
-    auto *const Record = E->getType()->getAsCXXRecordDecl();
-    if (Record)
-      handleAggregateInit(Record, E->getInitExprs());
-
+    RecordInfo = E->getType()->getAsCXXRecordDecl();
     return true;
   }
 
@@ -864,10 +861,9 @@ class ForwardingCallVisitor
     std::optional<FunctionDecl *> PackTarget;
   };
 
-  SmallVector<const FieldDecl *> AggregateInitFields {};
-
   // The output of this visitor
   std::optional<ForwardingInfo> Info;
+  const CXXRecordDecl *RecordInfo{};
 
 private:
   // inspects the given callee with the given args to check whether it
@@ -907,16 +903,6 @@ class ForwardingCallVisitor
     Info = FI;
   }
 
-  void handleAggregateInit(const CXXRecordDecl *Record,
-                           const ArrayRef<Expr *> InitExprs) {
-    // FIXME: Handle base classes
-    if (Record->getNumFields() == InitExprs.size()) {
-      for (const auto *Field : Record->fields()) {
-        AggregateInitFields.emplace_back(Field);
-      }
-    }
-  }
-
   // Returns the beginning of the expanded pack represented by Parameters
   // in the given arguments, if it is there.
   std::optional<size_t> findPack(typename CallExpr::arg_range Args) {
@@ -998,7 +984,7 @@ class ForwardingCallVisitor
 
 } // namespace
 
-std::variant<SmallVector<const ParmVarDecl *>, SmallVector<const FieldDecl *>>
+std::variant<SmallVector<const ParmVarDecl *>, const CXXRecordDecl *>
 resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
   auto Parameters = D->parameters();
   // If the function has a template parameter pack
@@ -1029,9 +1015,8 @@ resolveForwardingParameters(const FunctionDecl *D, 
unsigned MaxDepth) {
       V.TraverseStmt(CurrentFunction->getBody());
 
       // if fields are direct-initialized, then no more forwarding
-      if (!V.AggregateInitFields.empty()) {
-        return V.AggregateInitFields;
-      }
+      if (V.RecordInfo)
+        return V.RecordInfo;
 
       if (!V.Info) {
         break;
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 064c8c3cd0bdf..09fd109515d62 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -17,6 +17,7 @@
 #include "index/Symbol.h"
 #include "index/SymbolID.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/SourceLocation.h"
@@ -244,7 +245,7 @@ bool isDeeplyNested(const Decl *D, unsigned MaxDepth = 10);
 /// parameters to another function via variadic template parameters. This can
 /// for example be used to retrieve the constructor parameter ParmVarDecl for a
 /// make_unique or emplace_back call.
-std::variant<SmallVector<const ParmVarDecl *>, SmallVector<const FieldDecl *>>
+std::variant<SmallVector<const ParmVarDecl *>, const CXXRecordDecl *>
 resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth = 10);
 
 /// Checks whether D is instantiated from a function parameter pack
diff --git a/clang-tools-extra/clangd/InlayHints.cpp 
b/clang-tools-extra/clangd/InlayHints.cpp
index c264af9d6ed10..3d3dbff2a4d14 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -777,7 +777,7 @@ class InlayHintVisitor : public 
RecursiveASTVisitor<InlayHintVisitor> {
     // Resolve parameter packs to their forwarded parameter
     SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
     // If args are direct-initialized
-    SmallVector<const FieldDecl *> CXXRecordDeclFields{};
+    const CXXRecordDecl *CxxRecord{};
 
     if (Callee.Decl) {
       Params = maybeDropCxxExplicitObjectParameters(Callee.Decl->parameters());
@@ -788,8 +788,8 @@ class InlayHintVisitor : public 
RecursiveASTVisitor<InlayHintVisitor> {
           ForwardedParamsStorage =
               std::get<decltype(ForwardedParamsStorage)>(Params);
         }
-        if (std::holds_alternative<decltype(CXXRecordDeclFields)>(Params)) {
-          CXXRecordDeclFields = 
std::get<decltype(CXXRecordDeclFields)>(Params);
+        if (std::holds_alternative<decltype(CxxRecord)>(Params)) {
+          CxxRecord = std::get<decltype(CxxRecord)>(Params);
         }
       }();
 
@@ -802,16 +802,17 @@ class InlayHintVisitor : public 
RecursiveASTVisitor<InlayHintVisitor> {
 
     NameVec ParameterNames = chooseParameterNames(ForwardedParams);
 
-    if (!CXXRecordDeclFields.empty()) {
-      ParameterNames.clear();
-      for (size_t I = 0; I < Args.size(); ++I) {
-        const auto &Field = CXXRecordDeclFields[I];
+    if (CxxRecord) {
+      const auto ParamArgs = Args.drop_front(CxxRecord->getNumBases());
+      const auto Iter = llvm::zip(ParamArgs, CxxRecord->fields());
 
-        addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
+      for (const auto &[ParamArg, Field] : Iter) {
+        addInlayHint(ParamArg->getSourceRange(), HintSide::Left,
                      InlayHintKind::Parameter,
                      Field->getType()->isReferenceType() ? "&." : ".",
                      Field->getName(), ": ");
       }
+
       return;
     }
 
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp 
b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 110b368a35cb5..630da798c14dc 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1971,6 +1971,7 @@ TEST(ParameterHints, CXX20AggregateParenInitNoCtor) {
     struct Point {
       int& x;
       int y;
+      int z;
     };
 
     int foo() {
@@ -1981,6 +1982,40 @@ TEST(ParameterHints, CXX20AggregateParenInitNoCtor) {
       ExpectedHint{"&.x: ", "px"}, ExpectedHint{".y: ", "py"});
 }
 
+TEST(ParameterHints, CXX20AggregateParenInitNoCtorDerived) {
+  assertParameterHints(
+      R"cpp(
+    namespace std { 
+      // This prototype of std::forward is sufficient for clang to recognize it
+      template <typename T> T&& forward(T&);
+    }
+      
+    template<typename T, typename ...Args>
+    T* make_unique(Args&&...args) {
+      return new T(std::forward<Args>(args)...);
+    }
+
+    struct Col {
+      uint8_t r {};
+      uint8_t g {};
+      uint8_t b {};
+    };
+
+    struct Point : public Col {
+      int& x;
+      int y;
+      int z;
+    };
+
+    int foo() {
+      Col c {};
+      int t = 42;
+      make_unique<Point>(c, $px[[t]], $py[[2]]);
+    }
+  )cpp",
+      ExpectedHint{"&.x: ", "px"}, ExpectedHint{".y: ", "py"});
+}
+
 TEST(BlockEndHints, Functions) {
   assertBlockEndHints(R"cpp(
     int foo() {

>From 5392af44a4402c632b4794650d002bd82a44a921 Mon Sep 17 00:00:00 2001
From: Mythreya <[email protected]>
Date: Sun, 18 Jan 2026 02:37:24 -0800
Subject: [PATCH 3/3] fix crash

---
 clang-tools-extra/clangd/unittests/InlayHintTests.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp 
b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 630da798c14dc..f6ed9683fd0db 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1996,9 +1996,9 @@ TEST(ParameterHints, 
CXX20AggregateParenInitNoCtorDerived) {
     }
 
     struct Col {
-      uint8_t r {};
-      uint8_t g {};
-      uint8_t b {};
+      unsigned short r {};
+      unsigned short g {};
+      unsigned short b {};
     };
 
     struct Point : public Col {

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

Reply via email to