[clang-tools-extra] [clangd] Add inlay hints for forwarding direct init (PR #176635)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jan 18 02:47:48 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tools-extra
Author: Mythreya Kuricheti (MythreyaK)
<details>
<summary>Changes</summary>
Adds designator hints for direct init
Fixes clangd/clangd#<!-- -->2541
---
Full diff: https://github.com/llvm/llvm-project/pull/176635.diff
5 Files Affected:
- (modified) clang-tools-extra/clangd/AST.cpp (+17-3)
- (modified) clang-tools-extra/clangd/AST.h (+2-1)
- (modified) clang-tools-extra/clangd/Hover.cpp (+9-1)
- (modified) clang-tools-extra/clangd/InlayHints.cpp (+30-1)
- (modified) clang-tools-extra/clangd/unittests/InlayHintTests.cpp (+61)
``````````diff
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 3bcc89d360cdb..d30968f580e2b 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,11 @@ class ForwardingCallVisitor
return !Info.has_value();
}
+ bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+ RecordInfo = E->getType()->getAsCXXRecordDecl();
+ return true;
+ }
+
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
auto *Callee = E->getConstructor();
if (Callee) {
@@ -857,6 +863,7 @@ class ForwardingCallVisitor
// 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
@@ -977,7 +984,7 @@ class ForwardingCallVisitor
} // namespace
-SmallVector<const ParmVarDecl *>
+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
@@ -1006,9 +1013,15 @@ 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.RecordInfo)
+ return V.RecordInfo;
+
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 +1035,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 +1046,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..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.
-llvm::SmallVector<const ParmVarDecl *>
+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/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..3d3dbff2a4d14 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
+ const CXXRecordDecl *CxxRecord{};
+
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(CxxRecord)>(Params)) {
+ CxxRecord = std::get<decltype(CxxRecord)>(Params);
+ }
+ }();
+
ForwardedParams =
maybeDropCxxExplicitObjectParameters(ForwardedParamsStorage);
} else {
@@ -787,6 +802,20 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
NameVec ParameterNames = chooseParameterNames(ForwardedParams);
+ if (CxxRecord) {
+ const auto ParamArgs = Args.drop_front(CxxRecord->getNumBases());
+ const auto Iter = llvm::zip(ParamArgs, CxxRecord->fields());
+
+ for (const auto &[ParamArg, Field] : Iter) {
+ addInlayHint(ParamArg->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..f6ed9683fd0db 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1955,6 +1955,67 @@ 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 z;
+ };
+
+ int foo() {
+ int t = 42;
+ make_unique<Point>($px[[t]], $py[[2]]);
+ }
+ )cpp",
+ 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 {
+ unsigned short r {};
+ unsigned short g {};
+ unsigned short 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() {
``````````
</details>
https://github.com/llvm/llvm-project/pull/176635
More information about the cfe-commits
mailing list