xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/UnsafeBufferUsage.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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