Skip to content

Conversation

@MythreyaK
Copy link
Contributor

Enabled hints for CXXParenListInitExpr,

struct Point {
  int x;
  int y;
};

int main() {
  Point p1{1, 2};  // brace syntax  <-- already present
  Point p2(1, 2);  // parens syntax <-- added in this commit
}
image

Fixes clangd/clangd#2540

Copy link
Contributor

@zwuis zwuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add tests with classes having base classes? Aggregates are allowed to have base classes since C++17.

https://en.cppreference.com/w/cpp/language/aggregate_initialization.html

Comment on lines +1839 to +1850
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"},
Copy link
Contributor Author

@MythreyaK MythreyaK Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add some form of hint here?

S2 s2 ({0, 0}, 0, 0); // S2 s2 ({.a=0, .b=0}, .c=0, .d=0); // current

S2 s2 ({0, 0}, 0, 0); // S2 s2 (S1:{.a=0, .b=0}, .c=0, .d=0); 
// Some hint here? -------------^

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HighCommander4 suggested we add this in a separate PR so that it's easy to revert if that hint is not well received. Will create a separate PR for it.

@MythreyaK
Copy link
Contributor Author

I think I'm seeing a bug. For this snippet,

template <typename T>
struct S1 {
  int a;
  int b;
  T* ptr;
};

struct S2 : S1<S2> {
  int c;
  int d;
  S1<S1> mem;
};
S2 s2 ({0, 0}, 0, 0);
S2 s2 ({$a[[0]], $b[[0]]}, $c[[0]], $d[[0]]);

source range for mem is

------- FieldDecl 1 -------
FIELD: : RecordFields ----- (/llvm-project/clang-tools-extra/clangd/InlayHints.cpp:746)
FieldDecl 0x2233ae10 </clangd-test/TestTU.cpp:10:3, col:7> col:7 c 'int'
--- InitExpr 1 source range ---
 </clangd-test/TestTU.cpp:14:16>
/clangd-test/TestTU.cpp:14:16
/clangd-test/TestTU.cpp:14:16
------------ END ------------

------- FieldDecl 2 -------------------------
FIELD: : RecordFields ----- (/llvm-project/clang-tools-extra/clangd/InlayHints.cpp:746)
FieldDecl 0x2233ae80 </clangd-test/TestTU.cpp:11:3, col:7> col:7 d 'int'
--- InitExpr 2 source range ---
 </clangd-test/TestTU.cpp:14:19>
/clangd-test/TestTU.cpp:14:19
/clangd-test/TestTU.cpp:14:19
------------ END ------------

------- FieldDecl 3 -------------------------
FIELD: : RecordFields ----- (/llvm-project/clang-tools-extra/clangd/InlayHints.cpp:746)
FieldDecl 0x223aaff8 </clangd-test/TestTU.cpp:12:3, col:10> col:10 mem 'S1<S1<S2>>'
--- InitExpr 3 source range ---
 </clangd-test/TestTU.cpp:14:4>
/clangd-test/TestTU.cpp:14:4    // <------ source range seems incorrect?
/clangd-test/TestTU.cpp:14:4    // <------ source range seems incorrect?
------------ END ------------

@zwuis
Copy link
Contributor

zwuis commented Dec 6, 2025

I'm not sure if it is a bug. But it doesn't block this PR because no need to emit designator hints for mem AFAICT.

Update: Oh I see. We need to distinguish between these cases:

struct S1 {};
struct S2 : S1 {
  int a;
  S1 s1;
};
struct S3 { S3() {} };
struct S4 : S3 {
  int a;
  S3 s3;
};
S2 s2({}, 0);
S4 s4({}, 0, {});

@zwuis
Copy link
Contributor

zwuis commented Dec 6, 2025

We can use CXXParenListInitExpr::getUserSpecifiedInitExprs() so that we don't need to handle implicit initializations.

@MythreyaK
Copy link
Contributor Author

We can use CXXParenListInitExpr::getUserSpecifiedInitExprs() so that we don't need to handle implicit initializations.

Didn't notice that; Thank you, that fixed it!

@MythreyaK MythreyaK marked this pull request as ready for review December 7, 2025 08:25
@llvmbot
Copy link
Member

llvmbot commented Dec 7, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Mythreya Kuricheti (MythreyaK)

Changes

Enabled hints for CXXParenListInitExpr,

struct Point {
  int x;
  int y;
};

int main() {
  Point p1{1, 2};  // brace syntax  &lt;-- already present
  Point p2(1, 2);  // parens syntax &lt;-- added in this commit
}

<img width="434" height="413" alt="image" src="https://github.com/user-attachments/assets/710d7168-30f7-494d-8b2c-6413fa4152a7" />

Fixes clangd/clangd#2540


Full diff: https://github.com/llvm/llvm-project/pull/170642.diff

2 Files Affected:

  • (modified) clang-tools-extra/clangd/InlayHints.cpp (+27)
  • (modified) clang-tools-extra/clangd/unittests/InlayHintTests.cpp (+70-14)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 23bd02304a4fd..040015cfc2d13 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -699,6 +699,33 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
     return InstantiatedFunction->getParamDecl(ParamIdx);
   }
 
+  bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+    if (!Cfg.InlayHints.Designators)
+      return true;
+
+    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++;
+      }
+
+      // 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());
+      }
+    }
+
+    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 feb4404b3d2bf..d54e36c2f8a3b 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]]};
@@ -1822,6 +1821,63 @@ 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(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(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;

@llvmbot
Copy link
Member

llvmbot commented Dec 7, 2025

@llvm/pr-subscribers-clangd

Author: Mythreya Kuricheti (MythreyaK)

Changes

Enabled hints for CXXParenListInitExpr,

struct Point {
  int x;
  int y;
};

int main() {
  Point p1{1, 2};  // brace syntax  &lt;-- already present
  Point p2(1, 2);  // parens syntax &lt;-- added in this commit
}

<img width="434" height="413" alt="image" src="https://github.com/user-attachments/assets/710d7168-30f7-494d-8b2c-6413fa4152a7" />

Fixes clangd/clangd#2540


Full diff: https://github.com/llvm/llvm-project/pull/170642.diff

2 Files Affected:

  • (modified) clang-tools-extra/clangd/InlayHints.cpp (+27)
  • (modified) clang-tools-extra/clangd/unittests/InlayHintTests.cpp (+70-14)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 23bd02304a4fd..040015cfc2d13 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -699,6 +699,33 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
     return InstantiatedFunction->getParamDecl(ParamIdx);
   }
 
+  bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+    if (!Cfg.InlayHints.Designators)
+      return true;
+
+    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++;
+      }
+
+      // 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());
+      }
+    }
+
+    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 feb4404b3d2bf..d54e36c2f8a3b 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]]};
@@ -1822,6 +1821,63 @@ 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(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(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;

Co-authored-by: zwuis <zwuis@outlook.com>
@MythreyaK MythreyaK requested a review from zwuis December 7, 2025 11:38
@zwuis zwuis requested a review from HighCommander4 December 7, 2025 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Designator hints do not work with parenthesis aggregate initialization syntax

3 participants