[clang] [BoundsSafety][Sema] Allow counted_by and counted_by_or_null on pointers where the pointee type is incomplete but potentially completable (PR #106321)

Dan Liew via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 28 01:11:19 PDT 2024


https://github.com/delcypher updated https://github.com/llvm/llvm-project/pull/106321

>From cb71579988e3bd5d2182310b943e7705f8e04454 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Thu, 15 Aug 2024 16:33:04 -0700
Subject: [PATCH] [BoundsSafety][Sema] Allow counted_by and counted_by_or_null
 on pointers where the pointee type is incomplete but potentially completable

Previously using the `counted_by` or `counted_by_or_null` attribute on a
pointer with an incomplete pointee type was forbidden. Unfortunately
this prevented a situation like the following from being allowed.

Header file:

```
struct EltTy; // Incomplete type
struct Buffer {
  size_t count;
  struct EltTy* __counted_by(count) buffer; // error before this patch
};
```

Implementation file:

```
struct EltTy {
  // definition
};

void allocBuffer(struct Buffer* b) {
  b->buffer = malloc(sizeof(EltTy)* b->count);
}
```

To allow code like the above but still enforce that the pointee
type size is known in locations where `-fbounds-safety` needs to
emit bounds checks the following scheme is used.

* For incomplete pointee types that can never be completed (e.g. `void`)
  these are treated as error where the attribute is written (just like
  before this patch).
* For incomplete pointee types that might be completable later on
  (struct, union, and enum forward declarations)
  in the translation unit, writing the attribute on the incomplete
  pointee type is allowed on the FieldDecl declaration but "uses" of the
  declared pointer are forbidden if at the point of "use" the pointee
  type is still incomplete.

For this patch a "use" of a FieldDecl covers:

* Explicit and Implicit initialization (note see **Tentative Definition
  Initialization** for an exception to this)
* Assignment
* Conversion to an lvalue (e.g. for use in an expression)

In Apple's internal fork of Clang the `counted_by` and
`counted_by_or_null` attribute are allowed in many more contexts. That
isn't the case for upstream Clang so the "use" checks for the attribute
on VarDecl, ParamVarDecl, and function return type have been omitted
from this patch because they can't be tested. However, the
`BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy` and
`BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy` functions
retain the ability to emit diagnostics for these other contexts to avoid
unnecessary divergence between upstream Clang and Apple's internal fork.
Support for checking "uses" will be upstreamed when upstream Clang
allows the `counted_by` and `counted_by_or_null` attribute in additional
contexts.

This patch also includes the new `Sema::BoundsSafetySourceRangeFor`
method which allows a more accurate SourceRange for the `counted_by`
and `counted_by_or_null` attributes to be shown in diagnostics. This
is used by the new `diag::note_named_attribute` diagnostic introduced in
this patch. This method has several shortcomings but it didn't seem
worth covering them given this is essentially a workaround for the
fact `CountAttributedType` doesn't store the SourceLocation for its
attribute.

This patch has a few limitations:

** 1. Tentative Defition Initialization **

This patch currently allows something like:

```
struct IncompleteTy;
struct Buffer {
  int count;
  struct IncompleteTy* __counted_by(count) buf;
};

// Tentative definition
struct Buffer GlobalBuf;
```

The Tentative definition in this example becomes an actual definition
whose initialization **should be checked** but it currently isn't.
Addressing this problem will be done in a subseqent patch.

** 2. When the incomplete pointee type is a typedef diagnostics are slightly misleading **

For this situation:

```

struct IncompleteTy;
typedef struct IncompleteTy Incomplete_t;

struct Buffer {
  int count;
  struct IncompleteTy* __counted_by(count) buf;
};

void use(struct Buffer b) {
  b.buf = 0x0;
}
```

This code emits `note: forward declaration of 'Incomplete_t' (aka
'struct IncompleteTy')` but the location is on the `struct
IncompleteTy;` forward declaration.  This is misleading because
`Incomplete_t` isn't actually forward declared there (instead the
underlying type is). This could be resolved by additional diagnostics
that walk the chain of typedefs and explain each step of the walk.
However, that would be very verbose and didn't seem like a direction
worth pursuing.

rdar://133600117
---
 clang/include/clang/AST/Type.h                |  22 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  37 ++
 clang/include/clang/Sema/Sema.h               |  77 ++-
 clang/lib/AST/Type.cpp                        |  37 ++
 clang/lib/Sema/SemaBoundsSafety.cpp           | 400 +++++++++++-
 clang/lib/Sema/SemaExpr.cpp                   |  24 +
 clang/lib/Sema/SemaInit.cpp                   |  17 +
 .../attr-counted-by-late-parsed-struct-ptrs.c |   5 +-
 .../Sema/attr-counted-by-or-null-last-field.c |   4 +-
 ...unted-by-or-null-late-parsed-struct-ptrs.c |   5 +-
 .../attr-counted-by-or-null-struct-ptrs.c     |  12 +-
 ...ruct-ptrs-completable-incomplete-pointee.c | 584 ++++++++++++++++++
 clang/test/Sema/attr-counted-by-struct-ptrs.c |  12 +-
 clang/test/Sema/attr-counted-by-vla.c         |   3 +-
 clang/unittests/Sema/CMakeLists.txt           |   1 +
 .../Sema/GetCountedByAttrSourceRange.cpp      | 573 +++++++++++++++++
 16 files changed, 1773 insertions(+), 40 deletions(-)
 create mode 100644 clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
 create mode 100644 clang/unittests/Sema/GetCountedByAttrSourceRange.cpp

diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 575f3c17a3f691..3a6e87247a68f8 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2440,6 +2440,26 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     return !isFunctionType();
   }
 
+  /// \returns True if the type is incomplete and it is also a type that
+  /// cannot be completed by a later type definition.
+  ///
+  /// E.g. For `void` this is true but for `struct ForwardDecl;` this is false
+  /// because a definition for `ForwardDecl` could be provided later on in the
+  /// translation unit.
+  ///
+  /// Note even for types that this function returns true for it is still
+  /// possible for the declarations that contain this type to later have a
+  /// complete type in a translation unit. E.g.:
+  ///
+  /// \code{.c}
+  /// // This decl has type 'char[]' which is incomplete and cannot be later
+  /// // completed by another by another type declaration.
+  /// extern char foo[];
+  /// // This decl how has complete type 'char[5]'.
+  /// char foo[5]; // foo has a complete type
+  /// \endcode
+  bool isIncompletableIncompleteType() const;
+
   /// Determine whether this type is an object type.
   bool isObjectType() const {
     // C++ [basic.types]p8:
@@ -3343,6 +3363,8 @@ class CountAttributedType final
   static bool classof(const Type *T) {
     return T->getTypeClass() == CountAttributed;
   }
+
+  StringRef GetAttributeName(bool WithMacroPrefix) const;
 };
 
 /// Represents a type which was implicitly adjusted by the semantic
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index edf22b909c4d57..64d73b5b766e4a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4376,6 +4376,7 @@ def err_mismatched_visibility: Error<"visibility does not match previous declara
 def note_previous_attribute : Note<"previous attribute is here">;
 def note_conflicting_attribute : Note<"conflicting attribute is here">;
 def note_attribute : Note<"attribute is here">;
+def note_named_attribute : Note<"%0 attribute is here">;
 def err_mismatched_ms_inheritance : Error<
   "inheritance model does not match %select{definition|previous declaration}0">;
 def warn_ignored_ms_inheritance : Warning<
@@ -6628,6 +6629,42 @@ def err_counted_by_attr_pointee_unknown_size : Error<
     "%select{|. This will be an error in a future compiler version}3"
     ""
   "}2">;
