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 <[email protected]>
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<Expr *> 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) {
+      cutOffParsing();
+      Actions.CodeCompletion().CodeCompleteOffsetOfDesignator(
+          Actions.GetTypeFromParser(Ty.get()), D);
+    };
+
     // We must have at least one identifier here.
+    Designation D;
+    if (Tok.is(tok::code_completion)) {
+      TriggerCompletion(D);
+      return ExprError();
+    }
     if (Tok.isNot(tok::identifier)) {
       Diag(Tok, diag::err_expected) << tok::identifier;
       SkipUntil(tok::r_paren, StopAtSemi);
@@ -2415,12 +2426,18 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
     }
 
     // Keep track of the various subcomponents we see.
+    // FIXME: Comps and D below carry the same designator chain in two
+    // different shapes. ActOnBuiltinOffsetOf should be taught to accept a
+    // Designation directly so this duplication can go away.
     SmallVector<Sema::OffsetOfComponent, 4> Comps;
 
     Comps.push_back(Sema::OffsetOfComponent());
     Comps.back().isBrackets = false;
     Comps.back().U.IdentInfo = Tok.getIdentifierInfo();
-    Comps.back().LocStart = Comps.back().LocEnd = ConsumeToken();
+    Comps.back().LocStart = Comps.back().LocEnd = Tok.getLocation();
+    D.AddDesignator(Designator::CreateFieldDesignator(
+        Tok.getIdentifierInfo(), SourceLocation(), Tok.getLocation()));
+    ConsumeToken();
 
     // FIXME: This loop leaks the index expressions on error.
     while (true) {
@@ -2430,13 +2447,20 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
         Comps.back().isBrackets = false;
         Comps.back().LocStart = ConsumeToken();
 
+        if (Tok.is(tok::code_completion)) {
+          TriggerCompletion(D);
+          return ExprError();
+        }
         if (Tok.isNot(tok::identifier)) {
           Diag(Tok, diag::err_expected) << tok::identifier;
           SkipUntil(tok::r_paren, StopAtSemi);
           return ExprError();
         }
         Comps.back().U.IdentInfo = Tok.getIdentifierInfo();
-        Comps.back().LocEnd = ConsumeToken();
+        Comps.back().LocEnd = Tok.getLocation();
+        D.AddDesignator(Designator::CreateFieldDesignator(
+            Tok.getIdentifierInfo(), Comps.back().LocStart, 
Tok.getLocation()));
+        ConsumeToken();
       } else if (Tok.is(tok::l_square)) {
         if (CheckProhibitedCXX11Attribute())
           return ExprError();
@@ -2456,7 +2480,19 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
 
         ST.consumeClose();
         Comps.back().LocEnd = ST.getCloseLocation();
+        Designator ArrayD =
+            Designator::CreateArrayDesignator(Res.get(), 
Comps.back().LocStart);
+        ArrayD.setRBracketLoc(Comps.back().LocEnd);
+        D.AddDesignator(ArrayD);
       } else {
+        // A code-completion token here (e.g. cursor right after `]`) is past
+        // the point where a field can be applied without a leading `.`. Drop
+        // it on the floor rather than leak into outer-scope completion or
+        // emit field suggestions that wouldn't compose.
+        if (Tok.is(tok::code_completion)) {
+          cutOffParsing();
+          return ExprError();
+        }
         if (Tok.isNot(tok::r_paren)) {
           PT.consumeClose();
           Res = ExprError();
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp 
b/clang/lib/Sema/SemaCodeComplete.cpp
index 0cd9819dc2964..ca0086fa37ac1 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -407,6 +407,7 @@ class ResultBuilder {
   bool IsNamespaceOrAlias(const NamedDecl *ND) const;
   bool IsType(const NamedDecl *ND) const;
   bool IsMember(const NamedDecl *ND) const;
+  bool IsOffsetofField(const NamedDecl *ND) const;
   bool IsObjCIvar(const NamedDecl *ND) const;
   bool IsObjCMessageReceiver(const NamedDecl *ND) const;
   bool IsObjCMessageReceiverOrLambdaCapture(const NamedDecl *ND) const;
@@ -445,8 +446,12 @@ void 
PreferredTypeBuilder::enterVariableInit(SourceLocation Tok, Decl *D) {
   ExpectedLoc = Tok;
 }
 
-static QualType getDesignatedType(QualType BaseType, const Designation &Desig,
-                                  HeuristicResolver &Resolver);
+static const FieldDecl *lookupDirectField(RecordDecl *RD, const Designator &D);
+static QualType getDesignatedType(
+    ASTContext &Context, QualType BaseType, const Designation &Desig,
+    HeuristicResolver &Resolver,
+    llvm::function_ref<const FieldDecl *(RecordDecl *, const Designator &)>
+        LookupField);
 
 void PreferredTypeBuilder::enterDesignatedInitializer(SourceLocation Tok,
                                                       QualType BaseType,
@@ -455,7 +460,7 @@ void 
PreferredTypeBuilder::enterDesignatedInitializer(SourceLocation Tok,
     return;
   ComputeType = nullptr;
   HeuristicResolver Resolver(*Ctx);
-  Type = getDesignatedType(BaseType, D, Resolver);
+  Type = getDesignatedType(*Ctx, BaseType, D, Resolver, lookupDirectField);
   ExpectedLoc = Tok;
 }
 
@@ -1670,6 +1675,17 @@ bool ResultBuilder::IsMember(const NamedDecl *ND) const {
          isa<ObjCPropertyDecl>(ND);
 }
 
+/// Determines whether the given declaration is a member that
+/// __builtin_offsetof can name: a (direct or indirect) non-bit-field.
+bool ResultBuilder::IsOffsetofField(const NamedDecl *ND) const {
+  ND = ND->getUnderlyingDecl();
+  if (const auto *FD = dyn_cast<FieldDecl>(ND))
+    return !FD->isBitField();
+  if (const auto *IFD = dyn_cast<IndirectFieldDecl>(ND))
+    return !IFD->getAnonField()->isBitField();
+  return false;
+}
+
 static bool isObjCReceiverType(ASTContext &C, QualType T) {
   T = C.getCanonicalType(T);
   switch (T->getTypeClass()) {
@@ -6731,35 +6747,63 @@ QualType 
SemaCodeCompletion::ProduceTemplateArgumentSignatureHelp(
                               /*Braced=*/false);
 }
 
-static QualType getDesignatedType(QualType BaseType, const Designation &Desig,
-                                  HeuristicResolver &Resolver) {
+// Direct member lookup, used by designated initializers: only fields declared
+// in `RD` itself (including indirect fields from anonymous members) are valid.
+static const FieldDecl *lookupDirectField(RecordDecl *RD, const Designator &D) 
{
+  for (const auto *Member : RD->lookup(D.getFieldDecl())) {
+    if (const auto *FD = llvm::dyn_cast<FieldDecl>(Member))
+      return FD;
+    if (const auto *IFD = llvm::dyn_cast<IndirectFieldDecl>(Member))
+      return IFD->getAnonField();
+  }
+  return nullptr;
+}
+
+static QualType getDesignatedType(
+    ASTContext &Context, QualType BaseType, const Designation &Desig,
+    HeuristicResolver &Resolver,
+    llvm::function_ref<const FieldDecl *(RecordDecl *, const Designator &)>
+        LookupField) {
   for (unsigned I = 0; I < Desig.getNumDesignators(); ++I) {
     if (BaseType.isNull())
       break;
-    QualType NextType;
+
     const auto &D = Desig.getDesignator(I);
     if (D.isArrayDesignator() || D.isArrayRangeDesignator()) {
-      if (BaseType->isArrayType())
-        NextType = BaseType->getAsArrayTypeUnsafe()->getElementType();
-    } else {
-      assert(D.isFieldDesignator());
-      auto *RD = getAsRecordDecl(BaseType, Resolver);
-      if (RD && RD->isCompleteDefinition()) {
-        for (const auto *Member : RD->lookup(D.getFieldDecl()))
-          if (const FieldDecl *FD = llvm::dyn_cast<FieldDecl>(Member)) {
-            NextType = FD->getType();
-            break;
-          }
+      if (BaseType->isDependentType()) {
+        BaseType = Context.DependentTy;
+        continue;
       }
+      const ArrayType *AT = Context.getAsArrayType(BaseType);
+      if (!AT)
+        return QualType();
+      BaseType = AT->getElementType();
+      continue;
+    }
+
+    assert(D.isFieldDesignator());
+    if (BaseType->isDependentType()) {
+      BaseType = Context.DependentTy;
+      continue;
     }
-    BaseType = NextType;
+
+    RecordDecl *RD = getAsRecordDecl(BaseType, Resolver);
+    if (!RD || !RD->isCompleteDefinition())
+      return QualType();
+
+    const FieldDecl *MemberDecl = LookupField(RD, D);
+    if (!MemberDecl)
+      return QualType();
+
+    BaseType = MemberDecl->getType().getNonReferenceType();
   }
   return BaseType;
 }
 
 void SemaCodeCompletion::CodeCompleteDesignator(
     QualType BaseType, llvm::ArrayRef<Expr *> InitExprs, const Designation &D) 
{
-  BaseType = getDesignatedType(BaseType, D, Resolver);
+  BaseType = getDesignatedType(SemaRef.Context, BaseType, D, Resolver,
+                               lookupDirectField);
   if (BaseType.isNull())
     return;
   const auto *RD = getAsRecordDecl(BaseType, Resolver);
@@ -6792,6 +6836,57 @@ void SemaCodeCompletion::CodeCompleteDesignator(
                             Results.size());
 }
 
+void SemaCodeCompletion::CodeCompleteOffsetOfDesignator(QualType BaseType,
+                                                        const Designation &D) {
+  // offsetof allows inherited fields and follows normal qualified name lookup,
+  // not the direct-member iteration used by designated initializers.
+  auto LookupQualified = [&](RecordDecl *RD,
+                             const Designator &Des) -> const FieldDecl * {
+    LookupResult R(SemaRef, Des.getFieldDecl(), Des.getFieldLoc(),
+                   Sema::LookupMemberName);
+    SemaRef.LookupQualifiedName(R, RD);
+    // Peel via getUnderlyingDecl so a field exposed by `using Base::f;`
+    // resolves through its UsingShadowDecl.
+    for (NamedDecl *ND : R) {
+      ND = ND->getUnderlyingDecl();
+      if (auto *FD = dyn_cast<FieldDecl>(ND))
+        return FD;
+      if (auto *IFD = dyn_cast<IndirectFieldDecl>(ND))
+        return IFD->getAnonField();
+    }
+    return nullptr;
+  };
+  BaseType = getDesignatedType(SemaRef.Context, BaseType, D, Resolver,
+                               LookupQualified);
+  if (BaseType.isNull())
+    return;
+
+  RecordDecl *RD = getAsRecordDecl(BaseType, Resolver);
+  if (!RD)
+    return;
+
+  CodeCompletionContext CCC(CodeCompletionContext::CCC_DotMemberAccess,
+                            BaseType);
+  ResultBuilder Results(SemaRef, CodeCompleter->getAllocator(),
+                        CodeCompleter->getCodeCompletionTUInfo(), CCC,
+                        &ResultBuilder::IsOffsetofField);
+
+  Results.EnterNewScope();
+  CodeCompletionDeclConsumer Consumer(Results, RD, BaseType);
+  // LookupVisibleDecls traverses base classes (required for inherited fields)
+  // and dependent bases (best-effort for templates). Globals are skipped:
+  // offsetof designators name only members of the surrounding type.
+  SemaRef.LookupVisibleDecls(RD, Sema::LookupMemberName, Consumer,
+                             /*IncludeGlobalScope=*/false,
+                             /*IncludeDependentBases=*/true,
+                             CodeCompleter->loadExternal());
+  Results.ExitScope();
+
+  HandleCodeCompleteResults(&SemaRef, CodeCompleter,
+                            Results.getCompletionContext(), Results.data(),
+                            Results.size());
+}
+
 void SemaCodeCompletion::CodeCompleteInitializer(Scope *S, Decl *D) {
   ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D);
   if (!VD) {
diff --git a/clang/test/CodeCompletion/desig-init.cpp 
b/clang/test/CodeCompletion/desig-init.cpp
index ac250bc6d8bb3..d43f7123dfcd1 100644
--- a/clang/test/CodeCompletion/desig-init.cpp
+++ b/clang/test/CodeCompletion/desig-init.cpp
@@ -89,3 +89,37 @@ auto TestWithAnon = WithAnon { .inner = 2 };
   // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):33 %s -o - -std=c++2a | FileCheck 
-check-prefix=CHECK-CC5 %s
   // CHECK-CC5: COMPLETION: inner : [#int#]inner
   // CHECK-CC5: COMPLETION: outer : [#int#]outer
+
+// Field designator that traverses an anonymous struct: the IndirectFieldDecl
+// branch in the lookup callback is required to resolve `anon` here.
+struct WithAnonRecord {
+  struct Inner {
+    int leaf;
+    int other;
+  };
+  struct {
+    Inner anon;
+  };
+};
+auto TestWithAnonRecord = WithAnonRecord { .anon.leaf = 2 };
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):50 %s -o - -std=c++2a | FileCheck 
-check-prefix=CHECK-CC6 %s
+  // CHECK-CC6: COMPLETION: leaf : [#int#]leaf
+  // CHECK-CC6: COMPLETION: other : [#int#]other
+
+// Array element traversal: `Context.getAsArrayType` strips sugar so the
+// element type is reached cleanly.
+struct WithArrayField {
+  Base bases[2];
+};
+auto TestWithArrayField = WithArrayField { .bases[0].t = 2 };
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):54 %s -o - -std=c++2a | FileCheck 
-check-prefix=CHECK-CC8 %s
+  // CHECK-CC8: COMPLETION: t : [#int#]t
+
+// Reference-typed field: `getNonReferenceType()` on the resolved field type
+// lets the path continue into the referent's record.
+struct WithRefField {
+  Base &ref;
+};
+auto TestWithRefField = WithRefField { .ref.t = 2 };
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):45 %s -o - -std=c++2a | FileCheck 
-check-prefix=CHECK-CC7 %s
+  // CHECK-CC7: COMPLETION: t : [#int#]t
diff --git a/clang/test/CodeCompletion/offsetof.cpp 
b/clang/test/CodeCompletion/offsetof.cpp
new file mode 100644
index 0000000000000..e528f32d911b0
--- /dev/null
+++ b/clang/test/CodeCompletion/offsetof.cpp
@@ -0,0 +1,128 @@
+struct S {
+  int field;
+  int other;
+  void method();
+};
+
+struct Inner {
+  int leaf;
+  int otherLeaf;
+  void method();
+};
+
+struct Outer {
+  Inner inner;
+  Inner array[2];
+};
+
+struct Base {
+  int inherited;
+};
+
+struct Derived : Base {
+  int direct;
+};
+
+struct RefOuter {
+  Inner &ref;
+};
+
+struct WithAnon {
+  int outer;
+  union {
+    int anonInt;
+    Inner anonInner;
+  };
+};
+
+struct ShadowBase {
+  Inner shadowed;
+};
+
+struct ShadowDerived : ShadowBase {
+  using ShadowBase::shadowed;
+};
+
+struct WithBitField {
+  int regular;
+  int bit : 4;
+  int : 4;
+  int otherRegular;
+};
+
+struct WithAnonBitField {
+  int outer;
+  struct {
+    int anonRegular;
+    int anonBit : 4;
+  };
+};
+
+#define offsetof(type, member) __builtin_offsetof(type, member)
+
+// Cursor immediately after the comma: empty designator path.
+int empty = __builtin_offsetof(S, field);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):35 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-S --implicit-check-not=method %s
+
+// Cursor after a dot: completion in the nested record's type.
+int after_dot = __builtin_offsetof(Outer, inner.leaf);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):49 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-INNER --implicit-check-not=method %s
+
+// Cursor after a dot following an array subscript.
+int array = __builtin_offsetof(Outer, array[0].leaf);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):48 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-INNER --implicit-check-not=method %s
+
+// Inherited fields participate in offsetof completion.
+int inherited = __builtin_offsetof(Derived, inherited);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):45 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-DERIVED %s
+
+// Reference field: dereferenced before continuing the path.
+int ref = __builtin_offsetof(RefOuter, ref.leaf);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):44 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-INNER --implicit-check-not=method %s
+
+// Empty path on a record with anonymous-member indirect fields.
+int anon_empty = __builtin_offsetof(WithAnon, anonInt);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):47 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-ANON %s
+
+// Designator that starts with an indirect field from an anonymous member.
+int anon_nested = __builtin_offsetof(WithAnon, anonInner.leaf);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):58 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-INNER --implicit-check-not=method %s
+
+// Field exposed via `using Base::...` resolves through its UsingShadowDecl.
+int shadowed = __builtin_offsetof(ShadowDerived, shadowed.leaf);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):59 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-INNER --implicit-check-not=method %s
+
+// Macro form expands to __builtin_offsetof with the same completion behavior.
+int macro = offsetof(S, field);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):25 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-S --implicit-check-not=method %s
+
+// Bit-fields are not valid as the offsetof member designator
+// (err_offsetof_bitfield), so they should not be offered as completions.
+// Unnamed bit-fields have no identifier and are filtered out of lookup
+// independently.
+int bf = __builtin_offsetof(WithBitField, regular);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):43 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-BF --implicit-check-not=bit %s
+
+// Bit-fields exposed via an anonymous struct's IndirectFieldDecl are also
+// filtered: the leaf field is what offsetof would name.
+int abf = __builtin_offsetof(WithAnonBitField, outer);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns 
-code-completion-at=%s:%(line-1):48 %s -o - -std=c++17 | FileCheck 
-check-prefix=CHECK-ABF --implicit-check-not=anonBit %s
+
+// CHECK-S-DAG: COMPLETION: field : [#int#]field
+// CHECK-S-DAG: COMPLETION: other : [#int#]other
+
+// CHECK-INNER-DAG: COMPLETION: leaf : [#int#]leaf
+// CHECK-INNER-DAG: COMPLETION: otherLeaf : [#int#]otherLeaf
+
+// CHECK-DERIVED-DAG: COMPLETION: direct : [#int#]direct
+// CHECK-DERIVED-DAG: COMPLETION: inherited (InBase) : [#int#]inherited
+
+// CHECK-ANON-DAG: COMPLETION: anonInner : [#Inner#]anonInner
+// CHECK-ANON-DAG: COMPLETION: anonInt : [#int#]anonInt
+// CHECK-ANON-DAG: COMPLETION: outer : [#int#]outer
+
+// CHECK-BF-DAG: COMPLETION: regular : [#int#]regular
+// CHECK-BF-DAG: COMPLETION: otherRegular : [#int#]otherRegular
+
+// CHECK-ABF-DAG: COMPLETION: outer : [#int#]outer
+// CHECK-ABF-DAG: COMPLETION: anonRegular : [#int#]anonRegular

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to