[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
ArcsinX wrote: > LGTM if @ArcsinX is happy. I didn't look at the implementation code closely, but I checked that from a functionality point of view everything works as expected https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
schuay wrote: And done. PR text updated to mention AI assistance. Added bitfield tests, and skipped bitfields via `ResultBuilder::IsOffsetofField`. https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
https://github.com/schuay updated
https://github.com/llvm/llvm-project/pull/195126
>From edd263a699c37dd2dca16a5abce9cba228d059e3 Mon Sep 17 00:00:00 2001
From: Jakob Linke
Date: Thu, 30 Apr 2026 18:46:52 +0200
Subject: [PATCH] [clang] Complete fields in __builtin_offsetof designators
Code completion was a no-op inside __builtin_offsetof: a cursor at
__builtin_offsetof(T, ^) or __builtin_offsetof(T, a.^) fell through
to ordinary-name completion instead of suggesting fields. Route the
code_completion token to a new SemaCodeCompletion entry point that
walks the designator path so far, resolves the subobject's type, and
enumerates its members. Methods are filtered out, inherited fields
are included, indirect fields from anonymous unions and structs are
peeled, and `using Base::field` resolves through its UsingShadowDecl.
A code_completion token past a complete component (right after `]`
or at the end of the chain) is dropped rather than offering fields
the user can't paste without first typing `.`.
The offsetof and designated-initializer type walkers are folded into
one helper parameterized by a field-lookup callback, which
incidentally fixes reference-field and indirect-field traversal in
designated-initializer completion too.
Tests: lit cases in offsetof.cpp covering empty/dot/array/inheritance/
reference/anonymous/using-shadow/macro forms, extended desig-init.cpp
walker cases, and a clangd unit test exercising the IDE path.
---
.../clangd/unittests/CodeCompleteTests.cpp| 39 +
clang/include/clang/Sema/SemaCodeCompletion.h | 3 +
clang/lib/Parse/ParseExpr.cpp | 40 +-
clang/lib/Sema/SemaCodeComplete.cpp | 133 +++---
clang/test/CodeCompletion/desig-init.cpp | 34 +
clang/test/CodeCompletion/offsetof.cpp| 128 +
6 files changed, 356 insertions(+), 21 deletions(-)
create mode 100644 clang/test/CodeCompletion/offsetof.cpp
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 386ffb54924a7..109156fa4d176 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -2539,6 +2539,45 @@ TEST(CompletionTest, CodeCompletionContext) {
EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess);
}
+TEST(CompletionTest, OffsetOfDesignator) {
+ auto Results = completions(R"cpp(
+struct S { int field; int other; void fieldFn(); };
+int x = __builtin_offsetof(S, fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+ EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess);
+
+ Results = completions(R"cpp(
+struct Inner { int field; void fieldFn(); };
+struct Outer { Inner inner; };
+int x = __builtin_offsetof(Outer, inner.fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+
+ Results = completions(R"cpp(
+struct Inner { int field; void fieldFn(); };
+struct Outer { Inner inner[2]; };
+int i;
+int x = __builtin_offsetof(Outer, inner[i].fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+
+ Results = completions(R"cpp(
+struct Base { int field; void fieldFn(); };
+struct Derived : Base {};
+int x = __builtin_offsetof(Derived, fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+}
+
TEST(CompletionTest, FixItForArrowToDot) {
MockFS FS;
MockCompilationDatabase CDB;
diff --git a/clang/include/clang/Sema/SemaCodeCompletion.h
b/clang/include/clang/Sema/SemaCodeCompletion.h
index abdfb51900318..7203b19d58898 100644
--- a/clang/include/clang/Sema/SemaCodeCompletion.h
+++ b/clang/include/clang/Sema/SemaCodeCompletion.h
@@ -154,6 +154,9 @@ class SemaCodeCompletion : public SemaBase {
void CodeCompleteDesignator(const QualType BaseType,
llvm::ArrayRef InitExprs,
const Designation &D);
+ /// Trigger code completion for a position inside a __builtin_offsetof
+ /// member designator (after the type's `,`, or after a `.`).
+ void CodeCompleteOffsetOfDesignator(QualType BaseType, const Designation &D);
void CodeCompleteKeywordAfterIf(bool AfterExclaim) const;
void CodeCompleteAfterIf(Scope *S, bool IsBracedThen);
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index c3ac8d7e6eb74..34e0d7905cd23 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2407,7 +2407,18 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
return ExprError();
}
+auto TriggerCompletion = [&](const Designation &D) {
+
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
https://github.com/schuay edited https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
schuay wrote: > Please disclose AI usage in PR description. > > https://llvm.org/docs/AIToolPolicy.html Will do, wasn't aware of this. https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
zwuis wrote: Please disclose AI usage in PR description. https://llvm.org/docs/AIToolPolicy.html https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
schuay wrote: Thanks for the review! Pushed a follow-up commit that filters bit-fields from the offsetof completion (since they are not valid as the second argument; `err_offsetof_bitfield`) and adds tests for both direct and indirect (anonymous-member) bit-fields. https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
@@ -1670,6 +1675,11 @@ bool ResultBuilder::IsMember(const NamedDecl *ND) const {
isa(ND);
}
+bool ResultBuilder::IsField(const NamedDecl *ND) const {
+ ND = ND->getUnderlyingDecl();
+ return isa(ND) || isa(ND);
schuay wrote:
Good suggestion. After adding the bit-field filter (the other review comment),
the function needs to inspect the cast result to call `isBitField()`, so I
switched it to `dyn_cast` instead. Let me know if you'd prefer a different
shape.
https://github.com/llvm/llvm-project/pull/195126
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
https://github.com/schuay updated
https://github.com/llvm/llvm-project/pull/195126
>From a804b4d51fc4720e54d894dab4fc99938cdf2a0b Mon Sep 17 00:00:00 2001
From: Jakob Linke
Date: Thu, 30 Apr 2026 18:46:52 +0200
Subject: [PATCH 1/2] [clang] Complete fields in __builtin_offsetof designators
Code completion was a no-op inside __builtin_offsetof: a cursor at
__builtin_offsetof(T, ^) or __builtin_offsetof(T, a.^) fell through
to ordinary-name completion instead of suggesting fields. Route the
code_completion token to a new SemaCodeCompletion entry point that
walks the designator path so far, resolves the subobject's type, and
enumerates its members. Methods are filtered out, inherited fields
are included, indirect fields from anonymous unions and structs are
peeled, and `using Base::field` resolves through its UsingShadowDecl.
A code_completion token past a complete component (right after `]`
or at the end of the chain) is dropped rather than offering fields
the user can't paste without first typing `.`.
The offsetof and designated-initializer type walkers are folded into
one helper parameterized by a field-lookup callback, which
incidentally fixes reference-field and indirect-field traversal in
designated-initializer completion too.
Tests: lit cases in offsetof.cpp covering empty/dot/array/inheritance/
reference/anonymous/using-shadow/macro forms, extended desig-init.cpp
walker cases, and a clangd unit test exercising the IDE path.
---
.../clangd/unittests/CodeCompleteTests.cpp| 39 ++
clang/include/clang/Sema/SemaCodeCompletion.h | 3 +
clang/lib/Parse/ParseExpr.cpp | 40 +-
clang/lib/Sema/SemaCodeComplete.cpp | 127 +++---
clang/test/CodeCompletion/desig-init.cpp | 34 +
clang/test/CodeCompletion/offsetof.cpp| 95 +
6 files changed, 317 insertions(+), 21 deletions(-)
create mode 100644 clang/test/CodeCompletion/offsetof.cpp
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 386ffb54924a7..109156fa4d176 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -2539,6 +2539,45 @@ TEST(CompletionTest, CodeCompletionContext) {
EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess);
}
+TEST(CompletionTest, OffsetOfDesignator) {
+ auto Results = completions(R"cpp(
+struct S { int field; int other; void fieldFn(); };
+int x = __builtin_offsetof(S, fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+ EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess);
+
+ Results = completions(R"cpp(
+struct Inner { int field; void fieldFn(); };
+struct Outer { Inner inner; };
+int x = __builtin_offsetof(Outer, inner.fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+
+ Results = completions(R"cpp(
+struct Inner { int field; void fieldFn(); };
+struct Outer { Inner inner[2]; };
+int i;
+int x = __builtin_offsetof(Outer, inner[i].fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+
+ Results = completions(R"cpp(
+struct Base { int field; void fieldFn(); };
+struct Derived : Base {};
+int x = __builtin_offsetof(Derived, fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+}
+
TEST(CompletionTest, FixItForArrowToDot) {
MockFS FS;
MockCompilationDatabase CDB;
diff --git a/clang/include/clang/Sema/SemaCodeCompletion.h
b/clang/include/clang/Sema/SemaCodeCompletion.h
index abdfb51900318..7203b19d58898 100644
--- a/clang/include/clang/Sema/SemaCodeCompletion.h
+++ b/clang/include/clang/Sema/SemaCodeCompletion.h
@@ -154,6 +154,9 @@ class SemaCodeCompletion : public SemaBase {
void CodeCompleteDesignator(const QualType BaseType,
llvm::ArrayRef InitExprs,
const Designation &D);
+ /// Trigger code completion for a position inside a __builtin_offsetof
+ /// member designator (after the type's `,`, or after a `.`).
+ void CodeCompleteOffsetOfDesignator(QualType BaseType, const Designation &D);
void CodeCompleteKeywordAfterIf(bool AfterExclaim) const;
void CodeCompleteAfterIf(Scope *S, bool IsBracedThen);
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index c3ac8d7e6eb74..34e0d7905cd23 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2407,7 +2407,18 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
return ExprError();
}
+auto TriggerCompletion = [&](const Designation &D) {
+
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
https://github.com/zwuis edited https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
https://github.com/zwuis commented: Could you please add tests with bit-fields, which should not be the second argument of `offsetof`? https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
@@ -1670,6 +1675,11 @@ bool ResultBuilder::IsMember(const NamedDecl *ND) const {
isa(ND);
}
+bool ResultBuilder::IsField(const NamedDecl *ND) const {
+ ND = ND->getUnderlyingDecl();
+ return isa(ND) || isa(ND);
zwuis wrote:
```cpp
isa(ND)
```
https://github.com/llvm/llvm-project/pull/195126
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
https://github.com/schuay edited https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
schuay wrote: cc @ArcsinX https://github.com/llvm/llvm-project/pull/195126 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang] Complete fields in __builtin_offsetof designators (PR #195126)
https://github.com/schuay updated
https://github.com/llvm/llvm-project/pull/195126
>From a804b4d51fc4720e54d894dab4fc99938cdf2a0b Mon Sep 17 00:00:00 2001
From: Jakob Linke
Date: Thu, 30 Apr 2026 18:46:52 +0200
Subject: [PATCH] [clang] Complete fields in __builtin_offsetof designators
Code completion was a no-op inside __builtin_offsetof: a cursor at
__builtin_offsetof(T, ^) or __builtin_offsetof(T, a.^) fell through
to ordinary-name completion instead of suggesting fields. Route the
code_completion token to a new SemaCodeCompletion entry point that
walks the designator path so far, resolves the subobject's type, and
enumerates its members. Methods are filtered out, inherited fields
are included, indirect fields from anonymous unions and structs are
peeled, and `using Base::field` resolves through its UsingShadowDecl.
A code_completion token past a complete component (right after `]`
or at the end of the chain) is dropped rather than offering fields
the user can't paste without first typing `.`.
The offsetof and designated-initializer type walkers are folded into
one helper parameterized by a field-lookup callback, which
incidentally fixes reference-field and indirect-field traversal in
designated-initializer completion too.
Tests: lit cases in offsetof.cpp covering empty/dot/array/inheritance/
reference/anonymous/using-shadow/macro forms, extended desig-init.cpp
walker cases, and a clangd unit test exercising the IDE path.
---
.../clangd/unittests/CodeCompleteTests.cpp| 39 ++
clang/include/clang/Sema/SemaCodeCompletion.h | 3 +
clang/lib/Parse/ParseExpr.cpp | 40 +-
clang/lib/Sema/SemaCodeComplete.cpp | 127 +++---
clang/test/CodeCompletion/desig-init.cpp | 34 +
clang/test/CodeCompletion/offsetof.cpp| 95 +
6 files changed, 317 insertions(+), 21 deletions(-)
create mode 100644 clang/test/CodeCompletion/offsetof.cpp
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 386ffb54924a7..109156fa4d176 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -2539,6 +2539,45 @@ TEST(CompletionTest, CodeCompletionContext) {
EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess);
}
+TEST(CompletionTest, OffsetOfDesignator) {
+ auto Results = completions(R"cpp(
+struct S { int field; int other; void fieldFn(); };
+int x = __builtin_offsetof(S, fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+ EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess);
+
+ Results = completions(R"cpp(
+struct Inner { int field; void fieldFn(); };
+struct Outer { Inner inner; };
+int x = __builtin_offsetof(Outer, inner.fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+
+ Results = completions(R"cpp(
+struct Inner { int field; void fieldFn(); };
+struct Outer { Inner inner[2]; };
+int i;
+int x = __builtin_offsetof(Outer, inner[i].fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+
+ Results = completions(R"cpp(
+struct Base { int field; void fieldFn(); };
+struct Derived : Base {};
+int x = __builtin_offsetof(Derived, fiel^d);
+ )cpp");
+ EXPECT_THAT(
+ Results.Completions,
+ ElementsAre(AllOf(named("field"), kind(CompletionItemKind::Field;
+}
+
TEST(CompletionTest, FixItForArrowToDot) {
MockFS FS;
MockCompilationDatabase CDB;
diff --git a/clang/include/clang/Sema/SemaCodeCompletion.h
b/clang/include/clang/Sema/SemaCodeCompletion.h
index abdfb51900318..7203b19d58898 100644
--- a/clang/include/clang/Sema/SemaCodeCompletion.h
+++ b/clang/include/clang/Sema/SemaCodeCompletion.h
@@ -154,6 +154,9 @@ class SemaCodeCompletion : public SemaBase {
void CodeCompleteDesignator(const QualType BaseType,
llvm::ArrayRef InitExprs,
const Designation &D);
+ /// Trigger code completion for a position inside a __builtin_offsetof
+ /// member designator (after the type's `,`, or after a `.`).
+ void CodeCompleteOffsetOfDesignator(QualType BaseType, const Designation &D);
void CodeCompleteKeywordAfterIf(bool AfterExclaim) const;
void CodeCompleteAfterIf(Scope *S, bool IsBracedThen);
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index c3ac8d7e6eb74..34e0d7905cd23 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2407,7 +2407,18 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
return ExprError();
}
+auto TriggerCompletion = [&](const Designation &D) {
+
