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
