[clang] [BoundsSafety][Sema] Allow counted_by and counted_by_or_null on pointers where the pointee type is incomplete but potentially completable (PR #106321)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 13 09:20:34 PST 2025
================
@@ -186,4 +218,188 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
return false;
}
+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
+ // This is an approximation that's not quite right. This points to the
+ // the expression inside the attribute rather than the attribute itself.
+ //
+ // TODO: Implement logic to find the relevant TypeLoc for the attribute and
+ // get the SourceRange from that (#113582).
+ SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange();
+ 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')`
+ S.Diag(IncompleteTyDecl->getBeginLoc(), diag::note_forward_declaration)
+ << CATy->getPointeeType();
+}
+
+static std::tuple<const CountAttributedType *, QualType>
+HasCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) {
+ auto *CATy = Ty->getAs<CountAttributedType>();
+ if (!CATy)
+ return {};
+
+ // Incomplete pointee type is only a problem for
+ // counted_by/counted_by_or_null
+ if (CATy->isCountInBytes())
+ return {};
+
+ auto PointeeTy = CATy->getPointeeType();
+ if (PointeeTy.isNull())
+ return {}; // Reachable?
+
+ if (!PointeeTy->isIncompleteType(ND))
+ return {};
+
+ return {CATy, PointeeTy};
+}
+
+/// 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 Assignee The ValueDecl being assigned. This is used to compute
+/// the name of the assignee. If the assignee isn't known this can
+/// be set to nullptr.
+/// \param ShowFullyQualifiedAssigneeName If set to true when using \p
+/// Assignee to compute the name of the assignee use the fully
+/// qualified name, otherwise use the unqualified name.
+///
+/// \returns True iff no diagnostic where emitted, false otherwise.
+static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
+ Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
+ SourceLocation Loc, const ValueDecl *Assignee,
+ bool ShowFullyQualifiedAssigneeName) {
+ NamedDecl *IncompleteTyDecl = nullptr;
+ auto [CATy, PointeeTy] =
+ HasCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl);
+ if (!CATy)
+ return true;
+
+ std::string AssigneeStr;
+ if (Assignee) {
+ if (ShowFullyQualifiedAssigneeName) {
+ AssigneeStr = Assignee->getQualifiedNameAsString();
+ } else {
+ AssigneeStr = Assignee->getNameAsString();
+ }
+ }
+ {
+ auto D = S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign)
+ << static_cast<int>(Action) << AssigneeStr
+ << (AssigneeStr.size() > 0) << isa<ImplicitValueInitExpr>(RHSExpr)
+ << LHSTy << CATy->getAttributeName(/*WithMacroPrefix=*/true)
+ << PointeeTy << CATy->isOrNull();
+
+ if (RHSExpr->getSourceRange().isValid())
+ D << RHSExpr->getSourceRange();
+ }
----------------
Sirraide wrote:
Have you run into any concrete problems w/ the source range being invalid here? *Generally*, we expect the vast majority of source ranges to be valid, so unconditionally passing one to a diagnostic shouldn’t be an issue.
https://github.com/llvm/llvm-project/pull/106321
More information about the cfe-commits
mailing list