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
