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

Adds designator hints for direct init

Fixes clangd/clangd#2541 

>From 605c38e4f0e9124aae95cb964f16a5adbb62d5ea Mon Sep 17 00:00:00 2001
From: Mythreya <[email protected]>
Date: Sun, 18 Jan 2026 01:13:38 -0800
Subject: [PATCH] [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() {

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

Reply via email to