+def err_counted_by_on_incomplete_type_on_assign : Error <
+  "cannot %select{"
+    "assign to %select{object|'%1'}2 that has|" // AA_Assigning,
+    "pass argument to %select{parameter|parameter '%1'}2 that has|" // AA_Passing,
+    "return|" // AA_Returning,
+    "convert to|" // AA_Converting (UNUSED)
+    "%select{|implicitly }3initialize %select{object|'%1'}2 that has|" // AA_Initializing,
+    "pass argument to parameter that has|" // AA_Sending (UNUSED)
+    "cast to|" // AA_Casting (UNUSED)
+    "pass argument to parameter that has" // AA_Passing_CFAudited (UNUSED)
+  "}0 type %4 because the pointee type %6 is incomplete and the '%5' attribute "
+  "requires the pointee type be complete when %select{"
+    "assigning|" // AA_Assigning,
+    "passing|" // AA_Passing,
+    "returning|" // AA_Returning,
+    "converting|" // AA_Converting (UNUSED)
+    "initializing|" // AA_Initializing,
+    "passing|" // AA_Sending (UNUSED)
+    "casting|" // AA_Casting (UNUSED)
+    "passing" // AA_Passing_CFAudited (UNUSED)
+  "}0; "
+  "consider providing a complete definition for %6 or using the "
+  "'__sized_by%select{|_or_null}7' attribute"
+>;
+def err_counted_by_on_incomplete_type_on_use : Error <
+  "cannot %select{"
+    "use '%1'|" // Generic expr
+    "call '%1'" // CallExpr
+  "}0 with%select{"
+    "|"       // Generic expr
+    " return" // CallExpr
+  "}0 type %2 because the pointee type %3 is incomplete "
+  "and the '%4' attribute requires the pointee type be complete in this context; "
+  "consider providing a complete definition for %3 or using the "
+  "'__sized_by%select{|_or_null}5' attribute"
+>;
 
 def warn_counted_by_attr_elt_type_unknown_size :
   Warning<err_counted_by_attr_pointee_unknown_size.Summary>,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1f7e555d1b8717..56c0efaa71645c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2099,6 +2099,70 @@ class Sema final : public SemaBase {
   bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
                                  bool OrNull);
 
+  // AssignmentAction - This is used by all the assignment diagnostic functions
+  // to represent what is actually causing the operation
+  enum AssignmentAction {
+    AA_Assigning,
+    AA_Passing,
+    AA_Returning,
+    AA_Converting,
+    AA_Initializing,
+    AA_Sending,
+    AA_Casting,
+    AA_Passing_CFAudited
+  };
+
+  /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
+  /// `__counted_by_or_null` pointer type \param LHSTy.
+  ///
+  /// \param LHSTy The type being assigned to. Checks will only be performed if
+  ///              the type is a `counted_by` or `counted_by_or_null ` pointer.
+  /// \param RHSExpr The expression being assigned from.
+  /// \param Action The type assignment being performed
+  /// \param Loc The SourceLocation to use for error diagnostics
+  /// \param ComputeAssignee If provided this function will be called before
+  ///        emitting a diagnostic. The function should return the name of
+  ///        entity being assigned to or an empty string if this cannot be
+  ///        determined.
+  ///
+  /// \returns True iff no diagnostic where emitted, false otherwise.
+  bool BoundsSafetyCheckAssignmentToCountAttrPtr(
+      QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action,
+      SourceLocation Loc,
+      std::function<std::string()> ComputeAssignee = nullptr);
+
+  /// Perform Bounds Safety Semantic checks for initializing a Bounds Safety
+  /// pointer.
+  ///
+  /// \param Entity The entity being initialized
+  /// \param Kind The kind of initialization being performed
+  /// \param Action The type assignment being performed
+  /// \param LHSTy The type being assigned to. Checks will only be performed if
+  ///              the type is a `counted_by` or `counted_by_or_null ` pointer.
+  /// \param RHSExpr The expression being used for initialization.
+  ///
+  /// \returns True iff no diagnostic where emitted, false otherwise.
+  bool BoundsSafetyCheckInitialization(const InitializedEntity &Entity,
+                                       const InitializationKind &Kind,
+                                       Sema::AssignmentAction Action,
+                                       QualType LHSType, Expr *RHSExpr);
+
+  /// Perform Bounds Safety semantic checks for uses of invalid uses counted_by
+  /// or counted_by_or_null pointers in \param E.
+  ///
+  /// \param E the expression to check
+  ///
+  /// \returns True iff no diagnostic where emitted, false otherwise.
+  bool BoundsSafetyCheckUseOfCountAttrPtr(Expr *E);
+
+  /// \returns A SourceRange for \param CATy.
+  ///
+  /// This method tries to compute the most useful SourceRange for diagnostics.
+  /// If this fails the returned SourceRange will be the same as
+  /// `CATy->getCountExpr()->getSourceRange()`.
+  ///
+  SourceRange BoundsSafetySourceRangeFor(const CountAttributedType *CATy);
+
   ///@}
 
   //
@@ -6490,19 +6554,6 @@ class Sema final : public SemaBase {
   /// cleanup that are created by the current full expression.
   SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
 
-  // AssignmentAction - This is used by all the assignment diagnostic functions
-  // to represent what is actually causing the operation
-  enum AssignmentAction {
-    AA_Assigning,
-    AA_Passing,
-    AA_Returning,
-    AA_Converting,
-    AA_Initializing,
-    AA_Sending,
-    AA_Casting,
-    AA_Passing_CFAudited
-  };
-
   /// Determine whether the use of this declaration is valid, without
   /// emitting diagnostics.
   bool CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid);
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index e89ce2e4b38445..137609743fc02c 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2438,6 +2438,22 @@ bool Type::isIncompleteType(NamedDecl **Def) const {
   }
 }
 
