Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def =================================================================== --- clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -37,6 +37,7 @@ FIXABLE_GADGET(DerefSimplePtrArithFixable) FIXABLE_GADGET(PointerCtxAccess) FIXABLE_GADGET(PointerAssignment) +FIXABLE_GADGET(PointerInit) #undef FIXABLE_GADGET #undef WARNING_GADGET Index: clang/lib/Analysis/UnsafeBufferUsage.cpp =================================================================== --- clang/lib/Analysis/UnsafeBufferUsage.cpp +++ clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -547,6 +547,52 @@ } }; +/// A pointer initialization expression of the form: +/// \code +/// int *p = q; +/// \endcode +class PointerInitGadget : public FixableGadget { +private: + static constexpr const char *const PointerInitTag = "ptrInit"; + static constexpr const char *const PointerInitLHSTag = "ptrInitLHS"; + static constexpr const char *const PointerInitRHSTag = "ptrInitRHS"; + const DeclStmt *PI; // pointer arithmetic expression + const VarDecl * PtrInitLHS; // the LHS pointer expression in `PI` + const DeclRefExpr * PtrInitRHS; // the RHS pointer expression in `PI` + +public: + PointerInitGadget(const MatchFinder::MatchResult &Result) + : FixableGadget(Kind::PointerInit), + PI(Result.Nodes.getNodeAs(PointerInitTag)), + PtrInitLHS(Result.Nodes.getNodeAs(PointerInitLHSTag)), + PtrInitRHS(Result.Nodes.getNodeAs(PointerInitRHSTag)) {} + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::PointerInit; + } + + static Matcher matcher() { + auto PtrInitStmt = declStmt(hasSingleDecl(varDecl( hasInitializer(ignoringImpCasts(declRefExpr(hasPointerType()).bind(PointerInitRHSTag)))).bind(PointerInitLHSTag))); + + return PtrInitStmt; + } + + virtual std::optional getFixits(const Strategy &S) const override; + + virtual const Stmt *getBaseStmt() const override { return PI; } + + virtual DeclUseList getClaimedVarUseSites() const override { +// return DeclUseList{PtrInitLHS, PtrInitRHS}; + return DeclUseList{PtrInitRHS}; + } + + virtual std::optional> + getStrategyImplications() const override { + return std::make_pair(PtrInitLHS, + cast(PtrInitRHS->getDecl())); + } +}; + /// A pointer assignment expression of the form: /// \code /// p = q; @@ -579,8 +625,7 @@ hasLHS(declRefExpr(hasPointerType(), to(varDecl())). bind(PointerAssignLHSTag))); - - //FIXME: Handle declarations at assignments + auto PtrInitExpr = declRefExpr(to(varDecl( hasInitializer(ignoringImpCasts(declRefExpr(hasType(hasCanonicalType(pointerType()))).bind(PointerAssignRHSTag)))).bind(PointerAssignLHSTag))); return stmt(binaryOperator(PtrAssignExpr)); } @@ -682,8 +727,8 @@ namespace { // An auxiliary tracking facility for the fixit analysis. It helps connect -// declarations to its and make sure we've covered all uses with our analysis -// before we try to fix the declaration. +// declarations to its uses and make sure we've covered all uses with our +// analysis before we try to fix the declaration. class DeclUseTracker { using UseSetTy = SmallSet; using DefMapTy = DenseMap; @@ -1140,6 +1185,25 @@ return std::nullopt; } +std::optional +PointerInitGadget::getFixits(const Strategy &S) const { + const auto *LeftVD = PtrInitLHS; + const auto *RightVD = cast(PtrInitRHS->getDecl()); + switch (S.lookup(LeftVD)) { + case Strategy::Kind::Span: + if (S.lookup(RightVD) == Strategy::Kind::Span) + return FixItList{}; + return std::nullopt; + case Strategy::Kind::Wontfix: + return std::nullopt; + case Strategy::Kind::Iterator: + case Strategy::Kind::Array: + case Strategy::Kind::Vector: + llvm_unreachable("unsupported strategies for FixableGadgets"); + } + return std::nullopt; +} + // Return the text representation of the given `APInt Val`: static std::string getAPIntText(APInt Val) { SmallVector Txt; @@ -1561,16 +1625,16 @@ const DeclUseTracker &Tracker, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler) { - const DeclStmt *DS = Tracker.lookupDecl(VD); - assert(DS && "Fixing non-local variables not implemented yet!"); - if (!DS->isSingleDecl()) { - // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` - return {}; - } - // Currently DS is an unused variable but we'll need it when - // non-single decls are implemented, where the pointee type name - // and the '*' are spread around the place. - (void)DS; +// const DeclStmt *DS = Tracker.lookupDecl(VD); +// assert(DS && "Fixing non-local variables not implemented yet!"); +// if (!DS->isSingleDecl()) { +// // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` +// return {}; +// } +// // Currently DS is an unused variable but we'll need it when +// // non-single decls are implemented, where the pointee type name +// // and the '*' are spread around the place. +// (void)DS; // FIXME: handle cases where DS has multiple declarations return fixVarDeclWithSpan(VD, Ctx, Handler.getUserFillPlaceHolder()); Index: clang/test/SemaCXX/debug_crash.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/debug_crash.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s + +void rhs_span1() { + int *q = new int[12]; + int *p = q; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information$}}}} + p[5] = 10; // expected-note{{used in buffer access here}} +} Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp =================================================================== --- clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp +++ clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp @@ -39,16 +39,6 @@ p = q; } - -// FIXME: Support initializations at declarations. -void lhs_span_multi_assign() { - int *a = new int[2]; - int *b = a; - int *c = b; - int *d = c; // expected-warning{{'d' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'd' to 'std::span' to preserve bounds information$}}}} - int tmp = d[2]; // expected-note{{used in buffer access here}} -} - void rhs_span() { int *x = new int[3]; int *y; // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'y' to 'std::span' to preserve bounds information$}}}} @@ -57,33 +47,6 @@ x = y; } -void rhs_span1() { - int *q = new int[12]; - int *p = q; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information$}}}} - p[5] = 10; // expected-note{{used in buffer access here}} - int *r = q; // expected-warning{{'r' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'r' to 'std::span' to preserve bounds information$}}}} - r[10] = 5; // expected-note{{used in buffer access here}} -} - -void rhs_span2() { - int *q = new int[6]; - int *p = q; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information$}}}} - p[5] = 10; // expected-note{{used in buffer access here}} - int *r = q; -} - -void test_grouping() { - int *z = new int[8]; - int tmp; - int *y = new int[10]; // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'y' to 'std::span' to preserve bounds information$}}}} - tmp = y[5]; // expected-note{{used in buffer access here}} - - int *x = new int[10]; - x = y; - - int *w = z; -} - void test_grouping1() { int tmp; int *y = new int[10]; // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'y' to 'std::span' to preserve bounds information$}}}} @@ -172,15 +135,7 @@ q = r; } -void test_crash() { - int *r = new int[8]; - int *q = r; - int *p; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'q' \(to propagate bounds information between them\) to 'std::span'$}}}} - p = q; - int tmp = p[9]; // expected-note{{used in buffer access here}} -} - -void foo_uuc() { +void foo_not_uuc() { int *ptr; int *local; // expected-warning{{'local' is an unsafe pointer used for buffer access}} local = ptr;