diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 2cefaa9611c98..2a631d0c9a9be 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1303,7 +1303,9 @@ class ObjCInterfaceTypeLoc : public ConcreteTypeLoc { @@ -1311,10 +1313,15 @@ class BoundsAttributedTypeLoc TypeLoc getInnerLoc() const { return getInnerTypeLoc(); } QualType getInnerType() const { return getTypePtr()->desugar(); } void initializeLocal(ASTContext &Context, SourceLocation Loc) { - // nothing to do + setAttrRange({Loc, Loc}); } - // LocalData is empty and TypeLocBuilder doesn't handle DataSize 1. - unsigned getLocalDataSize() const { return 0; } + void setAttrRange(SourceRange Range) { getLocalData()->Range = Range; } + SourceRange getAttrRange() const { return getLocalData()->Range; } + + StringRef getAttrNameAsWritten(const ASTContext &Ctx) const; + SourceRange getAttrNameRange(const ASTContext &Ctx) const; + + unsigned getLocalDataSize() const { return sizeof(BoundsAttributedLocInfo); } }; class CountAttributedTypeLoc final @@ -1325,8 +1332,6 @@ class CountAttributedTypeLoc final Expr *getCountExpr() const { return getTypePtr()->getCountExpr(); } bool isCountInBytes() const { return getTypePtr()->isCountInBytes(); } bool isOrNull() const { return getTypePtr()->isOrNull(); } - - SourceRange getLocalSourceRange() const; }; struct MacroQualifiedLocInfo { diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index f54ccf0932bc7..66f9e3c67df84 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -590,10 +590,6 @@ SourceRange AttributedTypeLoc::getLocalSourceRange() const { return getAttr() ? getAttr()->getRange() : SourceRange(); } -SourceRange CountAttributedTypeLoc::getLocalSourceRange() const { - return getCountExpr() ? getCountExpr()->getSourceRange() : SourceRange(); -} - SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const { return getAttr() ? getAttr()->getRange() : SourceRange(); } diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp index de9adf8ef5a1b..87b600209c751 100644 --- a/clang/lib/Sema/SemaBoundsSafety.cpp +++ b/clang/lib/Sema/SemaBoundsSafety.cpp @@ -11,9 +11,20 @@ /// (e.g. `counted_by`) /// //===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeBase.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Sema.h" +#include "llvm/ADT/StringSwitch.h" namespace clang { @@ -231,9 +242,73 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return false; } +// FIXME: for some reason diagnostics highlight the end character, while +// getSourceText() does not include the end character. +static SourceRange getAttrNameRangeImpl(const ASTContext &Ctx, + SourceLocation Begin, + bool IsForDiagnostics) { + const SourceManager &SM = Ctx.getSourceManager(); + SourceLocation TokenStart = Begin; + while (TokenStart.isMacroID()) + TokenStart = SM.getImmediateExpansionRange(TokenStart).getBegin(); + unsigned Offset = IsForDiagnostics ? 1 : 0; + SourceLocation End = + Lexer::getLocForEndOfToken(TokenStart, Offset, SM, Ctx.getLangOpts()); + return {TokenStart, End}; +} + +StringRef +BoundsAttributedTypeLoc::getAttrNameAsWritten(const ASTContext &Ctx) const { + SourceRange Range = + getAttrNameRangeImpl(Ctx, getAttrRange().getBegin(), false); + CharSourceRange NameRange = CharSourceRange::getCharRange(Range); + return Lexer::getSourceText(NameRange, Ctx.getSourceManager(), + Ctx.getLangOpts()); +} + +SourceRange +BoundsAttributedTypeLoc::getAttrNameRange(const ASTContext &Ctx) const { + return getAttrNameRangeImpl(Ctx, getAttrRange().getBegin(), true); +} + +static TypeSourceInfo *getTSI(const Decl *D) { + if (const auto *DD = dyn_cast(D)) { + return DD->getTypeSourceInfo(); + } + return nullptr; +} + +struct TypeLocFinder : public ConstStmtVisitor { + TypeLoc VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); } + + TypeLoc VisitDeclRefExpr(const DeclRefExpr *E) { + return getTSI(E->getDecl())->getTypeLoc(); + } + + TypeLoc VisitMemberExpr(const MemberExpr *E) { + return getTSI(E->getMemberDecl())->getTypeLoc(); + } + + TypeLoc VisitExplicitCastExpr(const ExplicitCastExpr *E) { + return E->getTypeInfoAsWritten()->getTypeLoc(); + } + + TypeLoc VisitCallExpr(const CallExpr *E) { + if (const auto *D = E->getCalleeDecl()) { + FunctionTypeLoc FTL = getTSI(D)->getTypeLoc().getAs(); + if (FTL.isNull()) { + return FTL; + } + return FTL.getReturnLoc(); + } + return {}; + } +}; + static void EmitIncompleteCountedByPointeeNotes(Sema &S, const CountAttributedType *CATy, - NamedDecl *IncompleteTyDecl) { + NamedDecl *IncompleteTyDecl, + TypeLoc TL) { assert(IncompleteTyDecl == nullptr || isa(IncompleteTyDecl)); if (IncompleteTyDecl) { @@ -253,20 +328,36 @@ static void EmitIncompleteCountedByPointeeNotes(Sema &S, << CATy->getPointeeType(); } - // Suggest using __sized_by(_or_null) instead of __counted_by(_or_null) as - // __sized_by(_or_null) doesn't have the complete type restriction. - // - // We use the source range of the expression on the CountAttributedType as an - // approximation for the source range of the attribute. This isn't quite right - // but isn't easy to fix right now. - // - // TODO: Implement logic to find the relevant TypeLoc for the attribute and - // get the SourceRange from that (#113582). - // - // TODO: We should emit a fix-it here. - SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange(); + CountAttributedTypeLoc CATL; + if (!TL.isNull()) + CATL = TL.getAs(); + + if (CATL.isNull()) { + // Fall back to pointing to the count expr - not great, but close enough. + // This should happen rarely, if ever. + S.Diag(CATy->getCountExpr()->getExprLoc(), + diag::note_counted_by_consider_using_sized_by) + << CATy->isOrNull(); + return; + } + SourceRange AttrSrcRange = CATL.getAttrNameRange(S.getASTContext()); + + StringRef Spelling = CATL.getAttrNameAsWritten(S.getASTContext()); + StringRef FixedSpelling = + llvm::StringSwitch(Spelling) + .Case("__counted_by", "__sized_by") + .Case("counted_by", "sized_by") + .Case("__counted_by__", "__sized_by__") + .Case("__counted_by_or_null", "__sized_by_or_null") + .Case("counted_by_or_null", "sized_by_or_null") + .Case("__counted_by_or_null__", "__sized_by_or_null__") + .Default(""); + FixItHint Fix; + if (!FixedSpelling.empty()) + Fix = FixItHint::CreateReplacement(AttrSrcRange, FixedSpelling); + S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by) - << CATy->isOrNull() << AttrSrcRange; + << CATy->isOrNull() << AttrSrcRange << Fix; } static std::tuple @@ -335,7 +426,11 @@ static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy( << CATy->getAttributeName(/*WithMacroPrefix=*/true) << PointeeTy << CATy->isOrNull() << RHSExpr->getSourceRange(); - EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl); + TypeLoc TL; + if (TypeSourceInfo *TSI = getTSI(Assignee)) + TL = TSI->getTypeLoc(); + + EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl, TL); return false; // check failed } @@ -408,7 +503,8 @@ bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) { << CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull() << E->getSourceRange(); - EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl); + TypeLoc TL = TypeLocFinder().Visit(E); + EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl, TL); return false; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index a9e7b44ac9d73..1753261822755 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -10,10 +10,12 @@ // //===----------------------------------------------------------------------===// +#include "TypeLocBuilder.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -24,6 +26,8 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/Mangle.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeBase.h" +#include "clang/AST/TypeLoc.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Cuda.h" #include "clang/Basic/DarwinSDKInfo.h" @@ -6580,9 +6584,14 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { if (S.CheckCountedByAttrOnField(FD, CountExpr, CountInBytes, OrNull)) return; + TypeLocBuilder TLB; QualType CAT = S.BuildCountAttributedArrayOrPointerType( FD->getType(), CountExpr, CountInBytes, OrNull); + TLB.pushFullCopy(FD->getTypeSourceInfo()->getTypeLoc()); + CountAttributedTypeLoc CATL = TLB.push(CAT); + CATL.setAttrRange(AL.getRange()); FD->setType(CAT); + FD->setTypeSourceInfo(TLB.getTypeSourceInfo(S.getASTContext(), CAT)); } static void handleFunctionReturnThunksAttr(Sema &S, Decl *D, diff --git a/clang/test/Sema/bounds-safety-source-ranges.c b/clang/test/Sema/bounds-safety-source-ranges.c new file mode 100644 index 0000000000000..45715816b32df --- /dev/null +++ b/clang/test/Sema/bounds-safety-source-ranges.c @@ -0,0 +1,197 @@ +// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-print-source-range-info -fdiagnostics-parseable-fixits %s 2>&1 | \ +// RUN: FileCheck %s --implicit-check-not error --implicit-check-not note --implicit-check-not fix --implicit-check-not warning + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct IncompleteTy; +typedef struct IncompleteTy Incomplete_t; + +struct Container__cb { + int count; + struct IncompleteTy* buf __counted_by(count); + Incomplete_t* buf_typedef __counted_by(count); +}; + +void consume_struct_IncompleteTy(struct IncompleteTy* buf); +int idx(void); + +void test_Container__cb(struct Container__cb* ptr) { + struct Container__cb uninit; + uninit.count = 0; + + uninit.buf = 0x0; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:14:{[[@LINE-1]]:16-[[@LINE-1]]:19}: error: cannot assign to 'Container__cb::buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0]; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:45:{[[@LINE-1]]:45-[[@LINE-1]]:53}: error: cannot use 'ptr->buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()]; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:44:{[[@LINE-1]]:44-[[@LINE-1]]:52}: error: cannot use 'ptr->buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + consume_struct_IncompleteTy(uninit.buf); + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:31:{[[@LINE-1]]:31-[[@LINE-1]]:41}: error: cannot use 'uninit.buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + consume_struct_IncompleteTy(ptr->buf); + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:31:{[[@LINE-1]]:31-[[@LINE-1]]:39}: error: cannot use 'ptr->buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + uninit.buf[0] = uninit.buf[1]; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:3:{[[@LINE-1]]:3-[[@LINE-1]]:13}: error: cannot use 'uninit.buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + // CHECK: bounds-safety-source-ranges.c:[[@LINE-6]]:19:{[[@LINE-6]]:19-[[@LINE-6]]:29}: error: cannot use 'uninit.buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + ptr->buf[0] = ptr->buf[1]; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:3:{[[@LINE-1]]:3-[[@LINE-1]]:11}: error: cannot use 'ptr->buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" + + // CHECK: bounds-safety-source-ranges.c:[[@LINE-6]]:17:{[[@LINE-6]]:17-[[@LINE-6]]:25}: error: cannot use 'ptr->buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:11:28:{11:28-11:40}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{11:28-11:40}:"__sized_by" +} + + +struct Container__cb___nomacro { + int count; + struct IncompleteTy* buf __attribute__((__counted_by__(count))); +}; + +void test_Container__cb___nomacro(struct Container__cb___nomacro* ptr) { + struct Container__cb___nomacro local; + local.count = 0; + local.buf = 0x0; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:13:{[[@LINE-1]]:15-[[@LINE-1]]:18}: error: cannot assign to 'Container__cb___nomacro::buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:[[@LINE-9]]:43:{[[@LINE-9]]:43-[[@LINE-9]]:57}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{[[@LINE-10]]:43-[[@LINE-10]]:57}:"__sized_by__" +} + + +struct Containercb_nomacro { + int count; + struct IncompleteTy* buf __attribute__((counted_by(count))); +}; + +void test_Containercb_nomacro(struct Containercb_nomacro* ptr) { + struct Containercb_nomacro local; + local.count = 0; + local.buf = 0x0; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:13:{[[@LINE-1]]:15-[[@LINE-1]]:18}: error: cannot assign to 'Containercb_nomacro::buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:[[@LINE-9]]:43:{[[@LINE-9]]:43-[[@LINE-9]]:53}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{[[@LINE-10]]:43-[[@LINE-10]]:53}:"sized_by" +} + + +#define aaaaaaaa(x) __attribute__((counted_by(x))) +struct Container_aaaaaaaa { + int count; + struct IncompleteTy* buf aaaaaaaa(count); +}; + +void test_Container_aaaaaaaa(struct Container_aaaaaaaa* ptr) { + struct Container_aaaaaaaa local; + local.count = 0; + local.buf = 0x0; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:13:{[[@LINE-1]]:15-[[@LINE-1]]:18}: error: cannot assign to 'Container_aaaaaaaa::buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:[[@LINE-9]]:28:{[[@LINE-9]]:28-[[@LINE-9]]:36}: note: consider using '__sized_by' instead of '__counted_by' + // no fix-it +} + + +struct Container_macro1 { + int count; + struct IncompleteTy* buf __attribute__((counted_by(count))); +}; + +#define A() \ +void test_Contain_macro1(struct Container_macro1* ptr) { \ + struct Container_macro1 local; \ + local.count = 0; \ + local.buf = 0x0; \ +} + +A() + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:1:{[[@LINE-1]]:1-[[@LINE-1]]:4}: error: cannot assign to 'Container_macro1::buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:[[@LINE-5]]:13:{[[@LINE-5]]:15-[[@LINE-5]]:18}: note: expanded from macro 'A' + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:[[@LINE-14]]:43:{[[@LINE-14]]:43-[[@LINE-14]]:53}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{[[@LINE-15]]:43-[[@LINE-15]]:53}:"sized_by" + + +#define B() \ +struct Container_macro2 { \ + int count; \ + struct IncompleteTy* buf __attribute__((counted_by(count))); \ +}; +B() + +void test_Contain_macro2(struct Container_macro2* ptr) { + struct Container_macro2 local; + local.count = 0; + local.buf = 0x0; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:13:{[[@LINE-1]]:15-[[@LINE-1]]:18}: error: cannot assign to 'Container_macro2::buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:[[@LINE-8]]:1:{[[@LINE-8]]:1-[[@LINE-8]]:2}: note: consider using '__sized_by' instead of '__counted_by' + // no fix-it +} + + +#define counted_by(x) __attribute__((counted_by(x))) + +struct Containercb { + int count; + struct IncompleteTy* buf counted_by(count); +}; + +void test_Containercb(struct Containercb* ptr) { + struct Containercb local; + local.count = 0; + local.buf = 0x0; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:13:{[[@LINE-1]]:15-[[@LINE-1]]:18}: error: cannot assign to 'Containercb::buf' with '__counted_by' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:[[@LINE-9]]:28:{[[@LINE-9]]:28-[[@LINE-9]]:38}: note: consider using '__sized_by' instead of '__counted_by' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{[[@LINE-10]]:28-[[@LINE-10]]:38}:"sized_by" +} + + +struct Containercbon { + int count; + struct IncompleteTy* buf __attribute__((counted_by_or_null(count))); +}; + +void test_Containercbon(struct Containercbon* ptr) { + struct Containercbon local; + local.count = 0; + local.buf = 0x0; + // CHECK: bounds-safety-source-ranges.c:[[@LINE-1]]:13:{[[@LINE-1]]:15-[[@LINE-1]]:18}: error: cannot assign to 'Containercbon::buf' with '__counted_by_or_null' {{.*}} because the pointee type 'struct IncompleteTy' is incomplete + // CHECK: bounds-safety-source-ranges.c:6:1: note: consider providing a complete definition for 'struct IncompleteTy' + // CHECK: bounds-safety-source-ranges.c:[[@LINE-9]]:43:{[[@LINE-9]]:43-[[@LINE-9]]:61}: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + // CHECK: fix-it:"{{.*}}bounds-safety-source-ranges.c":{[[@LINE-10]]:43-[[@LINE-10]]:61}:"sized_by_or_null" +} + +// prevent 'error' from being unmatched +// CHECK: errors generated