+bool Type::isIncompletableIncompleteType() const {
+  if (!isIncompleteType())
+    return false;
+
+  // Forward declarations of structs, classes, enums, and unions could be later
+  // completed in a compilation unit by providing a type definition.
+  if (isStructureOrClassType() || isEnumeralType() || isUnionType())
+    return false;
+
+  // Other types are incompletable.
+  //
+  // E.g. `char[]` and `void`. The type is incomplete and no future
+  // type declarations can make the type complete.
+  return true;
+}
+
 bool Type::isSizelessBuiltinType() const {
   if (isSizelessVectorType())
     return true;
@@ -3862,6 +3878,27 @@ CountAttributedType::CountAttributedType(
     DeclSlot[i] = CoupledDecls[i];
 }
 
+StringRef CountAttributedType::GetAttributeName(bool WithMacroPrefix) const {
+#define ENUMERATE_ATTRS(PREFIX)                                                \
+  do {                                                                         \
+    if (isCountInBytes()) {                                                    \
+      if (isOrNull())                                                          \
+        return PREFIX "sized_by_or_null";                                      \
+      return PREFIX "sized_by";                                                \
+    }                                                                          \
+    if (isOrNull())                                                            \
+      return PREFIX "counted_by_or_null";                                      \
+    return PREFIX "counted_by";                                                \
+  } while (0)
+
+  if (WithMacroPrefix)
+    ENUMERATE_ATTRS("__");
+  else
+    ENUMERATE_ATTRS("");
+
+#undef ENUMERATE_ATTRS
+}
+
 TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D,
                          QualType Underlying, QualType can)
     : Type(tc, can, toSemanticDependence(can->getDependence())),
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp
index d63a2389ea11de..fa72553317a810 100644
--- a/clang/lib/Sema/SemaBoundsSafety.cpp
+++ b/clang/lib/Sema/SemaBoundsSafety.cpp
@@ -11,6 +11,9 @@
 /// (e.g. `counted_by`)
 ///
 //===----------------------------------------------------------------------===//
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Sema/Initialization.h"
 #include "clang/Sema/Sema.h"
 
 namespace clang {
@@ -102,7 +105,36 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
   // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
   // when `FieldTy->isArrayType()`.
   bool ShouldWarn = false;
-  if (PointeeTy->isIncompleteType() && !CountInBytes) {
+  if (PointeeTy->isIncompletableIncompleteType() && !CountInBytes) {
+    // In general using `counted_by` or `counted_by_or_null` on
+    // pointers where the pointee is an incomplete type are problematic. This is
+    // because it isn't possible to compute the pointer's bounds without knowing
+    // the pointee type size. At the same time it is common to forward declare
+    // types in header files.
+    //
+    // E.g.:
+    //
+    // struct Handle;
+    // struct Wrapper {
+    //   size_t size;
+    //   struct Handle* __counted_by(count) handles;
+    // }
+    //
+    // To allow the above code pattern but still prevent the pointee type from
+    // being incomplete in places where bounds checks are needed the following
+    // scheme is used:
+    //
+    // * When the pointee type is a "completable incomplete" type (i.e.
+    // a type that is currently incomplete but might be completed later
+    // on in the translation unit) the attribute is allowed by this method
+    // but later uses of the FieldDecl are checked that the pointee type
+    // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`,
+    // `BoundsSafetyCheckInitialization`, and
+    // `BoundsSafetyCheckUseOfCountAttrPtr`
+    //
+    // * When the pointee type is a "incompletable incomplete" type (e.g.
+    // `void`) the attribute is disallowed by this method because we know the
+    // type can never be completed so there's no reason to allow it.
     InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
   } else if (PointeeTy->isSizelessType()) {
     InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
@@ -186,4 +218,370 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
   return false;
 }
 
+SourceRange Sema::BoundsSafetySourceRangeFor(const CountAttributedType *CATy) {
+  // Note: This implementation relies on `CountAttributedType` being unique.
+  // E.g.:
+  //
+  // struct Foo {
+  //   int count;
+  //   char* __counted_by(count) buffer;
+  //   char* __counted_by(count) buffer2;
+  // };
+  //
+  // The types of `buffer` and `buffer2` are unique. The types being
+  // unique means the SourceLocation of the `counted_by` expression can be used
+  // to find where the attribute was written.
+
+  auto Fallback = CATy->getCountExpr()->getSourceRange();
+  auto CountExprBegin = CATy->getCountExpr()->getBeginLoc();
+
+  // FIXME: We currently don't support the count expression being a macro
+  // itself. E.g.:
+  //
+  // #define ZERO 0
+  // int* __counted_by(ZERO) x;
+  //
+  if (SourceMgr.isMacroBodyExpansion(CountExprBegin))
+    return Fallback;
+
+  auto FetchIdentifierTokenFromOffset =
+      [&](ssize_t Offset) -> std::optional<Token> {
+    SourceLocation OffsetLoc = CountExprBegin.getLocWithOffset(Offset);
+    Token Result;
+    if (Lexer::getRawToken(OffsetLoc, Result, SourceMgr, getLangOpts()))
+      return std::optional<Token>(); // Failed
+
+    if (!Result.isAnyIdentifier())
+      return std::optional<Token>(); // Failed
+
+    return Result; // Success
+  };
+
+  auto CountExprEnd = CATy->getCountExpr()->getEndLoc();
+  auto FindRParenTokenAfter = [&]() -> std::optional<Token> {
+    auto CountExprEndSpelling = SourceMgr.getSpellingLoc(CountExprEnd);
+    auto MaybeRParenTok = Lexer::findNextToken(
+        CountExprEndSpelling, getSourceManager(), getLangOpts());
+
+    if (!MaybeRParenTok.has_value())
+      return std::nullopt;
+
+    if (!MaybeRParenTok->is(tok::r_paren))
+      return std::nullopt;
+
+    return *MaybeRParenTok;
+  };
+
+  // Step back two characters to point at the last character of the attribute
+  // text.
+  //
+  // __counted_by(count)
+  //            ^ ^
+  //            | |
+  //            ---
+  auto MaybeLastAttrCharToken = FetchIdentifierTokenFromOffset(-2);
+  if (!MaybeLastAttrCharToken)
+    return Fallback;
+
+  auto LastAttrCharToken = MaybeLastAttrCharToken.value();
+
+  if (LastAttrCharToken.getLength() > 1) {
+    // Special case: When the character is part of a macro the Token we get
+    // is the whole macro name (e.g. `__counted_by`).
+    if (LastAttrCharToken.getRawIdentifier() !=
+        CATy->GetAttributeName(/*WithMacroPrefix=*/true))
+      return Fallback;
+
+    // Found the beginning of the `__counted_by` macro
+    SourceLocation Begin = LastAttrCharToken.getLocation();
+    // Now try to find the closing `)` of the macro.
+    auto MaybeRParenTok = FindRParenTokenAfter();
+    if (!MaybeRParenTok.has_value())
+      return Fallback;
+
+    return SourceRange(Begin, MaybeRParenTok->getLocation());
+  }
+
+  assert(LastAttrCharToken.getLength() == 1);
+  // The Token we got back is just the last character of the identifier.
+  // This means a macro is not being used and instead the attribute is being
+  // used directly. We need to find the beginning of the identifier. We support
+  // two cases:
+  //
+  // * Non-affixed version. E.g: `counted_by`
+  // * Affixed version. E.g.: `__counted_by__`
+
+  // Try non-affixed version. E.g.:
+  //
+  // __attribute__((counted_by(count)))
+  //                ^          ^
+  //                |          |
+  //                ------------
+
+  // +1 is for `(`
+  const ssize_t NonAffixedSkipCount =
+      CATy->GetAttributeName(/*WithMacroPrefix=*/false).size() + 1;
+  auto MaybeNonAffixedBeginToken =
+      FetchIdentifierTokenFromOffset(-NonAffixedSkipCount);
+  if (!MaybeNonAffixedBeginToken)
+    return Fallback;
+
+  auto NonAffixedBeginToken = MaybeNonAffixedBeginToken.value();
+  if (NonAffixedBeginToken.getRawIdentifier() ==
+      CATy->GetAttributeName(/*WithMacroPrefix=*/false)) {
+    // Found the beginning of the `counted_by`-like attribute
+    auto SL = NonAffixedBeginToken.getLocation();
+
+    // Now try to find the closing `)` of the attribute
+    auto MaybeRParenTok = FindRParenTokenAfter();
+    if (!MaybeRParenTok.has_value())
+      return Fallback;
+
+    return SourceRange(SL, MaybeRParenTok->getLocation());
+  }
+
+  // Try affixed version. E.g.:
+  //
+  // __attribute__((__counted_by__(count)))
+  //                ^              ^
+  //                |              |
+  //                ----------------
+  std::string AffixedTokenStr =
+      (llvm::Twine("__") + CATy->GetAttributeName(/*WithMacroPrefix=*/false) +
+       llvm::Twine("__"))
+          .str();
+  // +1 is for `(`
+  // +4 is for the 4 `_` characters
+  const ssize_t AffixedSkipCount =
+      CATy->GetAttributeName(/*WithMacroPrefix=*/false).size() + 1 + 4;
+  auto MaybeAffixedBeginToken =
+      FetchIdentifierTokenFromOffset(-AffixedSkipCount);
+  if (!MaybeAffixedBeginToken)
+    return Fallback;
+
+  auto AffixedBeginToken = MaybeAffixedBeginToken.value();
+  if (AffixedBeginToken.getRawIdentifier() != AffixedTokenStr)
+    return Fallback;
+
+  // Found the beginning of the `__counted_by__`-like like attribute.
+  auto SL = AffixedBeginToken.getLocation();
+  // Now try to find the closing `)` of the attribute
+  auto MaybeRParenTok = FindRParenTokenAfter();
+  if (!MaybeRParenTok.has_value())
+    return Fallback;
+
+  return SourceRange(SL, MaybeRParenTok->getLocation());
+}
+
+static void EmitIncompleteCountedByPointeeNotes(Sema &S,
+                                                const CountAttributedType *CATy,
+                                                NamedDecl *IncompleteTyDecl,
+                                                bool NoteAttrLocation = true) {
+  assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));
+
+  if (NoteAttrLocation) {
+    // Note where the attribute is declared
+    auto AttrSrcRange = S.BoundsSafetySourceRangeFor(CATy);
+    S.Diag(AttrSrcRange.getBegin(), diag::note_named_attribute)
+        << CATy->GetAttributeName(/*WithMacroPrefix=*/true) << AttrSrcRange;
+  }
+
+  if (!IncompleteTyDecl)
+    return;
+
+  // If there's an associated forward declaration display it to emphasize
+  // why the type is incomplete (all we have is a forward declaration).
+
+  // Note the `IncompleteTyDecl` type is the underlying type which might not
+  // be the same as `CATy->getPointeeType()` which could be a typedef.
+  //
+  // The diagnostic printed will be at the location of the underlying type but
+  // the diagnostic text will print the type of `CATy->getPointeeType()` which
+  // could be a typedef name rather than the underlying type. This is ok
+  // though because the diagnostic will print the underlying type name too.
+  // E.g:
+  //
+  // `forward declaration of 'Incomplete_Struct_t'
+  //  (aka 'struct IncompleteStructTy')`
+  //
+  // If this ends up being confusing we could emit a second diagnostic (one
+  // explaining where the typedef is) but that seems overly verbose.
+
+  S.Diag(IncompleteTyDecl->getBeginLoc(), diag::note_forward_declaration)
+      << CATy->getPointeeType();
+}
+
+static bool
+HasCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND,
+                                    const CountAttributedType **CATyOut,
+                                    QualType *PointeeTyOut) {
+  auto *CATy = Ty->getAs<CountAttributedType>();
+  if (!CATy)
+    return false;
+
+  // Incomplete pointee type is only a problem for
+  // counted_by/counted_by_or_null
+  if (CATy->isCountInBytes())
+    return false;
+
+  auto PointeeTy = CATy->getPointeeType();
+  if (PointeeTy.isNull())
+    return false; // Reachable?
+
+  if (!PointeeTy->isIncompleteType(ND))
+    return false;
+
+  if (CATyOut)
+    *CATyOut = CATy;
+  if (PointeeTyOut)
+    *PointeeTyOut = PointeeTy;
+  return true;
+}
+
+/// Perform Checks for assigning to a `__counted_by` or
+/// `__counted_by_or_null` pointer type \param LHSTy where the pointee type
+/// is incomplete which is invalid.
+///
+/// \param S The Sema instance.
+/// \param LHSTy The type being assigned to. Checks will only be performed if
+///              the type is a `counted_by` or `counted_by_or_null ` pointer.
+/// \param RHSExpr The expression being assigned from.
+/// \param Action The type assignment being performed
+/// \param Loc The SourceLocation to use for error diagnostics
+/// \param ComputeAssignee If provided this function will be called before
+///        emitting a diagnostic. The function should return the name of
+///        entity being assigned to or an empty string if this cannot be
+///        determined.
+///
+/// \returns True iff no diagnostic where emitted, false otherwise.
+static bool BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
+    Sema &S, QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action,
+    SourceLocation Loc, std::function<std::string()> ComputeAssignee) {
+  NamedDecl *IncompleteTyDecl = nullptr;
+  const CountAttributedType *CATy = nullptr;
+  QualType PointeeTy;
+  if (!HasCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl, &CATy,
+                                           &PointeeTy))
+    return true;
+  assert(CATy && !CATy->isCountInBytes() && !PointeeTy.isNull());
+
+  // It's not expected that the diagnostic be emitted in these cases.
+  // It's not necessarily a problem but we should catch when this starts
+  // to happen.
+  assert(Action != Sema::AA_Converting && Action != Sema::AA_Sending &&
+         Action != Sema::AA_Casting && Action != Sema::AA_Passing_CFAudited);
+
+  // By having users provide a function we only pay the cost of allocation the
+  // string and computing when a diagnostic is emitted.
+  std::string Assignee = ComputeAssignee ? ComputeAssignee() : "";
+  {
+    auto D = S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign)
+             << /*0*/ (int)Action << /*1*/ Assignee
+             << /*2*/ (Assignee.size() > 0)
+             << /*3*/ isa<ImplicitValueInitExpr>(RHSExpr) << /*4*/ LHSTy
+             << /*5*/ CATy->GetAttributeName(/*WithMacroPrefix=*/true)
+             << /*6*/ PointeeTy << /*7*/ CATy->isOrNull();
+
+    if (RHSExpr->getSourceRange().isValid())
+      D << RHSExpr->getSourceRange();
+  }
+
+  EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);
+  return false; // check failed
+}
+
+bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr(
+    QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action,
+    SourceLocation Loc, std::function<std::string()> ComputeAssignee) {
+  return BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
+      *this, LHSTy, RHSExpr, Action, Loc, ComputeAssignee);
+}
+
+bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity,
+                                           const InitializationKind &Kind,
+                                           Sema::AssignmentAction Action,
+                                           QualType LHSType, Expr *RHSExpr) {
+  bool ChecksPassed = true;
+  auto SL = Kind.getLocation();
+
+  // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here
+  // because we need conditionalize what is checked. In downstream
+  // Clang `counted_by` is supported on variable definitions and in that
+  // implementation an error diagnostic will be emitted on the variable
+  // definition if the pointee is an incomplete type. To avoid warning about the
+  // same problem twice (once when the variable is defined, once when Sema
+  // checks the initializer) we skip checking the initializer if it's a
+  // variable.
+  if (Action == Sema::AA_Initializing &&
+      Entity.getKind() != InitializedEntity::EK_Variable) {
+
+    if (!BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
+            *this, LHSType, RHSExpr, Action, SL, [&Entity]() -> std::string {
+              if (const auto *VD =
+                      dyn_cast_or_null<ValueDecl>(Entity.getDecl())) {
+                return VD->getQualifiedNameAsString();
+              }
+              return "";
+            })) {
+      ChecksPassed = false;
+
+      // It's not necessarily bad if this assert fails but we should catch
+      // if this happens.
+      assert(Entity.getKind() == InitializedEntity::EK_Member);
+    }
+  }
+
+  return ChecksPassed;
+}
+
+static bool BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy(Sema &S,
+                                                                      Expr *E) {
+  QualType T = E->getType();
+  assert(T->isPointerType());
+
+  const CountAttributedType *CATy = nullptr;
+  QualType PointeeTy;
+  NamedDecl *IncompleteTyDecl = nullptr;
+  if (!HasCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl, &CATy,
+                                           &PointeeTy))
+    return true;
+  assert(CATy && !CATy->isCountInBytes() && !PointeeTy.isNull());
+
+  // Generate a string for the diagnostic that describes the "use".
+  // The string is specialized for direct calls to produce a better
+  // diagnostic.
+  StringRef DirectCallFn;
+  std::string UseStr;
+  if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParens())) {
+    if (const auto *FD = CE->getDirectCallee()) {
+      DirectCallFn = FD->getName();
+    }
+  }
+  int SelectExprKind = DirectCallFn.size() > 0 ? 1 : 0;
+  if (SelectExprKind) {
+    UseStr = DirectCallFn;
+  } else {
+    llvm::raw_string_ostream SS(UseStr);
+    E->printPretty(SS, nullptr, PrintingPolicy(S.getPrintingPolicy()));
+  }
+  assert(UseStr.size() > 0);
+
+  S.Diag(E->getBeginLoc(), diag::err_counted_by_on_incomplete_type_on_use)
+      << /*0*/ SelectExprKind << /*1*/ UseStr << /*2*/ T << /*3*/ PointeeTy
+      << /*4*/ CATy->GetAttributeName(/*WithMacroPrefix=*/true)
+      << /*5*/ CATy->isOrNull() << E->getSourceRange();
+
+  EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);
+  return false;
+}
+
+bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(Expr *E) {
+
+  QualType T = E->getType();
+  if (!T->isPointerType())
+    return true;
+
+  return BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy(*this, E);
+}
+
 } // namespace clang
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 95f53dfefbcc52..265213aa8bcbe5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -731,6 +731,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
   if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct)
     Cleanup.setExprNeedsCleanups(true);
 
