1*bdd1243dSDimitry Andric //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===// 2*bdd1243dSDimitry Andric // 3*bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*bdd1243dSDimitry Andric // 7*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8*bdd1243dSDimitry Andric 9*bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsage.h" 10*bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 11*bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 12*bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h" 13*bdd1243dSDimitry Andric #include <memory> 14*bdd1243dSDimitry Andric #include <optional> 15*bdd1243dSDimitry Andric 16*bdd1243dSDimitry Andric using namespace llvm; 17*bdd1243dSDimitry Andric using namespace clang; 18*bdd1243dSDimitry Andric using namespace ast_matchers; 19*bdd1243dSDimitry Andric 20*bdd1243dSDimitry Andric namespace clang::ast_matchers { 21*bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n" 22*bdd1243dSDimitry Andric // except for those belonging to a different callable of "n". 23*bdd1243dSDimitry Andric class MatchDescendantVisitor 24*bdd1243dSDimitry Andric : public RecursiveASTVisitor<MatchDescendantVisitor> { 25*bdd1243dSDimitry Andric public: 26*bdd1243dSDimitry Andric typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase; 27*bdd1243dSDimitry Andric 28*bdd1243dSDimitry Andric // Creates an AST visitor that matches `Matcher` on all 29*bdd1243dSDimitry Andric // descendants of a given node "n" except for the ones 30*bdd1243dSDimitry Andric // belonging to a different callable of "n". 31*bdd1243dSDimitry Andric MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, 32*bdd1243dSDimitry Andric internal::ASTMatchFinder *Finder, 33*bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *Builder, 34*bdd1243dSDimitry Andric internal::ASTMatchFinder::BindKind Bind) 35*bdd1243dSDimitry Andric : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind), 36*bdd1243dSDimitry Andric Matches(false) {} 37*bdd1243dSDimitry Andric 38*bdd1243dSDimitry Andric // Returns true if a match is found in a subtree of `DynNode`, which belongs 39*bdd1243dSDimitry Andric // to the same callable of `DynNode`. 40*bdd1243dSDimitry Andric bool findMatch(const DynTypedNode &DynNode) { 41*bdd1243dSDimitry Andric Matches = false; 42*bdd1243dSDimitry Andric if (const Stmt *StmtNode = DynNode.get<Stmt>()) { 43*bdd1243dSDimitry Andric TraverseStmt(const_cast<Stmt *>(StmtNode)); 44*bdd1243dSDimitry Andric *Builder = ResultBindings; 45*bdd1243dSDimitry Andric return Matches; 46*bdd1243dSDimitry Andric } 47*bdd1243dSDimitry Andric return false; 48*bdd1243dSDimitry Andric } 49*bdd1243dSDimitry Andric 50*bdd1243dSDimitry Andric // The following are overriding methods from the base visitor class. 51*bdd1243dSDimitry Andric // They are public only to allow CRTP to work. They are *not *part 52*bdd1243dSDimitry Andric // of the public API of this class. 53*bdd1243dSDimitry Andric 54*bdd1243dSDimitry Andric // For the matchers so far used in safe buffers, we only need to match 55*bdd1243dSDimitry Andric // `Stmt`s. To override more as needed. 56*bdd1243dSDimitry Andric 57*bdd1243dSDimitry Andric bool TraverseDecl(Decl *Node) { 58*bdd1243dSDimitry Andric if (!Node) 59*bdd1243dSDimitry Andric return true; 60*bdd1243dSDimitry Andric if (!match(*Node)) 61*bdd1243dSDimitry Andric return false; 62*bdd1243dSDimitry Andric // To skip callables: 63*bdd1243dSDimitry Andric if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node)) 64*bdd1243dSDimitry Andric return true; 65*bdd1243dSDimitry Andric // Traverse descendants 66*bdd1243dSDimitry Andric return VisitorBase::TraverseDecl(Node); 67*bdd1243dSDimitry Andric } 68*bdd1243dSDimitry Andric 69*bdd1243dSDimitry Andric bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) { 70*bdd1243dSDimitry Andric if (!Node) 71*bdd1243dSDimitry Andric return true; 72*bdd1243dSDimitry Andric if (!match(*Node)) 73*bdd1243dSDimitry Andric return false; 74*bdd1243dSDimitry Andric // To skip callables: 75*bdd1243dSDimitry Andric if (isa<LambdaExpr>(Node)) 76*bdd1243dSDimitry Andric return true; 77*bdd1243dSDimitry Andric return VisitorBase::TraverseStmt(Node); 78*bdd1243dSDimitry Andric } 79*bdd1243dSDimitry Andric 80*bdd1243dSDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; } 81*bdd1243dSDimitry Andric bool shouldVisitImplicitCode() const { 82*bdd1243dSDimitry Andric // TODO: let's ignore implicit code for now 83*bdd1243dSDimitry Andric return false; 84*bdd1243dSDimitry Andric } 85*bdd1243dSDimitry Andric 86*bdd1243dSDimitry Andric private: 87*bdd1243dSDimitry Andric // Sets 'Matched' to true if 'Matcher' matches 'Node' 88*bdd1243dSDimitry Andric // 89*bdd1243dSDimitry Andric // Returns 'true' if traversal should continue after this function 90*bdd1243dSDimitry Andric // returns, i.e. if no match is found or 'Bind' is 'BK_All'. 91*bdd1243dSDimitry Andric template <typename T> bool match(const T &Node) { 92*bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder); 93*bdd1243dSDimitry Andric 94*bdd1243dSDimitry Andric if (Matcher->matches(DynTypedNode::create(Node), Finder, 95*bdd1243dSDimitry Andric &RecursiveBuilder)) { 96*bdd1243dSDimitry Andric ResultBindings.addMatch(RecursiveBuilder); 97*bdd1243dSDimitry Andric Matches = true; 98*bdd1243dSDimitry Andric if (Bind != internal::ASTMatchFinder::BK_All) 99*bdd1243dSDimitry Andric return false; // Abort as soon as a match is found. 100*bdd1243dSDimitry Andric } 101*bdd1243dSDimitry Andric return true; 102*bdd1243dSDimitry Andric } 103*bdd1243dSDimitry Andric 104*bdd1243dSDimitry Andric const internal::DynTypedMatcher *const Matcher; 105*bdd1243dSDimitry Andric internal::ASTMatchFinder *const Finder; 106*bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *const Builder; 107*bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder ResultBindings; 108*bdd1243dSDimitry Andric const internal::ASTMatchFinder::BindKind Bind; 109*bdd1243dSDimitry Andric bool Matches; 110*bdd1243dSDimitry Andric }; 111*bdd1243dSDimitry Andric 112*bdd1243dSDimitry Andric AST_MATCHER_P(Stmt, forEveryDescendant, internal::Matcher<Stmt>, innerMatcher) { 113*bdd1243dSDimitry Andric const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 114*bdd1243dSDimitry Andric 115*bdd1243dSDimitry Andric MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All); 116*bdd1243dSDimitry Andric return Visitor.findMatch(DynTypedNode::create(Node)); 117*bdd1243dSDimitry Andric } 118*bdd1243dSDimitry Andric } // namespace clang::ast_matchers 119*bdd1243dSDimitry Andric 120*bdd1243dSDimitry Andric namespace { 121*bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to 122*bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs). 123*bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>; 124*bdd1243dSDimitry Andric 125*bdd1243dSDimitry Andric // Convenience typedef. 126*bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>; 127*bdd1243dSDimitry Andric 128*bdd1243dSDimitry Andric // Defined below. 129*bdd1243dSDimitry Andric class Strategy; 130*bdd1243dSDimitry Andric } // namespace 131*bdd1243dSDimitry Andric 132*bdd1243dSDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that. 133*bdd1243dSDimitry Andric static auto hasPointerType() { 134*bdd1243dSDimitry Andric return hasType(hasCanonicalType(pointerType())); 135*bdd1243dSDimitry Andric } 136*bdd1243dSDimitry Andric 137*bdd1243dSDimitry Andric static auto hasArrayType() { 138*bdd1243dSDimitry Andric return hasType(hasCanonicalType(arrayType())); 139*bdd1243dSDimitry Andric } 140*bdd1243dSDimitry Andric 141*bdd1243dSDimitry Andric namespace { 142*bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to 143*bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific 144*bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object. 145*bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand 146*bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it. 147*bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not 148*bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify 149*bdd1243dSDimitry Andric /// it warrants a fixit). 150*bdd1243dSDimitry Andric class Gadget { 151*bdd1243dSDimitry Andric public: 152*bdd1243dSDimitry Andric enum class Kind { 153*bdd1243dSDimitry Andric #define GADGET(x) x, 154*bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 155*bdd1243dSDimitry Andric }; 156*bdd1243dSDimitry Andric 157*bdd1243dSDimitry Andric /// Common type of ASTMatchers used for discovering gadgets. 158*bdd1243dSDimitry Andric /// Useful for implementing the static matcher() methods 159*bdd1243dSDimitry Andric /// that are expected from all non-abstract subclasses. 160*bdd1243dSDimitry Andric using Matcher = decltype(stmt()); 161*bdd1243dSDimitry Andric 162*bdd1243dSDimitry Andric Gadget(Kind K) : K(K) {} 163*bdd1243dSDimitry Andric 164*bdd1243dSDimitry Andric Kind getKind() const { return K; } 165*bdd1243dSDimitry Andric 166*bdd1243dSDimitry Andric virtual bool isWarningGadget() const = 0; 167*bdd1243dSDimitry Andric virtual const Stmt *getBaseStmt() const = 0; 168*bdd1243dSDimitry Andric 169*bdd1243dSDimitry Andric /// Returns the list of pointer-type variables on which this gadget performs 170*bdd1243dSDimitry Andric /// its operation. Typically, there's only one variable. This isn't a list 171*bdd1243dSDimitry Andric /// of all DeclRefExprs in the gadget's AST! 172*bdd1243dSDimitry Andric virtual DeclUseList getClaimedVarUseSites() const = 0; 173*bdd1243dSDimitry Andric 174*bdd1243dSDimitry Andric virtual ~Gadget() = default; 175*bdd1243dSDimitry Andric 176*bdd1243dSDimitry Andric private: 177*bdd1243dSDimitry Andric Kind K; 178*bdd1243dSDimitry Andric }; 179*bdd1243dSDimitry Andric 180*bdd1243dSDimitry Andric 181*bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants 182*bdd1243dSDimitry Andric /// an immediate warning. 183*bdd1243dSDimitry Andric class WarningGadget : public Gadget { 184*bdd1243dSDimitry Andric public: 185*bdd1243dSDimitry Andric WarningGadget(Kind K) : Gadget(K) {} 186*bdd1243dSDimitry Andric 187*bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return G->isWarningGadget(); } 188*bdd1243dSDimitry Andric bool isWarningGadget() const final { return true; } 189*bdd1243dSDimitry Andric }; 190*bdd1243dSDimitry Andric 191*bdd1243dSDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but need to be 192*bdd1243dSDimitry Andric /// properly recognized in order to emit fixes. For example, if a raw pointer-type 193*bdd1243dSDimitry Andric /// variable is replaced by a safe C++ container, every use of such variable must be 194*bdd1243dSDimitry Andric /// carefully considered and possibly updated. 195*bdd1243dSDimitry Andric class FixableGadget : public Gadget { 196*bdd1243dSDimitry Andric public: 197*bdd1243dSDimitry Andric FixableGadget(Kind K) : Gadget(K) {} 198*bdd1243dSDimitry Andric 199*bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return !G->isWarningGadget(); } 200*bdd1243dSDimitry Andric bool isWarningGadget() const final { return false; } 201*bdd1243dSDimitry Andric 202*bdd1243dSDimitry Andric /// Returns a fixit that would fix the current gadget according to 203*bdd1243dSDimitry Andric /// the current strategy. Returns None if the fix cannot be produced; 204*bdd1243dSDimitry Andric /// returns an empty list if no fixes are necessary. 205*bdd1243dSDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &) const { 206*bdd1243dSDimitry Andric return std::nullopt; 207*bdd1243dSDimitry Andric } 208*bdd1243dSDimitry Andric }; 209*bdd1243dSDimitry Andric 210*bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>; 211*bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>; 212*bdd1243dSDimitry Andric 213*bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer 214*bdd1243dSDimitry Andric /// out of bounds. 215*bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget { 216*bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 217*bdd1243dSDimitry Andric const UnaryOperator *Op; 218*bdd1243dSDimitry Andric 219*bdd1243dSDimitry Andric public: 220*bdd1243dSDimitry Andric IncrementGadget(const MatchFinder::MatchResult &Result) 221*bdd1243dSDimitry Andric : WarningGadget(Kind::Increment), 222*bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 223*bdd1243dSDimitry Andric 224*bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 225*bdd1243dSDimitry Andric return G->getKind() == Kind::Increment; 226*bdd1243dSDimitry Andric } 227*bdd1243dSDimitry Andric 228*bdd1243dSDimitry Andric static Matcher matcher() { 229*bdd1243dSDimitry Andric return stmt(unaryOperator( 230*bdd1243dSDimitry Andric hasOperatorName("++"), 231*bdd1243dSDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType())) 232*bdd1243dSDimitry Andric ).bind(OpTag)); 233*bdd1243dSDimitry Andric } 234*bdd1243dSDimitry Andric 235*bdd1243dSDimitry Andric const UnaryOperator *getBaseStmt() const override { return Op; } 236*bdd1243dSDimitry Andric 237*bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 238*bdd1243dSDimitry Andric SmallVector<const DeclRefExpr *, 2> Uses; 239*bdd1243dSDimitry Andric if (const auto *DRE = 240*bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 241*bdd1243dSDimitry Andric Uses.push_back(DRE); 242*bdd1243dSDimitry Andric } 243*bdd1243dSDimitry Andric 244*bdd1243dSDimitry Andric return std::move(Uses); 245*bdd1243dSDimitry Andric } 246*bdd1243dSDimitry Andric }; 247*bdd1243dSDimitry Andric 248*bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer 249*bdd1243dSDimitry Andric /// out of bounds. 250*bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget { 251*bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 252*bdd1243dSDimitry Andric const UnaryOperator *Op; 253*bdd1243dSDimitry Andric 254*bdd1243dSDimitry Andric public: 255*bdd1243dSDimitry Andric DecrementGadget(const MatchFinder::MatchResult &Result) 256*bdd1243dSDimitry Andric : WarningGadget(Kind::Decrement), 257*bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 258*bdd1243dSDimitry Andric 259*bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 260*bdd1243dSDimitry Andric return G->getKind() == Kind::Decrement; 261*bdd1243dSDimitry Andric } 262*bdd1243dSDimitry Andric 263*bdd1243dSDimitry Andric static Matcher matcher() { 264*bdd1243dSDimitry Andric return stmt(unaryOperator( 265*bdd1243dSDimitry Andric hasOperatorName("--"), 266*bdd1243dSDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType())) 267*bdd1243dSDimitry Andric ).bind(OpTag)); 268*bdd1243dSDimitry Andric } 269*bdd1243dSDimitry Andric 270*bdd1243dSDimitry Andric const UnaryOperator *getBaseStmt() const override { return Op; } 271*bdd1243dSDimitry Andric 272*bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 273*bdd1243dSDimitry Andric if (const auto *DRE = 274*bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 275*bdd1243dSDimitry Andric return {DRE}; 276*bdd1243dSDimitry Andric } 277*bdd1243dSDimitry Andric 278*bdd1243dSDimitry Andric return {}; 279*bdd1243dSDimitry Andric } 280*bdd1243dSDimitry Andric }; 281*bdd1243dSDimitry Andric 282*bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as 283*bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array. 284*bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget { 285*bdd1243dSDimitry Andric static constexpr const char *const ArraySubscrTag = "arraySubscr"; 286*bdd1243dSDimitry Andric const ArraySubscriptExpr *ASE; 287*bdd1243dSDimitry Andric 288*bdd1243dSDimitry Andric public: 289*bdd1243dSDimitry Andric ArraySubscriptGadget(const MatchFinder::MatchResult &Result) 290*bdd1243dSDimitry Andric : WarningGadget(Kind::ArraySubscript), 291*bdd1243dSDimitry Andric ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {} 292*bdd1243dSDimitry Andric 293*bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 294*bdd1243dSDimitry Andric return G->getKind() == Kind::ArraySubscript; 295*bdd1243dSDimitry Andric } 296*bdd1243dSDimitry Andric 297*bdd1243dSDimitry Andric static Matcher matcher() { 298*bdd1243dSDimitry Andric // FIXME: What if the index is integer literal 0? Should this be 299*bdd1243dSDimitry Andric // a safe gadget in this case? 300*bdd1243dSDimitry Andric // clang-format off 301*bdd1243dSDimitry Andric return stmt(arraySubscriptExpr( 302*bdd1243dSDimitry Andric hasBase(ignoringParenImpCasts( 303*bdd1243dSDimitry Andric anyOf(hasPointerType(), hasArrayType()))), 304*bdd1243dSDimitry Andric unless(hasIndex(integerLiteral(equals(0))))) 305*bdd1243dSDimitry Andric .bind(ArraySubscrTag)); 306*bdd1243dSDimitry Andric // clang-format on 307*bdd1243dSDimitry Andric } 308*bdd1243dSDimitry Andric 309*bdd1243dSDimitry Andric const ArraySubscriptExpr *getBaseStmt() const override { return ASE; } 310*bdd1243dSDimitry Andric 311*bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 312*bdd1243dSDimitry Andric if (const auto *DRE = 313*bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) { 314*bdd1243dSDimitry Andric return {DRE}; 315*bdd1243dSDimitry Andric } 316*bdd1243dSDimitry Andric 317*bdd1243dSDimitry Andric return {}; 318*bdd1243dSDimitry Andric } 319*bdd1243dSDimitry Andric }; 320*bdd1243dSDimitry Andric 321*bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms: 322*bdd1243dSDimitry Andric /// \code 323*bdd1243dSDimitry Andric /// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n 324*bdd1243dSDimitry Andric /// \endcode 325*bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget { 326*bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticTag = "ptrAdd"; 327*bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr"; 328*bdd1243dSDimitry Andric const BinaryOperator *PA; // pointer arithmetic expression 329*bdd1243dSDimitry Andric const Expr * Ptr; // the pointer expression in `PA` 330*bdd1243dSDimitry Andric 331*bdd1243dSDimitry Andric public: 332*bdd1243dSDimitry Andric PointerArithmeticGadget(const MatchFinder::MatchResult &Result) 333*bdd1243dSDimitry Andric : WarningGadget(Kind::PointerArithmetic), 334*bdd1243dSDimitry Andric PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)), 335*bdd1243dSDimitry Andric Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {} 336*bdd1243dSDimitry Andric 337*bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 338*bdd1243dSDimitry Andric return G->getKind() == Kind::PointerArithmetic; 339*bdd1243dSDimitry Andric } 340*bdd1243dSDimitry Andric 341*bdd1243dSDimitry Andric static Matcher matcher() { 342*bdd1243dSDimitry Andric auto HasIntegerType = anyOf( 343*bdd1243dSDimitry Andric hasType(isInteger()), hasType(enumType())); 344*bdd1243dSDimitry Andric auto PtrAtRight = allOf(hasOperatorName("+"), 345*bdd1243dSDimitry Andric hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 346*bdd1243dSDimitry Andric hasLHS(HasIntegerType)); 347*bdd1243dSDimitry Andric auto PtrAtLeft = allOf( 348*bdd1243dSDimitry Andric anyOf(hasOperatorName("+"), hasOperatorName("-"), 349*bdd1243dSDimitry Andric hasOperatorName("+="), hasOperatorName("-=")), 350*bdd1243dSDimitry Andric hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 351*bdd1243dSDimitry Andric hasRHS(HasIntegerType)); 352*bdd1243dSDimitry Andric 353*bdd1243dSDimitry Andric return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight)).bind(PointerArithmeticTag)); 354*bdd1243dSDimitry Andric } 355*bdd1243dSDimitry Andric 356*bdd1243dSDimitry Andric const Stmt *getBaseStmt() const override { return PA; } 357*bdd1243dSDimitry Andric 358*bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 359*bdd1243dSDimitry Andric if (const auto *DRE = 360*bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) { 361*bdd1243dSDimitry Andric return {DRE}; 362*bdd1243dSDimitry Andric } 363*bdd1243dSDimitry Andric 364*bdd1243dSDimitry Andric return {}; 365*bdd1243dSDimitry Andric } 366*bdd1243dSDimitry Andric // FIXME: pointer adding zero should be fine 367*bdd1243dSDimitry Andric //FIXME: this gadge will need a fix-it 368*bdd1243dSDimitry Andric }; 369*bdd1243dSDimitry Andric } // namespace 370*bdd1243dSDimitry Andric 371*bdd1243dSDimitry Andric namespace { 372*bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect 373*bdd1243dSDimitry Andric // declarations to its and make sure we've covered all uses with our analysis 374*bdd1243dSDimitry Andric // before we try to fix the declaration. 375*bdd1243dSDimitry Andric class DeclUseTracker { 376*bdd1243dSDimitry Andric using UseSetTy = SmallSet<const DeclRefExpr *, 16>; 377*bdd1243dSDimitry Andric using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>; 378*bdd1243dSDimitry Andric 379*bdd1243dSDimitry Andric // Allocate on the heap for easier move. 380*bdd1243dSDimitry Andric std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()}; 381*bdd1243dSDimitry Andric DefMapTy Defs{}; 382*bdd1243dSDimitry Andric 383*bdd1243dSDimitry Andric public: 384*bdd1243dSDimitry Andric DeclUseTracker() = default; 385*bdd1243dSDimitry Andric DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies. 386*bdd1243dSDimitry Andric DeclUseTracker(DeclUseTracker &&) = default; 387*bdd1243dSDimitry Andric DeclUseTracker &operator=(DeclUseTracker &&) = default; 388*bdd1243dSDimitry Andric 389*bdd1243dSDimitry Andric // Start tracking a freshly discovered DRE. 390*bdd1243dSDimitry Andric void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); } 391*bdd1243dSDimitry Andric 392*bdd1243dSDimitry Andric // Stop tracking the DRE as it's been fully figured out. 393*bdd1243dSDimitry Andric void claimUse(const DeclRefExpr *DRE) { 394*bdd1243dSDimitry Andric assert(Uses->count(DRE) && 395*bdd1243dSDimitry Andric "DRE not found or claimed by multiple matchers!"); 396*bdd1243dSDimitry Andric Uses->erase(DRE); 397*bdd1243dSDimitry Andric } 398*bdd1243dSDimitry Andric 399*bdd1243dSDimitry Andric // A variable is unclaimed if at least one use is unclaimed. 400*bdd1243dSDimitry Andric bool hasUnclaimedUses(const VarDecl *VD) const { 401*bdd1243dSDimitry Andric // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs? 402*bdd1243dSDimitry Andric return any_of(*Uses, [VD](const DeclRefExpr *DRE) { 403*bdd1243dSDimitry Andric return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl(); 404*bdd1243dSDimitry Andric }); 405*bdd1243dSDimitry Andric } 406*bdd1243dSDimitry Andric 407*bdd1243dSDimitry Andric void discoverDecl(const DeclStmt *DS) { 408*bdd1243dSDimitry Andric for (const Decl *D : DS->decls()) { 409*bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(D)) { 410*bdd1243dSDimitry Andric // FIXME: Assertion temporarily disabled due to a bug in 411*bdd1243dSDimitry Andric // ASTMatcher internal behavior in presence of GNU 412*bdd1243dSDimitry Andric // statement-expressions. We need to properly investigate this 413*bdd1243dSDimitry Andric // because it can screw up our algorithm in other ways. 414*bdd1243dSDimitry Andric // assert(Defs.count(VD) == 0 && "Definition already discovered!"); 415*bdd1243dSDimitry Andric Defs[VD] = DS; 416*bdd1243dSDimitry Andric } 417*bdd1243dSDimitry Andric } 418*bdd1243dSDimitry Andric } 419*bdd1243dSDimitry Andric 420*bdd1243dSDimitry Andric const DeclStmt *lookupDecl(const VarDecl *VD) const { 421*bdd1243dSDimitry Andric auto It = Defs.find(VD); 422*bdd1243dSDimitry Andric assert(It != Defs.end() && "Definition never discovered!"); 423*bdd1243dSDimitry Andric return It->second; 424*bdd1243dSDimitry Andric } 425*bdd1243dSDimitry Andric }; 426*bdd1243dSDimitry Andric } // namespace 427*bdd1243dSDimitry Andric 428*bdd1243dSDimitry Andric namespace { 429*bdd1243dSDimitry Andric // Strategy is a map from variables to the way we plan to emit fixes for 430*bdd1243dSDimitry Andric // these variables. It is figured out gradually by trying different fixes 431*bdd1243dSDimitry Andric // for different variables depending on gadgets in which these variables 432*bdd1243dSDimitry Andric // participate. 433*bdd1243dSDimitry Andric class Strategy { 434*bdd1243dSDimitry Andric public: 435*bdd1243dSDimitry Andric enum class Kind { 436*bdd1243dSDimitry Andric Wontfix, // We don't plan to emit a fixit for this variable. 437*bdd1243dSDimitry Andric Span, // We recommend replacing the variable with std::span. 438*bdd1243dSDimitry Andric Iterator, // We recommend replacing the variable with std::span::iterator. 439*bdd1243dSDimitry Andric Array, // We recommend replacing the variable with std::array. 440*bdd1243dSDimitry Andric Vector // We recommend replacing the variable with std::vector. 441*bdd1243dSDimitry Andric }; 442*bdd1243dSDimitry Andric 443*bdd1243dSDimitry Andric private: 444*bdd1243dSDimitry Andric using MapTy = llvm::DenseMap<const VarDecl *, Kind>; 445*bdd1243dSDimitry Andric 446*bdd1243dSDimitry Andric MapTy Map; 447*bdd1243dSDimitry Andric 448*bdd1243dSDimitry Andric public: 449*bdd1243dSDimitry Andric Strategy() = default; 450*bdd1243dSDimitry Andric Strategy(const Strategy &) = delete; // Let's avoid copies. 451*bdd1243dSDimitry Andric Strategy(Strategy &&) = default; 452*bdd1243dSDimitry Andric 453*bdd1243dSDimitry Andric void set(const VarDecl *VD, Kind K) { 454*bdd1243dSDimitry Andric Map[VD] = K; 455*bdd1243dSDimitry Andric } 456*bdd1243dSDimitry Andric 457*bdd1243dSDimitry Andric Kind lookup(const VarDecl *VD) const { 458*bdd1243dSDimitry Andric auto I = Map.find(VD); 459*bdd1243dSDimitry Andric if (I == Map.end()) 460*bdd1243dSDimitry Andric return Kind::Wontfix; 461*bdd1243dSDimitry Andric 462*bdd1243dSDimitry Andric return I->second; 463*bdd1243dSDimitry Andric } 464*bdd1243dSDimitry Andric }; 465*bdd1243dSDimitry Andric } // namespace 466*bdd1243dSDimitry Andric 467*bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits. 468*bdd1243dSDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker> findGadgets(const Decl *D) { 469*bdd1243dSDimitry Andric 470*bdd1243dSDimitry Andric struct GadgetFinderCallback : MatchFinder::MatchCallback { 471*bdd1243dSDimitry Andric FixableGadgetList FixableGadgets; 472*bdd1243dSDimitry Andric WarningGadgetList WarningGadgets; 473*bdd1243dSDimitry Andric DeclUseTracker Tracker; 474*bdd1243dSDimitry Andric 475*bdd1243dSDimitry Andric void run(const MatchFinder::MatchResult &Result) override { 476*bdd1243dSDimitry Andric // In debug mode, assert that we've found exactly one gadget. 477*bdd1243dSDimitry Andric // This helps us avoid conflicts in .bind() tags. 478*bdd1243dSDimitry Andric #if NDEBUG 479*bdd1243dSDimitry Andric #define NEXT return 480*bdd1243dSDimitry Andric #else 481*bdd1243dSDimitry Andric [[maybe_unused]] int numFound = 0; 482*bdd1243dSDimitry Andric #define NEXT ++numFound 483*bdd1243dSDimitry Andric #endif 484*bdd1243dSDimitry Andric 485*bdd1243dSDimitry Andric if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) { 486*bdd1243dSDimitry Andric Tracker.discoverUse(DRE); 487*bdd1243dSDimitry Andric NEXT; 488*bdd1243dSDimitry Andric } 489*bdd1243dSDimitry Andric 490*bdd1243dSDimitry Andric if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) { 491*bdd1243dSDimitry Andric Tracker.discoverDecl(DS); 492*bdd1243dSDimitry Andric NEXT; 493*bdd1243dSDimitry Andric } 494*bdd1243dSDimitry Andric 495*bdd1243dSDimitry Andric // Figure out which matcher we've found, and call the appropriate 496*bdd1243dSDimitry Andric // subclass constructor. 497*bdd1243dSDimitry Andric // FIXME: Can we do this more logarithmically? 498*bdd1243dSDimitry Andric #define FIXABLE_GADGET(name) \ 499*bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 500*bdd1243dSDimitry Andric FixableGadgets.push_back(std::make_unique<name ## Gadget>(Result)); \ 501*bdd1243dSDimitry Andric NEXT; \ 502*bdd1243dSDimitry Andric } 503*bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 504*bdd1243dSDimitry Andric #define WARNING_GADGET(name) \ 505*bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 506*bdd1243dSDimitry Andric WarningGadgets.push_back(std::make_unique<name ## Gadget>(Result)); \ 507*bdd1243dSDimitry Andric NEXT; \ 508*bdd1243dSDimitry Andric } 509*bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 510*bdd1243dSDimitry Andric 511*bdd1243dSDimitry Andric assert(numFound >= 1 && "Gadgets not found in match result!"); 512*bdd1243dSDimitry Andric assert(numFound <= 1 && "Conflicting bind tags in gadgets!"); 513*bdd1243dSDimitry Andric } 514*bdd1243dSDimitry Andric }; 515*bdd1243dSDimitry Andric 516*bdd1243dSDimitry Andric MatchFinder M; 517*bdd1243dSDimitry Andric GadgetFinderCallback CB; 518*bdd1243dSDimitry Andric 519*bdd1243dSDimitry Andric // clang-format off 520*bdd1243dSDimitry Andric M.addMatcher( 521*bdd1243dSDimitry Andric stmt(forEveryDescendant( 522*bdd1243dSDimitry Andric stmt(anyOf( 523*bdd1243dSDimitry Andric // Add Gadget::matcher() for every gadget in the registry. 524*bdd1243dSDimitry Andric #define GADGET(x) \ 525*bdd1243dSDimitry Andric x ## Gadget::matcher().bind(#x), 526*bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 527*bdd1243dSDimitry Andric // In parallel, match all DeclRefExprs so that to find out 528*bdd1243dSDimitry Andric // whether there are any uncovered by gadgets. 529*bdd1243dSDimitry Andric declRefExpr(anyOf(hasPointerType(), hasArrayType()), 530*bdd1243dSDimitry Andric to(varDecl())).bind("any_dre"), 531*bdd1243dSDimitry Andric // Also match DeclStmts because we'll need them when fixing 532*bdd1243dSDimitry Andric // their underlying VarDecls that otherwise don't have 533*bdd1243dSDimitry Andric // any backreferences to DeclStmts. 534*bdd1243dSDimitry Andric declStmt().bind("any_ds") 535*bdd1243dSDimitry Andric )) 536*bdd1243dSDimitry Andric // FIXME: Idiomatically there should be a forCallable(equalsNode(D)) 537*bdd1243dSDimitry Andric // here, to make sure that the statement actually belongs to the 538*bdd1243dSDimitry Andric // function and not to a nested function. However, forCallable uses 539*bdd1243dSDimitry Andric // ParentMap which can't be used before the AST is fully constructed. 540*bdd1243dSDimitry Andric // The original problem doesn't sound like it needs ParentMap though, 541*bdd1243dSDimitry Andric // maybe there's a more direct solution? 542*bdd1243dSDimitry Andric )), 543*bdd1243dSDimitry Andric &CB 544*bdd1243dSDimitry Andric ); 545*bdd1243dSDimitry Andric // clang-format on 546*bdd1243dSDimitry Andric 547*bdd1243dSDimitry Andric M.match(*D->getBody(), D->getASTContext()); 548*bdd1243dSDimitry Andric 549*bdd1243dSDimitry Andric // Gadgets "claim" variables they're responsible for. Once this loop finishes, 550*bdd1243dSDimitry Andric // the tracker will only track DREs that weren't claimed by any gadgets, 551*bdd1243dSDimitry Andric // i.e. not understood by the analysis. 552*bdd1243dSDimitry Andric for (const auto &G : CB.FixableGadgets) { 553*bdd1243dSDimitry Andric for (const auto *DRE : G->getClaimedVarUseSites()) { 554*bdd1243dSDimitry Andric CB.Tracker.claimUse(DRE); 555*bdd1243dSDimitry Andric } 556*bdd1243dSDimitry Andric } 557*bdd1243dSDimitry Andric 558*bdd1243dSDimitry Andric return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets), std::move(CB.Tracker)}; 559*bdd1243dSDimitry Andric } 560*bdd1243dSDimitry Andric 561*bdd1243dSDimitry Andric struct WarningGadgetSets { 562*bdd1243dSDimitry Andric std::map<const VarDecl *, std::set<std::unique_ptr<WarningGadget>>> byVar; 563*bdd1243dSDimitry Andric // These Gadgets are not related to pointer variables (e. g. temporaries). 564*bdd1243dSDimitry Andric llvm::SmallVector<std::unique_ptr<WarningGadget>, 16> noVar; 565*bdd1243dSDimitry Andric }; 566*bdd1243dSDimitry Andric 567*bdd1243dSDimitry Andric static WarningGadgetSets 568*bdd1243dSDimitry Andric groupWarningGadgetsByVar(WarningGadgetList &&AllUnsafeOperations) { 569*bdd1243dSDimitry Andric WarningGadgetSets result; 570*bdd1243dSDimitry Andric // If some gadgets cover more than one 571*bdd1243dSDimitry Andric // variable, they'll appear more than once in the map. 572*bdd1243dSDimitry Andric for (auto &G : AllUnsafeOperations) { 573*bdd1243dSDimitry Andric DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites(); 574*bdd1243dSDimitry Andric 575*bdd1243dSDimitry Andric bool AssociatedWithVarDecl = false; 576*bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : ClaimedVarUseSites) { 577*bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 578*bdd1243dSDimitry Andric result.byVar[VD].emplace(std::move(G)); 579*bdd1243dSDimitry Andric AssociatedWithVarDecl = true; 580*bdd1243dSDimitry Andric } 581*bdd1243dSDimitry Andric } 582*bdd1243dSDimitry Andric 583*bdd1243dSDimitry Andric if (!AssociatedWithVarDecl) { 584*bdd1243dSDimitry Andric result.noVar.emplace_back(std::move(G)); 585*bdd1243dSDimitry Andric continue; 586*bdd1243dSDimitry Andric } 587*bdd1243dSDimitry Andric } 588*bdd1243dSDimitry Andric return result; 589*bdd1243dSDimitry Andric } 590*bdd1243dSDimitry Andric 591*bdd1243dSDimitry Andric struct FixableGadgetSets { 592*bdd1243dSDimitry Andric std::map<const VarDecl *, std::set<std::unique_ptr<FixableGadget>>> byVar; 593*bdd1243dSDimitry Andric }; 594*bdd1243dSDimitry Andric 595*bdd1243dSDimitry Andric static FixableGadgetSets 596*bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) { 597*bdd1243dSDimitry Andric FixableGadgetSets FixablesForUnsafeVars; 598*bdd1243dSDimitry Andric for (auto &F : AllFixableOperations) { 599*bdd1243dSDimitry Andric DeclUseList DREs = F->getClaimedVarUseSites(); 600*bdd1243dSDimitry Andric 601*bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : DREs) { 602*bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 603*bdd1243dSDimitry Andric FixablesForUnsafeVars.byVar[VD].emplace(std::move(F)); 604*bdd1243dSDimitry Andric } 605*bdd1243dSDimitry Andric } 606*bdd1243dSDimitry Andric } 607*bdd1243dSDimitry Andric return FixablesForUnsafeVars; 608*bdd1243dSDimitry Andric } 609*bdd1243dSDimitry Andric 610*bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList> 611*bdd1243dSDimitry Andric getFixIts(FixableGadgetSets &FixablesForUnsafeVars, const Strategy &S) { 612*bdd1243dSDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariable; 613*bdd1243dSDimitry Andric for (const auto &[VD, Fixables] : FixablesForUnsafeVars.byVar) { 614*bdd1243dSDimitry Andric // TODO fixVariable - fixit for the variable itself 615*bdd1243dSDimitry Andric bool ImpossibleToFix = false; 616*bdd1243dSDimitry Andric llvm::SmallVector<FixItHint, 16> FixItsForVD; 617*bdd1243dSDimitry Andric for (const auto &F : Fixables) { 618*bdd1243dSDimitry Andric llvm::Optional<FixItList> Fixits = F->getFixits(S); 619*bdd1243dSDimitry Andric if (!Fixits) { 620*bdd1243dSDimitry Andric ImpossibleToFix = true; 621*bdd1243dSDimitry Andric break; 622*bdd1243dSDimitry Andric } else { 623*bdd1243dSDimitry Andric const FixItList CorrectFixes = Fixits.value(); 624*bdd1243dSDimitry Andric FixItsForVD.insert(FixItsForVD.end(), CorrectFixes.begin(), 625*bdd1243dSDimitry Andric CorrectFixes.end()); 626*bdd1243dSDimitry Andric } 627*bdd1243dSDimitry Andric } 628*bdd1243dSDimitry Andric if (ImpossibleToFix) 629*bdd1243dSDimitry Andric FixItsForVariable.erase(VD); 630*bdd1243dSDimitry Andric else 631*bdd1243dSDimitry Andric FixItsForVariable[VD].insert(FixItsForVariable[VD].end(), 632*bdd1243dSDimitry Andric FixItsForVD.begin(), FixItsForVD.end()); 633*bdd1243dSDimitry Andric } 634*bdd1243dSDimitry Andric return FixItsForVariable; 635*bdd1243dSDimitry Andric } 636*bdd1243dSDimitry Andric 637*bdd1243dSDimitry Andric static Strategy 638*bdd1243dSDimitry Andric getNaiveStrategy(const llvm::SmallVectorImpl<const VarDecl *> &UnsafeVars) { 639*bdd1243dSDimitry Andric Strategy S; 640*bdd1243dSDimitry Andric for (const VarDecl *VD : UnsafeVars) { 641*bdd1243dSDimitry Andric S.set(VD, Strategy::Kind::Span); 642*bdd1243dSDimitry Andric } 643*bdd1243dSDimitry Andric return S; 644*bdd1243dSDimitry Andric } 645*bdd1243dSDimitry Andric 646*bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D, 647*bdd1243dSDimitry Andric UnsafeBufferUsageHandler &Handler) { 648*bdd1243dSDimitry Andric assert(D && D->getBody()); 649*bdd1243dSDimitry Andric 650*bdd1243dSDimitry Andric WarningGadgetSets UnsafeOps; 651*bdd1243dSDimitry Andric FixableGadgetSets FixablesForUnsafeVars; 652*bdd1243dSDimitry Andric DeclUseTracker Tracker; 653*bdd1243dSDimitry Andric 654*bdd1243dSDimitry Andric { 655*bdd1243dSDimitry Andric auto [FixableGadgets, WarningGadgets, TrackerRes] = findGadgets(D); 656*bdd1243dSDimitry Andric UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); 657*bdd1243dSDimitry Andric FixablesForUnsafeVars = groupFixablesByVar(std::move(FixableGadgets)); 658*bdd1243dSDimitry Andric Tracker = std::move(TrackerRes); 659*bdd1243dSDimitry Andric } 660*bdd1243dSDimitry Andric 661*bdd1243dSDimitry Andric // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. 662*bdd1243dSDimitry Andric for (auto it = FixablesForUnsafeVars.byVar.cbegin(); 663*bdd1243dSDimitry Andric it != FixablesForUnsafeVars.byVar.cend();) { 664*bdd1243dSDimitry Andric // FIXME: Support ParmVarDecl as well. 665*bdd1243dSDimitry Andric if (!it->first->isLocalVarDecl() || Tracker.hasUnclaimedUses(it->first)) { 666*bdd1243dSDimitry Andric it = FixablesForUnsafeVars.byVar.erase(it); 667*bdd1243dSDimitry Andric } else { 668*bdd1243dSDimitry Andric ++it; 669*bdd1243dSDimitry Andric } 670*bdd1243dSDimitry Andric } 671*bdd1243dSDimitry Andric 672*bdd1243dSDimitry Andric llvm::SmallVector<const VarDecl *, 16> UnsafeVars; 673*bdd1243dSDimitry Andric for (const auto &[VD, ignore] : FixablesForUnsafeVars.byVar) 674*bdd1243dSDimitry Andric UnsafeVars.push_back(VD); 675*bdd1243dSDimitry Andric 676*bdd1243dSDimitry Andric Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars); 677*bdd1243dSDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariable = 678*bdd1243dSDimitry Andric getFixIts(FixablesForUnsafeVars, NaiveStrategy); 679*bdd1243dSDimitry Andric 680*bdd1243dSDimitry Andric // FIXME Detect overlapping FixIts. 681*bdd1243dSDimitry Andric 682*bdd1243dSDimitry Andric for (const auto &G : UnsafeOps.noVar) { 683*bdd1243dSDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false); 684*bdd1243dSDimitry Andric } 685*bdd1243dSDimitry Andric 686*bdd1243dSDimitry Andric for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) { 687*bdd1243dSDimitry Andric auto FixItsIt = FixItsForVariable.find(VD); 688*bdd1243dSDimitry Andric Handler.handleFixableVariable(VD, FixItsIt != FixItsForVariable.end() 689*bdd1243dSDimitry Andric ? std::move(FixItsIt->second) 690*bdd1243dSDimitry Andric : FixItList{}); 691*bdd1243dSDimitry Andric for (const auto &G : WarningGadgets) { 692*bdd1243dSDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true); 693*bdd1243dSDimitry Andric } 694*bdd1243dSDimitry Andric } 695*bdd1243dSDimitry Andric } 696