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
16using namespace llvm;
17using namespace clang;
18using namespace ast_matchers;
19
20namespace 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".
23class MatchDescendantVisitor
24    : public RecursiveASTVisitor<MatchDescendantVisitor> {
25public:
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
86private:
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
112AST_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
120namespace {
121// Because the analysis revolves around variables and their types, we'll need to
122// track uses of variables (aka DeclRefExprs).
123using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
124
125// Convenience typedef.
126using FixItList = SmallVector<FixItHint, 4>;
127
128// Defined below.
129class Strategy;
130} // namespace
131
132// Because we're dealing with raw pointers, let's define what we mean by that.
133static auto hasPointerType() {
134    return hasType(hasCanonicalType(pointerType()));
135}
136
137static auto hasArrayType() {
138    return hasType(hasCanonicalType(arrayType()));
139}
140
141namespace {
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).
150class Gadget {
151public:
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
176private:
177  Kind K;
178};
179
180
181/// Warning gadgets correspond to unsafe code patterns that warrants
182/// an immediate warning.
183class WarningGadget : public Gadget {
184public:
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.
195class FixableGadget : public Gadget {
196public:
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
210using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
211using 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.
215class IncrementGadget : public WarningGadget {
216  static constexpr const char *const OpTag = "op";
217  const UnaryOperator *Op;
218
219public:
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.
250class DecrementGadget : public WarningGadget {
251  static constexpr const char *const OpTag = "op";
252  const UnaryOperator *Op;
253
254public:
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.
284class ArraySubscriptGadget : public WarningGadget {
285  static constexpr const char *const ArraySubscrTag = "arraySubscr";
286  const ArraySubscriptExpr *ASE;
287
288public:
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
325class 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
331public:
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
371namespace {
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.
375class 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
383public:
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
428namespace {
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.
433class Strategy {
434public:
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
443private:
444  using MapTy = llvm::DenseMap<const VarDecl *, Kind>;
445
446  MapTy Map;
447
448public:
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.
468static 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
561struct 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
567static WarningGadgetSets
568groupWarningGadgetsByVar(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
591struct FixableGadgetSets {
592  std::map<const VarDecl *, std::set<std::unique_ptr<FixableGadget>>> byVar;
593};
594
595static FixableGadgetSets
596groupFixablesByVar(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
610static std::map<const VarDecl *, FixItList>
611getFixIts(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
637static Strategy
638getNaiveStrategy(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
646void 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