+  if (!BoundsSafetyCheckUseOfCountAttrPtr(Res.get()))
+    return ExprError();
+
   // C++ [conv.lval]p3:
   //   If T is cv std::nullptr_t, the result is a null pointer constant.
   CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue;
@@ -13559,6 +13562,27 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
   QualType LHSType = LHSExpr->getType();
   QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() :
                                              CompoundType;
+
+  if (RHS.isUsable()) {
+    // Even if this check fails don't return early to allow the best
+    // possible error recovery and to allow any subsequent diagnostics to
+    // work.
+    (void)BoundsSafetyCheckAssignmentToCountAttrPtr(
+        LHSType, RHS.get(), Sema::AA_Assigning, Loc,
+        [&LHSExpr]() -> std::string {
+          // In simple cases describe what is being assigned to
+          if (auto *DR = dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenCasts())) {
+            auto *II = DR->getDecl()->getDeclName().getAsIdentifierInfo();
+            if (II)
+              return II->getName().str();
+          } else if (auto *ME =
+                         dyn_cast<MemberExpr>(LHSExpr->IgnoreParenCasts())) {
+            return ME->getMemberDecl()->getQualifiedNameAsString();
+          }
+          return "";
+        });
+  }
+
   // OpenCL v1.2 s6.1.1.1 p2:
   // The half data type can only be used to declare a pointer to a buffer that
   // contains half values
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 5a19a3505454ca..b14e5ba15520e5 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8192,6 +8192,14 @@ ExprResult InitializationSequence::Perform(Sema &S,
             Kind.getRange().getEnd());
       } else {
         CurInit = new (S.Context) ImplicitValueInitExpr(Step->Type);
+        // Note the return value isn't used to return early
+        // to preserve the AST as best as possible even though an error
+        // might have occurred. For struct initialization it also allows
+        // all field assignments to be checked rather than bailing on the
+        // first error.
+        S.BoundsSafetyCheckInitialization(Entity, Kind, Sema::AA_Initializing,
+                                          /*LHSType=*/Step->Type,
+                                          /*RHSExpr=*/CurInit.get());
       }
       break;
     }
