[clang-tools-extra] [clangd] Add designator hints for parenthesis aggregate initialization (PR #170642)
Mythreya Kuricheti via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 9 18:09:28 PST 2026
https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/170642
>From c2c9ade830cb2de5e858d3bd8c384005c2e3d8f3 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Wed, 3 Dec 2025 21:41:40 -0800
Subject: [PATCH 1/7] Enable designator hints test
---
.../clangd/unittests/InlayHintTests.cpp | 27 +++++++++----------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index feb4404b3d2bf..204c022851017 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1147,20 +1147,6 @@ TEST(ParameterHints, CopyOrMoveConstructor) {
)cpp");
}
-TEST(ParameterHints, AggregateInit) {
- // FIXME: This is not implemented yet, but it would be a natural
- // extension to show member names as hints here.
- assertParameterHints(R"cpp(
- struct Point {
- int x;
- int y;
- };
- void bar() {
- Point p{41, 42};
- }
- )cpp");
-}
-
TEST(ParameterHints, UserDefinedLiteral) {
// Do not hint call to user-defined literal operator.
assertParameterHints(R"cpp(
@@ -1746,6 +1732,19 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
}
TEST(DesignatorHints, Basic) {
+ assertDesignatorHints(R"cpp(
+ struct Point {
+ int x;
+ int y;
+ };
+ void bar() {
+ Point p{$x[[41]], $y[[42]]};
+ }
+ )cpp",
+ ExpectedHint{".x=", "x"}, ExpectedHint{".y=", "y"});
+}
+
+TEST(DesignatorHints, BasicArray) {
assertDesignatorHints(R"cpp(
struct S { int x, y, z; };
S s {$x[[1]], $y[[2+2]]};
>From 9b65360969cc5d82b0306b204790652ee35b9c4b Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Wed, 3 Dec 2025 23:49:16 -0800
Subject: [PATCH 2/7] [clangd] Add designator hints for parenthesis aggregate
initialization
---
clang-tools-extra/clangd/InlayHints.cpp | 20 +++++++++++++++++++
.../clangd/unittests/InlayHintTests.cpp | 13 ++++++++++++
2 files changed, 33 insertions(+)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 23bd02304a4fd..edd7b2d38e90c 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -699,6 +699,26 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
return InstantiatedFunction->getParamDecl(ParamIdx);
}
+ bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+ // TODO: Lang check needed?
+ if (!Cfg.InlayHints.Designators &&
+ AST.getLangOpts().LangStd >= LangStandard::Kind::lang_cxx20)
+ return true;
+
+ if (const auto *CXXRecord = E->getType()->getAsCXXRecordDecl()) {
+ const auto &InitExprs = E->getInitExprs();
+ auto RecordFields = CXXRecord->fields().begin();
+
+ for (size_t I = 0; I < InitExprs.size();
+ ++I, RecordFields = std::next(RecordFields)) {
+ // TODO: is prepending "." sufficient?
+ addDesignatorHint(InitExprs[I]->getSourceRange(),
+ "." + RecordFields->getName().str());
+ }
+ }
+ return true;
+ }
+
bool VisitInitListExpr(InitListExpr *Syn) {
// We receive the syntactic form here (shouldVisitImplicitCode() is false).
// This is the one we will ultimately attach designators to.
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 204c022851017..72e7be2f3d943 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1821,6 +1821,19 @@ TEST(DesignatorHints, NoCrash) {
ExpectedHint{".b=", "b"});
}
+TEST(DesignatorHints, BasicParenInit) {
+ assertDesignatorHints(R"cpp(
+ struct S {
+ int x;
+ int y;
+ int z;
+ };
+ S s ($x[[1]], $y[[2+2]], $z[[4]]);
+ )cpp",
+ ExpectedHint{".x=", "x"}, ExpectedHint{".y=", "y"},
+ ExpectedHint{".z=", "z"});
+}
+
TEST(InlayHints, RestrictRange) {
Annotations Code(R"cpp(
auto a = false;
>From d1a361e57bcc6b9845ddb673762500a957b469e2 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Fri, 5 Dec 2025 23:28:53 -0800
Subject: [PATCH 3/7] code-review
---
clang-tools-extra/clangd/InlayHints.cpp | 24 +++++++++++++------
.../clangd/unittests/InlayHintTests.cpp | 18 ++++++++++++++
2 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index edd7b2d38e90c..9476fe9deea57 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -700,22 +700,32 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
}
bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
- // TODO: Lang check needed?
- if (!Cfg.InlayHints.Designators &&
- AST.getLangOpts().LangStd >= LangStandard::Kind::lang_cxx20)
+ if (!Cfg.InlayHints.Designators)
return true;
if (const auto *CXXRecord = E->getType()->getAsCXXRecordDecl()) {
const auto &InitExprs = E->getInitExprs();
+ size_t InitInx = 0;
+
+ // Inherited members are first
+ for (const auto &Base : CXXRecord->bases()) {
+ std::ignore = Base;
+ // For a base record, just use its name
+ // Base of base requires its own initialization; ParenListInitExpr or
+ // InitListExpr will be invoked separately for them
+ InitInx++;
+ }
+
+ // Then the members defined in this record
auto RecordFields = CXXRecord->fields().begin();
- for (size_t I = 0; I < InitExprs.size();
- ++I, RecordFields = std::next(RecordFields)) {
- // TODO: is prepending "." sufficient?
- addDesignatorHint(InitExprs[I]->getSourceRange(),
+ for (; InitInx < InitExprs.size();
+ ++InitInx, RecordFields = std::next(RecordFields)) {
+ addDesignatorHint(InitExprs[InitInx]->getSourceRange(),
"." + RecordFields->getName().str());
}
}
+
return true;
}
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 72e7be2f3d943..afa67c336b867 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1834,6 +1834,24 @@ TEST(DesignatorHints, BasicParenInit) {
ExpectedHint{".z=", "z"});
}
+TEST(DesignatorHints, BasicParenInitDerived) {
+ assertDesignatorHints(R"cpp(
+ struct S1 {
+ int a;
+ int b;
+ };
+
+ struct S2 : S1 {
+ int c;
+ int d;
+ };
+ S2 s2 ({$a[[0]], $b[[0]]}, $c[[0]], $d[[0]]);
+ )cpp",
+ // ExpectedHint{"S1:", "S1"},
+ ExpectedHint{".a=", "a"}, ExpectedHint{".b=", "b"},
+ ExpectedHint{".c=", "c"}, ExpectedHint{".d=", "d"});
+}
+
TEST(InlayHints, RestrictRange) {
Annotations Code(R"cpp(
auto a = false;
>From b706eb2bb9df17d3067796f8a635b6a9bd615573 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Sun, 7 Dec 2025 00:10:00 -0800
Subject: [PATCH 4/7] code-review
---
clang-tools-extra/clangd/InlayHints.cpp | 11 +++-----
.../clangd/unittests/InlayHintTests.cpp | 26 +++++++++++++++++++
2 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 9476fe9deea57..040015cfc2d13 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -704,15 +704,12 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
return true;
if (const auto *CXXRecord = E->getType()->getAsCXXRecordDecl()) {
- const auto &InitExprs = E->getInitExprs();
+ const auto &InitExprs = E->getUserSpecifiedInitExprs();
size_t InitInx = 0;
- // Inherited members are first
- for (const auto &Base : CXXRecord->bases()) {
- std::ignore = Base;
- // For a base record, just use its name
- // Base of base requires its own initialization; ParenListInitExpr or
- // InitListExpr will be invoked separately for them
+ // Inherited members are first, skip them for now.
+ // FIXME: '.base=' or 'base:' hint?
+ for (const auto &_ : CXXRecord->bases()) {
InitInx++;
}
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index afa67c336b867..d54e36c2f8a3b 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1852,6 +1852,32 @@ TEST(DesignatorHints, BasicParenInitDerived) {
ExpectedHint{".c=", "c"}, ExpectedHint{".d=", "d"});
}
+TEST(DesignatorHints, BasicParenInitTemplate) {
+ assertDesignatorHints(R"cpp(
+ template <typename T>
+ struct S1 {
+ int a;
+ int b;
+ T* ptr;
+ };
+
+ struct S2 : S1<S2> {
+ int c;
+ int d;
+ S1<int> mem;
+ };
+
+ int main() {
+ S2 sa ({$a1[[0]], $b1[[0]]}, $c[[0]], $d[[0]], $mem[[S1<int>($a2[[1]], $b2[[2]], $ptr[[nullptr]])]]);
+ }
+ )cpp",
+ ExpectedHint{".a=", "a1"}, ExpectedHint{".b=", "b1"},
+ ExpectedHint{".c=", "c"}, ExpectedHint{".d=", "d"},
+ ExpectedHint{".mem=", "mem"}, ExpectedHint{".a=", "a2"},
+ ExpectedHint{".b=", "b2"},
+ ExpectedHint{".ptr=", "ptr"});
+}
+
TEST(InlayHints, RestrictRange) {
Annotations Code(R"cpp(
auto a = false;
>From 65c99319ae56262cb92b21111b29888695cca5d7 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Sun, 7 Dec 2025 03:26:09 -0800
Subject: [PATCH 5/7] code-review
Co-authored-by: zwuis <zwuis at outlook.com>
---
clang-tools-extra/clangd/InlayHints.cpp | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 040015cfc2d13..2290fbd98056d 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -705,21 +705,20 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
if (const auto *CXXRecord = E->getType()->getAsCXXRecordDecl()) {
const auto &InitExprs = E->getUserSpecifiedInitExprs();
- size_t InitInx = 0;
- // Inherited members are first, skip them for now.
- // FIXME: '.base=' or 'base:' hint?
- for (const auto &_ : CXXRecord->bases()) {
- InitInx++;
- }
+ if (InitExprs.size() <= CXXRecord->getNumBases())
+ return true;
- // Then the members defined in this record
- auto RecordFields = CXXRecord->fields().begin();
-
- for (; InitInx < InitExprs.size();
- ++InitInx, RecordFields = std::next(RecordFields)) {
- addDesignatorHint(InitExprs[InitInx]->getSourceRange(),
- "." + RecordFields->getName().str());
+ // Inherited members are first, skip hinting them for now.
+ // FIXME: '.base=' or 'base:' hint?
+ const auto &MemberInitExprs =
+ InitExprs.drop_front(CXXRecord->getNumBases());
+
+ // Then the fields in this record
+ for (const auto &[InitExpr, Field] :
+ llvm::zip(MemberInitExprs, CXXRecord->fields())) {
+ addDesignatorHint(InitExpr->getSourceRange(),
+ "." + Field->getName().str());
}
}
>From 23f831bff319882a33cae76197504c27a460939b Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Sat, 13 Dec 2025 03:57:51 -0800
Subject: [PATCH 6/7] Update release notes
---
clang-tools-extra/docs/ReleaseNotes.rst | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 924b2c03cfd18..f79b6d81cf325 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -92,6 +92,9 @@ Improvements to clangd
Inlay hints
^^^^^^^^^^^
+- ``clangd`` now shows designator hints for aggregate initialization of structures
+ with parentheses-list initialization (``CXXParenListInitExpr``) syntax.
+
Diagnostics
^^^^^^^^^^^
>From 3590dedab7558059df58253229e9b9546c956015 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Fri, 9 Jan 2026 18:09:13 -0800
Subject: [PATCH 7/7] code review
---
.../clangd/unittests/InlayHintTests.cpp | 17 ++---------------
1 file changed, 2 insertions(+), 15 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index d54e36c2f8a3b..643cac62e2fe5 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1732,19 +1732,6 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
}
TEST(DesignatorHints, Basic) {
- assertDesignatorHints(R"cpp(
- struct Point {
- int x;
- int y;
- };
- void bar() {
- Point p{$x[[41]], $y[[42]]};
- }
- )cpp",
- ExpectedHint{".x=", "x"}, ExpectedHint{".y=", "y"});
-}
-
-TEST(DesignatorHints, BasicArray) {
assertDesignatorHints(R"cpp(
struct S { int x, y, z; };
S s {$x[[1]], $y[[2+2]]};
@@ -1821,7 +1808,7 @@ TEST(DesignatorHints, NoCrash) {
ExpectedHint{".b=", "b"});
}
-TEST(DesignatorHints, BasicParenInit) {
+TEST(DesignatorHints, ParenInit) {
assertDesignatorHints(R"cpp(
struct S {
int x;
@@ -1834,7 +1821,7 @@ TEST(DesignatorHints, BasicParenInit) {
ExpectedHint{".z=", "z"});
}
-TEST(DesignatorHints, BasicParenInitDerived) {
+TEST(DesignatorHints, ParenInitDerived) {
assertDesignatorHints(R"cpp(
struct S1 {
int a;
More information about the cfe-commits
mailing list