Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def =================================================================== --- clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -36,6 +36,7 @@ FIXABLE_GADGET(UPCAddressofArraySubscript) // '&DRE[any]' in an Unspecified Pointer Context FIXABLE_GADGET(DerefSimplePtrArithFixable) FIXABLE_GADGET(PointerCtxAccess) +FIXABLE_GADGET(PointerArithmeticFixable) #undef FIXABLE_GADGET #undef WARNING_GADGET Index: clang/lib/Analysis/UnsafeBufferUsage.cpp =================================================================== --- clang/lib/Analysis/UnsafeBufferUsage.cpp +++ clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -614,6 +614,56 @@ return {}; } }; + +class PointerArithmeticFixableGadget : public FixableGadget { +private: + static constexpr const char *const DRETag = "DRETag"; + static constexpr const char *const RHSLiteralTag = "RHSLiteralTag"; + const DeclRefExpr *DRE = nullptr; + const IntegerLiteral *RHSLiteral = nullptr; + +public: + PointerArithmeticFixableGadget(const MatchFinder::MatchResult &Result) + : FixableGadget(Kind::PointerCtxAccess), + DRE(Result.Nodes.getNodeAs(DRETag)), + RHSLiteral(Result.Nodes.getNodeAs(RHSLiteralTag)) { + assert(DRE != nullptr && "Expecting a non-null matching result"); + } + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::PointerCtxAccess; + } + + static Matcher matcher() { + auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); + auto target = expr( + ignoringParenImpCasts( + binaryOperator( + hasOperatorName("+"), + hasLHS( + expr( + hasPointerType(), + ignoringImpCasts( + declRefExpr().bind(DRETag)))), + hasRHS( + anyOf( + integerLiteral().bind(RHSLiteralTag), + expr( + hasType(isUnsignedInteger()) + ) + ) + )))); + return stmt(isInUnspecifiedPointerContext(target)); + } + + virtual std::optional getFixits(const Strategy &S) const override; + + virtual const Stmt *getBaseStmt() const override { return DRE; } + + virtual DeclUseList getClaimedVarUseSites() const override { + return {DRE}; + } +}; } // namespace namespace { @@ -1185,6 +1235,23 @@ return std::nullopt; } +std::optional PointerArithmeticFixableGadget::getFixits(const Strategy &S) const { + if (const auto *VD = dyn_cast(DRE->getDecl())) { + if (S.lookup(VD) == Strategy::Kind::Span) { + ASTContext &Ctx = VD->getASTContext(); + if (RHSLiteral && RHSLiteral->getIntegerConstantExpr(Ctx)->isNegative()) + return std::nullopt; + SourceManager &SM = Ctx.getSourceManager(); + std::optional endOfOperand = + getEndCharLoc(DRE, SM, Ctx.getLangOpts()); + + return FixItList{{FixItHint::CreateInsertion( + endOfOperand.value().getLocWithOffset(1), ".data()")}}; + } + } + return std::nullopt; +} + // For a non-null initializer `Init` of `T *` type, this function returns // `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it // to output stream.