@@ -8238,6 +8246,15 @@ ExprResult InitializationSequence::Perform(Sema &S,
         }
       }
 
+      // Note the return value isn't used to return early so that additional
+      // diagnostics can be emitted and to preserve the AST as best as possible
+      // even though an error might have occurred. For struct initialization it
+      // also allows all field assignments to be checked rather than bailing on
+      // the first error.
+      (void)S.BoundsSafetyCheckInitialization(
+          Entity, Kind, /*Action=*/getAssignmentAction(Entity, true),
+          /*LHSType=*/Step->Type, /*RHSExpr=*/InitialCurInit.get());
+
       bool Complained;
       if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
                                      Step->Type, SourceType,
diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
index 9ff3b080f6576b..8d4e0c510603a2 100644
--- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
@@ -19,13 +19,12 @@ struct on_member_pointer_complete_ty {
 };
 
 struct on_member_pointer_incomplete_ty {
-  struct size_unknown * buf __counted_by(count); // expected-error{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}}
+  struct size_unknown * buf __counted_by(count); // ok
   int count;
 };
 
 struct on_member_pointer_const_incomplete_ty {
-  // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}}
-  const struct size_unknown * buf __counted_by(count);
+  const struct size_unknown * buf __counted_by(count); // ok
   int count;
 };
 
