================ @@ -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(); + } ---------------- delcypher wrote:
This seems to work despite the invalid source range. It looks like `TextDiagnostic::emitDiagnosticLoc` ignores invalid source ranges. https://github.com/llvm/llvm-project/pull/106321 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits