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