diff --git a/clang/test/Sema/attr-counted-by-or-null-last-field.c b/clang/test/Sema/attr-counted-by-or-null-last-field.c
index 12c0b6de44f726..60a1f571b19e95 100644
--- a/clang/test/Sema/attr-counted-by-or-null-last-field.c
+++ b/clang/test/Sema/attr-counted-by-or-null-last-field.c
@@ -118,12 +118,12 @@ struct annotated_with_anon_struct {
 struct count_unknown;
 struct on_member_ptr_incomplete_ty_ty_pos {
   int count;
-  struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct count_unknown' is an incomplete type}}
+  struct count_unknown * ptr __counted_by_or_null(count); // ok
 };
 
 struct on_member_ptr_incomplete_const_ty_ty_pos {
   int count;
-  const struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct count_unknown' is an incomplete type}}
+  const struct count_unknown * ptr __counted_by_or_null(count); // ok
 };
 
 struct on_member_ptr_void_ty_ty_pos {
diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
index 95f517e3144f72..2150c81f9e9bec 100644
--- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
@@ -20,13 +20,12 @@ struct on_member_pointer_complete_ty {
 };
 
 struct on_member_pointer_incomplete_ty {
-  struct size_unknown * buf __counted_by_or_null(count); // expected-error{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}}
+  struct size_unknown * buf __counted_by_or_null(count); // ok
   int count;
 };
 
 struct on_member_pointer_const_incomplete_ty {
-  // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}}
-  const struct size_unknown * buf __counted_by_or_null(count);
+  const struct size_unknown * buf __counted_by_or_null(count); // ok
   int count;
 };
 
diff --git a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
index 708bb727ce09da..0bb09059c97f91 100644
--- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
@@ -22,14 +22,12 @@ struct on_member_pointer_complete_ty {
 
 struct on_member_pointer_incomplete_ty {
   int count;
-  // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}}
-  struct size_unknown * buf __counted_by_or_null(count);
+  struct size_unknown * buf __counted_by_or_null(count); // ok
 };
 
 struct on_member_pointer_const_incomplete_ty {
   int count;
-  // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}}
-  const struct size_unknown * buf __counted_by_or_null(count);
+  const struct size_unknown * buf __counted_by_or_null(count); // ok
 };
 
 struct on_member_pointer_void_ty {
@@ -116,14 +114,12 @@ struct on_member_pointer_complete_ty_ty_pos {
 
 struct on_member_pointer_incomplete_ty_ty_pos {
   int count;
-  // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}}
-  struct size_unknown * __counted_by_or_null(count) buf;
+  struct size_unknown * __counted_by_or_null(count) buf; // ok
 };
 
 struct on_member_pointer_const_incomplete_ty_ty_pos {
   int count;
-  // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}}
-  const struct size_unknown * __counted_by_or_null(count) buf;
+  const struct size_unknown * __counted_by_or_null(count) buf; // ok
 };
 
 struct on_member_pointer_void_ty_ty_pos {
diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
new file mode 100644
index 00000000000000..8e2e163822b1fe
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
@@ -0,0 +1,584 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s
+
+#define __counted_by(f)  __attribute__((counted_by(f)))
+
+// =============================================================================
+// # Struct incomplete type with attribute in the decl position
+// =============================================================================
+
+// Note: This could be considered misleading. The typedef isn't actually on this
+// line. Also note the discrepancy in diagnostic count (27 vs 51) is due to
+// the pointer arithmetic on incomplete pointee type diagnostic always using
+// diagnostic text that refers to the underlying forward decl, even when the
+// typedef is used.
+// expected-note at +1 27{{forward declaration of 'Incomplete_t' (aka 'struct IncompleteTy')}}
+struct IncompleteTy; // expected-note 51{{forward declaration of 'struct IncompleteTy'}}
+
+typedef struct IncompleteTy Incomplete_t; 
+
+struct CBBufDeclPos {
+  int count;
+  struct IncompleteTy* buf __counted_by(count); // OK expected-note 27{{__counted_by attribute is here}}
+  Incomplete_t* buf_typedef __counted_by(count); // OK expected-note 27{{__counted_by attribute is here}}
+};
+
+void consume_struct_IncompleteTy(struct IncompleteTy* buf);
+
+int idx(void);
+
+
+
+void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
+  // ===========================================================================
+  // ## Local variable initialization
+  // ===========================================================================
+  struct CBBufDeclPos explicit_desig_init = {
+    .count = 0,
+    // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+  // Variable is not currently marked as invalid so uses of the variable allows
+  // diagnostics to fire.
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error at +1{{cannot use 'explicit_desig_init.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  void *tmp = explicit_desig_init.buf;
+  // expected-error at +1{{cannot use 'explicit_desig_init.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  void *tmp2 = explicit_desig_init.buf_typedef;
+
+  struct CBBufDeclPos partial_explicit_desig_init = {
+    .count = 0,
+    // .buf and .buf_typedef are implicit zero initialized
+    // expected-error at +2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+    // expected-error at +1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  };
+
+  struct CBBufDeclPos implicit_full_init = {
+    0
+    // expected-error at +2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+    // expected-error at +1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  };
+  // Variable is not currently marked as invalid so uses of the variable allows
+  // diagnostics to fire.
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  implicit_full_init.buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  implicit_full_init.buf_typedef = 0x0;
+  // expected-error at +1{{cannot use 'implicit_full_init.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  void* tmp3 = implicit_full_init.buf;
+  // expected-error at +1{{cannot use 'implicit_full_init.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  void* tmp4 = implicit_full_init.buf_typedef;
+  
+  struct CBBufDeclPos explicit_non_desig_init = {
+    0,
+    // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+    0x0,
+    // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+    0x0
+  };
+
+  // ===========================================================================
+  // ## Assignment to fields
+  // ===========================================================================
+  struct CBBufDeclPos uninit;
+  uninit.count = 0;
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  uninit.buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  uninit.buf_typedef = 0x0;
+  ptr->count = 0;
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+
+  // ===========================================================================
+  // ## Make sure modifying the fields through other assignment operators is not
+  //    allowed
+  // ===========================================================================
+  uninit.buf++; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  ++uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  uninit.buf += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  
+  uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  --uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  uninit.buf_typedef--; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  --uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+
+  ptr->buf++; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  ++ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  ptr->buf += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  ptr->buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  --ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+  ptr->buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
+
+  ptr->buf_typedef++; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ++ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ptr->buf_typedef += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ptr->buf_typedef--; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  --ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ptr->buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+
+  // ===========================================================================
+  // ## Use of fields in expressions
+  // ===========================================================================
+  // expected-error at +2{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) uninit.buf ) + 1;
+  // expected-error at +2{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) uninit.buf_typedef ) + 1;
+  // expected-error at +2{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  void* addr_ptr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error at +2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  void* addr_ptr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // ===========================================================================
+  // ## Take address of fields
+  // ===========================================================================
+  // TODO: This should be forbidden, not because of the incomplete pointee type
+  // but because in the -fbounds-safety language model the address of a
+  // `counted_by` pointer cannot be taken to avoid it being possible to modify
+  // the `counted_by` pointer through another pointer. Whether or not this
+  // should be forbidden when `-fbounds-safety` is off is TBD.
+  //
+  // The incomplete pointee type isn't actually a problem here for
+  // `-fbounds-safety` because taking the address of a pointer returns a pointer
+  // that have the bounds of a single `void*`, so bounds checks on the resulting
+  // pointer don't need to know `sizeof(struct IncompleteTy)` but instead
+  // `sizeof(struct IncompleteTy* buf __counted_by(count))` which is just the
+  // size of a pointer.
+  void* take_addr = &uninit.buf;
+  void* take_addr_typedef = &uninit.buf_typedef;
+  void* take_addr_ptr = &ptr->buf;
+  void* take_addr_ptr_typedef = &ptr->buf_typedef;
+
+  // expected-error at +1{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_zero = &uninit.buf[0];
+  // expected-error at +1{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_idx = &uninit.buf[idx()];
+
+  // expected-error at +1{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_zero_typedef = &uninit.buf_typedef[0];
+  // expected-error at +1{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_idx_typedef = &uninit.buf_typedef[idx()];
+
+  // expected-error at +1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0];
+  // expected-error at +1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()];
+  // expected-error at +1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0];
+  // expected-error at +1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()];
+
+  // ===========================================================================
+  // ## Use fields as call arguments
+  // ===========================================================================
+  // expected-error at +1{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy(uninit.buf);
+  // expected-error at +1{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy(uninit.buf_typedef);
+  // expected-error at +1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy(ptr->buf);
+  // expected-error at +1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy(ptr->buf_typedef);
+
+  // ===========================================================================
+  // ## Use [] operator on fields
+  // ===========================================================================
+  // expected-error at +1 2{{cannot use 'uninit.buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  uninit.buf[0] = uninit.buf[1];
+  // expected-error at +1 2{{cannot use 'uninit.buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  uninit.buf_typedef[0] = uninit.buf_typedef[1];
+  // expected-error at +1 2{{cannot use 'ptr->buf' with type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error at +1 2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+// =============================================================================
+// ## Global initialization
+// =============================================================================
+
+struct CBBufDeclPos global_explicit_desig_init = {
+  .count = 0,
+  // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+void use_global_explicit_desig_init(void) {
+  // Variable isn't marked as invalid so diagnostics still fire
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  global_explicit_desig_init.buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  global_explicit_desig_init.buf_typedef = 0x0;
+}
+
+struct CBBufDeclPos global_partial_explicit_desig_init = {
+  .count = 0,
+  // .buf and .buf_typedef are implicit zero initialized
+  // expected-error at +2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  // expected-error at +1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+};
+
+struct CBBufDeclPos global_implicit_full_init = {
+  0
+  // expected-error at +2{{cannot implicitly initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  // expected-error at +1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+};
+
+struct CBBufDeclPos global_explicit_non_desig_init = {
+  0,
+  // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf' that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  0x0,
+  // expected-error at +1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  0x0
+};
+
+extern struct CBBufDeclPos global_declaration; // OK
+
+// TODO: These tentative definitions are implicitly empty initialized to zero.
+// This should generate an error diagnostic but currently doesn't. There should
+// be a carve out to allow `__counted_by(0)` which is the only constant count
+// version of the attribute where it is valid to assign NULL.
+struct CBBufDeclPos global_tentative_defn;
+static struct CBBufDeclPos global_tentative_defn2;
+
+// =============================================================================
+// ## Completing the definition of the type allows use of CBBufDeclPos fields
+// =============================================================================
+struct IncompleteTy {
+  int field;
+};
+
+void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) {
+  // Initialization is ok
+  struct CBBufDeclPos explicit_desig_init = {
+    .count = 0,
+    .buf = 0x0,
+    .buf_typedef = 0x0
+  };
+
+  struct CBBufDeclPos partial_explicit_desig_init = {
+    .count = 0,
+    // .buf and .buf_typedef are implicit zero initialized
+  };
+
+  struct CBBufDeclPos implicit_full_init = {0};
+  
+  struct CBBufDeclPos explicit_non_desig_init = {
+    0,
+    0x0,
+    0x0
+  };
+
+  // Assignment to fields is ok
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+
+  // Use of fields in expressions is ok
+  void* tmp = ptr->buf;
+  void* tmp2 = ptr->buf_typedef;
+
+  // Take address of fields is ok
+  void* take_addr_ptr = &ptr->buf;
+  void* take_addr_ptr_typedef = &ptr->buf_typedef;
+
+  struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0];
+  struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()];
+  struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0];
+  struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()];
+
+  // As call arguments is ok
+  consume_struct_IncompleteTy(ptr->buf);
+  consume_struct_IncompleteTy(ptr->buf_typedef);
+
+  // In [] operator is ok
+  ptr->buf[0] = ptr->buf[1];
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+// Global initialization is ok
+
+struct CBBufDeclPos global_explicit_desig_init_completed = {
+  .count = 0,
+  .buf = 0x0,
+  .buf_typedef = 0x0
+};
+
+struct CBBufDeclPos global_partial_explicit_desig_init_completed = {
+  .count = 0,
+  // .buf and .buf_typedef are implicit zero initialized
+};
+
+struct CBBufDeclPos global_implicit_full_init_completed = {0};
+
+struct CBBufDeclPos global_explicit_non_desig_init_completed = {
+  0,
+  0x0,
+  0x0
+};
+
+extern struct CBBufDeclPos global_declaration;
+struct CBBufDeclPos global_tentative_defn;
+static struct CBBufDeclPos global_tentative_defn2;
+
+// =============================================================================
+// # Struct incomplete type with attribute in the pointer position
+// =============================================================================
+
+// expected-note at +1 8{{forward declaration of 'Incomplete_ty2' (aka 'struct IncompleteTy2')}}
+struct IncompleteTy2; // expected-note 8{{forward declaration of 'struct IncompleteTy2'}}
+typedef struct IncompleteTy2 Incomplete_ty2;
+
+void consume_struct_IncompleteTy2(struct IncompleteTy2* buf);
+
+struct CBBufTyPos {
+  int count;
+  struct IncompleteTy2* __counted_by(count) buf ; // OK expected-note 8{{__counted_by attribute is here}}
+  Incomplete_ty2 *__counted_by(count) buf_typedef; // OK expected-note 8{{__counted_by attribute is here}}
+};
+
+void use_CBBufTyPos(struct CBBufTyPos* ptr) {
+  struct CBBufTyPos explicit_desig_init = {
+    .count = 0,
+    // expected-error at +1{{cannot initialize 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error at +1{{cannot initialize 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+
+  // Assignment
+  // expected-error at +1{{cannot assign to 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+  // Use
+  // expected-error at +2{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error at +2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // expected-error at +1{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy2(ptr->buf);
+  // expected-error at +1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy2(ptr->buf_typedef);
+
+  // expected-error at +1 2{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error at +1 2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+struct CBBufTyPos global_explicit_desig_init_struct_type_pos = {
+  .count = 0,
+  // expected-error at +1{{cannot initialize 'CBBufTyPos::buf' that has type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error at +1{{cannot initialize 'CBBufTyPos::buf_typedef' that has type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_ty2' or using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+// Defining the type makes `CBBufTyPos` fields usable
+struct IncompleteTy2 {
+  int field;
+};
+
+void use_CBBufTyPos_completed(struct CBBufTyPos* ptr) {
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+  void* addr = ((char*) ptr->buf) + 1;
+  void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
+}
+
+// =============================================================================
+// # union incomplete type
+// =============================================================================
+
+// expected-note at +1 8{{forward declaration of 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy')}}
+union IncompleteUnionTy; // expected-note 8{{forward declaration of 'union IncompleteUnionTy'}}
+typedef union IncompleteUnionTy IncompleteUnion_ty;
+
+void consume_struct_IncompleteUnionTy(union IncompleteUnionTy* buf);
+
+struct CBBufUnionTyPos {
+  int count;
+  union IncompleteUnionTy* __counted_by(count) buf ; // OK expected-note 8{{__counted_by attribute is here}}
+  IncompleteUnion_ty *__counted_by(count) buf_typedef; // OK expected-note 8{{__counted_by attribute is here}}
+};
+
+void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) {
+  struct CBBufUnionTyPos explicit_desig_init = {
+    .count = 0,
+    // expected-error at +1{{cannot initialize 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error at +1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+
+  // Assignment
+  // expected-error at +1{{cannot assign to 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+  // Use
+  // expected-error at +2{{cannot use 'ptr->buf' with type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error at +2{{cannot use 'ptr->buf_typedef' with type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // expected-error at +1{{cannot use 'ptr->buf' with type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteUnionTy(ptr->buf);
+  // expected-error at +1{{cannot use 'ptr->buf_typedef' with type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteUnionTy(ptr->buf_typedef);
+
+  // expected-error at +1 2{{cannot use 'ptr->buf' with type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error at +1 2{{cannot use 'ptr->buf_typedef' with type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+struct CBBufUnionTyPos global_explicit_desig_init_union_type_pos = {
+  .count = 0,
+  // expected-error at +1{{cannot initialize 'CBBufUnionTyPos::buf' that has type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'union IncompleteUnionTy' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error at +1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+// Defining the type makes `CBBufUnionTyPos` fields usable
+union IncompleteUnionTy {
+  int field;
+};
+
+void use_CBBufUnionTyPos_completed(struct CBBufUnionTyPos* ptr) {
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+  void* addr = ((char*) ptr->buf) + 1;
+  void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
+}
+
+// =============================================================================
+// # enum incomplete type
+// =============================================================================
+
+// expected-note at +1 8{{forward declaration of 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy')}}
+enum IncompleteEnumTy; // expected-note 8{{forward declaration of 'enum IncompleteEnumTy'}}
+typedef enum IncompleteEnumTy IncompleteEnum_ty;
+
+void consume_struct_IncompleteEnumTy(enum IncompleteEnumTy* buf);
+
+struct CBBufEnumTyPos {
+  int count;
+  enum IncompleteEnumTy* __counted_by(count) buf ; // OK expected-note 8{{__counted_by attribute is here}}
+  IncompleteEnum_ty *__counted_by(count) buf_typedef; // OK expected-note 8{{__counted_by attribute is here}}
+};
+
+void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) {
+  struct CBBufEnumTyPos explicit_desig_init = {
+    .count = 0,
+    // expected-error at +1{{cannot initialize 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error at +1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+
+  // Assignment
+  // expected-error at +1{{cannot assign to 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error at +1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+  // Use
+  // expected-error at +2{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error at +2{{cannot use 'ptr->buf_typedef' with type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // expected-error at +1{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteEnumTy(ptr->buf);
+  // expected-error at +1{{cannot use 'ptr->buf_typedef' with type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteEnumTy(ptr->buf_typedef);
+
+  // expected-error at +1 2{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error at +1 2{{cannot use 'ptr->buf_typedef' with type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+struct CBBufEnumTyPos global_explicit_desig_init_enum_type_pos = {
+  .count = 0,
+  // expected-error at +1{{cannot initialize 'CBBufEnumTyPos::buf' that has type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error at +1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' that has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+// Defining the type makes `CBBufEnumTyPos` fields usable
+enum IncompleteEnumTy {
+  ONE,
+  TWO
+};
+
+void use_CBBufEnumTyPos_completed(struct CBBufEnumTyPos* ptr) {
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+  void* addr = ((char*) ptr->buf) + 1;
+  void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
+}
+
+// =============================================================================
+// # Array of __counted_by pointers
+// =============================================================================
+
+struct IncompleteTy3;
+
+struct CBBufFAMofCountedByPtrs {
+  int size;
+  // TODO: This is misleading. The attribute is written in the type position
+  // but clang currently doesn't treat it like that and it gets treated as
+  // an attribute on the array, rather than on the element type.
+  struct IncompleteTy3* __counted_by(size) arr[];
+};
+
+void arr_of_counted_by_ptr(struct CBBufFAMofCountedByPtrs* ptr) {
+  // TODO: Should be disallowed once parsing attributes in the type position
+  // works.
+  ptr->arr[0] = 0x0;
+  void* addr = ((char*) ptr->arr[0]) + 1;
+}
diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs.c b/clang/test/Sema/attr-counted-by-struct-ptrs.c
index 321d6aafbeba24..c05d18262e2b77 100644
--- a/clang/test/Sema/attr-counted-by-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c
@@ -21,14 +21,12 @@ struct on_member_pointer_complete_ty {
 
 struct on_member_pointer_incomplete_ty {
   int count;
-  // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}}
-  struct size_unknown * buf __counted_by(count);
+  struct size_unknown * buf __counted_by(count); // ok
 };
 
 struct on_member_pointer_const_incomplete_ty {
   int count;
-  // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}}
-  const struct size_unknown * buf __counted_by(count);
+  const struct size_unknown * buf __counted_by(count); // ok
 };
 
 struct on_member_pointer_void_ty {
@@ -115,14 +113,12 @@ struct on_member_pointer_complete_ty_ty_pos {
 
 struct on_member_pointer_incomplete_ty_ty_pos {
   int count;
-  // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}}
-  struct size_unknown * __counted_by(count) buf;
+  struct size_unknown * __counted_by(count) buf; // ok
 };
 
 struct on_member_pointer_const_incomplete_ty_ty_pos {
   int count;
-  // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}}
-  const struct size_unknown * __counted_by(count) buf;
+  const struct size_unknown * __counted_by(count) buf; // ok
 };
 
 struct on_member_pointer_void_ty_ty_pos {
diff --git a/clang/test/Sema/attr-counted-by-vla.c b/clang/test/Sema/attr-counted-by-vla.c
index 35737c03f3222e..5b1b4833dce7df 100644
--- a/clang/test/Sema/attr-counted-by-vla.c
+++ b/clang/test/Sema/attr-counted-by-vla.c
@@ -98,8 +98,7 @@ struct array_of_ints_count {
 
 struct not_a_fam {
   int count;
-  // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct bar' is an incomplete type}}
-  struct bar *non_fam __counted_by(count);
+  struct bar *non_fam __counted_by(count); // ok
 };
 
 struct not_a_c99_fam {
diff --git a/clang/unittests/Sema/CMakeLists.txt b/clang/unittests/Sema/CMakeLists.txt
index 7ded562e8edfa5..ebc829e82af6b5 100644
--- a/clang/unittests/Sema/CMakeLists.txt
+++ b/clang/unittests/Sema/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
 add_clang_unittest(SemaTests
   ExternalSemaSourceTest.cpp
   CodeCompleteTest.cpp
+  GetCountedByAttrSourceRange.cpp
   GslOwnerPointerInference.cpp
   SemaLookupTest.cpp
   SemaNoloadLookupTest.cpp
diff --git a/clang/unittests/Sema/GetCountedByAttrSourceRange.cpp b/clang/unittests/Sema/GetCountedByAttrSourceRange.cpp
new file mode 100644
index 00000000000000..3333ed046bff82
--- /dev/null
+++ b/clang/unittests/Sema/GetCountedByAttrSourceRange.cpp
@@ -0,0 +1,573 @@
+//=== unittests/Sema/GetCountedByAttrSourceRange.cpp =========================//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// FIXME: audit these header files
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Testing/Annotations/Annotations.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+using CallBackTy =
+    std::function<void(CompilerInstance &, const CountAttributedType *)>;
+
+bool runOnToolAndCheckAttrSourceRange(llvm::Annotations &Code,
+                                      bool TestForFallback = false) {
+  class RunFnAfterSema : public clang::ASTFrontendAction {
+    llvm::Annotations &Code;
+    bool TestForFallback;
+
+  public:
+    RunFnAfterSema(llvm::Annotations &Code, bool TestForFallback)
+        : Code(Code), TestForFallback(TestForFallback) {}
+    void EndSourceFile() override {
+      auto &S = getCompilerInstance().getSema();
+      // Find `TestStruct::ptr`
+      auto Matched = match(
+          recordDecl(hasName("TestStruct"),
+                     hasDescendant(fieldDecl(hasName("ptr")).bind("ptr"))),
+          S.getASTContext());
+      EXPECT_EQ(Matched.size(), 1u);
+      auto *FD = Matched[0].getNodeAs<FieldDecl>("ptr");
+      ASSERT_NE(FD, nullptr);
+
+      const auto *CATy = FD->getType()->getAs<CountAttributedType>();
+
+      auto SR = S.BoundsSafetySourceRangeFor(CATy);
+
+      if (TestForFallback) {
+        // Make sure for these types of test cases that there are no
+        // annotations. The presence of annotations for this type of test would
+        // be very misleading because they aren't being checked.
+        const auto &Ranges = Code.all_ranges();
+        ASSERT_EQ(Ranges.size(), 0U);
+
+        // The fallback is using the SourceRange of the CountExpr.
+        ASSERT_EQ(SR, CATy->getCountExpr()->getSourceRange());
+        // Don't test for precise column position in this case.
+        // The code below doesn't really work correctly when the
+        // count expression is itself a macro.
+        return;
+      }
+
+      // Check Begin
+      auto ExpectedBegin = Code.range("attr").Begin;
+      auto Begin = SR.getBegin();
+      auto BeginSpellingLoc = S.getSourceManager().getSpellingLoc(Begin);
+      auto BeginFileOffset =
+          S.getSourceManager().getFileOffset(BeginSpellingLoc);
+      ASSERT_EQ(BeginFileOffset, ExpectedBegin);
+      // Check End
+      auto ExpectedEnd = Code.range("attr").End - 1;
+      auto End = SR.getEnd();
+      auto EndSpellingLoc = S.getSourceManager().getSpellingLoc(End);
+      auto EndFileOffset = S.getSourceManager().getFileOffset(EndSpellingLoc);
+      ASSERT_EQ(EndFileOffset, ExpectedEnd);
+
+      ASTFrontendAction::EndSourceFile();
+    }
+
+    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                   StringRef InFile) override {
+      return std::make_unique<ASTConsumer>();
+    }
+  };
+  auto Action = std::make_unique<RunFnAfterSema>(Code, TestForFallback);
+  return clang::tooling::runToolOnCodeWithArgs(std::move(Action), Code.code(),
+                                               {"-std=c11"}, "test.c");
+}
+
+//==============================================================================
+// counted_by
+//==============================================================================
+
+TEST(GetCountedByAttrSourceRange, GNUAttrNoAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by(X) __attribute__((counted_by(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[counted_by(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByAttrSourceRange, GNUAttrNoAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __counted_by(X) __attribute__((counted_by(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((counted_by(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByAttrSourceRange, GNUAttrAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by(X) __attribute__((__counted_by__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[counted_by(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByAttrSourceRange, GNUAttrAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __counted_by(X) __attribute__((__counted_by__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((counted_by(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByAttrSourceRange, MacroNoAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by(X) __attribute__((counted_by(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__counted_by(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByAttrSourceRange, MacroAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by(X) __attribute__((__counted_by__(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__counted_by(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByAttrSourceRange, CustomMacro) {
+  // For this code no special SourceRange is computed.
+  llvm::Annotations Code(
+      R"C(
+#define custom_cb(X) __attribute__((__counted_by__(X)))
+struct TestStruct {
+  int count;
+  char* custom_cb(count) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByAttrSourceRange, MacroArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __counted_by(X) __attribute__((__counted_by__(X)))
+struct TestStruct {
+  int count;
+  char* __counted_by(COUNT) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code, true);
+  ASSERT_TRUE(result);
+}
+
+//==============================================================================
+// counted_by_or_null
+//==============================================================================
+
+TEST(GetCountedByOrNullAttrSourceRange, GNUAttrNoAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by_or_null(X) __attribute__((counted_by_or_null(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[counted_by_or_null(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByOrNullAttrSourceRange, GNUAttrNoAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __counted_by_or_null(X) __attribute__((counted_by_or_null(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((counted_by_or_null(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByOrNullAttrSourceRange, GNUAttrAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by_or_null(X) __attribute__((__counted_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[counted_by_or_null(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByOrNullAttrSourceRange, GNUAttrAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __counted_by_or_null(X) __attribute__((__counted_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((counted_by_or_null(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByOrNullAttrSourceRange, MacroNoAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by_or_null(X) __attribute__((counted_by_or_null(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__counted_by_or_null(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByOrNullAttrSourceRange, MacroAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __counted_by_or_null(X) __attribute__((__counted_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__counted_by_or_null(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByOrNullAttrSourceRange, CustomMacro) {
+  // For this code no special SourceRange is computed.
+  llvm::Annotations Code(
+      R"C(
+#define custom_cbon(X) __attribute__((__counted_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* custom_cbon(count) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetCountedByOrNullAttrSourceRange, MacroArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __counted_by_or_null(X) __attribute__((__counted_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* __counted_by_or_null(COUNT) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code, true);
+  ASSERT_TRUE(result);
+}
+
+//==============================================================================
+// sized_by
+//==============================================================================
+
+TEST(GetSizedByAttrSourceRange, GNUAttrNoAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by(X) __attribute__((sized_by(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[sized_by(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByAttrSourceRange, GNUAttrNoAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __sized_by(X) __attribute__((sized_by(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((sized_by(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByAttrSourceRange, GNUAttrAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by(X) __attribute__((__sized_by__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[sized_by(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByAttrSourceRange, GNUAttrAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __sized_by(X) __attribute__((__sized_by__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((sized_by(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByAttrSourceRange, MacroNoAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by(X) __attribute__((sized_by(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__sized_by(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByAttrSourceRange, MacroAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by(X) __attribute__((__sized_by__(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__sized_by(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByAttrSourceRange, CustomMacro) {
+  // For this code no special SourceRange is computed.
+  llvm::Annotations Code(
+      R"C(
+#define custom_sb(X) __attribute__((__sized_by__(X)))
+struct TestStruct {
+  int count;
+  char* custom_sb(count) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByAttrSourceRange, MacroArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __sized_by(X) __attribute__((__sized_by__(X)))
+struct TestStruct {
+  int count;
+  char* __sized_by(COUNT) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code, true);
+  ASSERT_TRUE(result);
+}
+
+//==============================================================================
+// sized_by_or_null
+//==============================================================================
+
+TEST(GetSizedByOrNullAttrSourceRange, GNUAttrNoAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by_or_null(X) __attribute__((sized_by_or_null(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[sized_by_or_null(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByOrNullAttrSourceRange, GNUAttrNoAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __sized_by_or_null(X) __attribute__((sized_by_or_null(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((sized_by_or_null(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByOrNullAttrSourceRange, GNUAttrAffix) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by_or_null(X) __attribute__((__sized_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__(($attr[[sized_by_or_null(count)]])) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByOrNullAttrSourceRange, GNUAttrAffixArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __sized_by_or_null(X) __attribute__((__sized_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* __attribute__((sized_by_or_null(COUNT))) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByOrNullAttrSourceRange, MacroNoAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by_or_null(X) __attribute__((sized_by_or_null(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__sized_by_or_null(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByOrNullAttrSourceRange, MacroAffixImpl) {
+  llvm::Annotations Code(
+      R"C(
+#define __sized_by_or_null(X) __attribute__((__sized_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* $attr[[__sized_by_or_null(count)]] ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByOrNullAttrSourceRange, CustomMacro) {
+  // For this code no special SourceRange is computed.
+  llvm::Annotations Code(
+      R"C(
+#define custom_sbon(X) __attribute__((__sized_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* custom_sbon(count) ptr;
+};
+)C");
+  bool result =
+      runOnToolAndCheckAttrSourceRange(Code, /*TestForFallback=*/true);
+  ASSERT_TRUE(result);
+}
+
+TEST(GetSizedByOrNullAttrSourceRange, MacroArgIsMacro) {
+  // For this code no special SourceRange is computed
+  // FIXME: This shouldn't use the fallback path.
+  llvm::Annotations Code(
+      R"C(
+#define COUNT count
+#define __sized_by_or_null(X) __attribute__((__sized_by_or_null__(X)))
+struct TestStruct {
+  int count;
+  char* __sized_by_or_null(COUNT) ptr;
+};
+)C");
+  bool result = runOnToolAndCheckAttrSourceRange(Code, true);
+  ASSERT_TRUE(result);
+}



More information about the cfe-commits mailing list