xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/UnsafeBufferUsage.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1bdd1243dSDimitry Andric //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===//
2bdd1243dSDimitry Andric //
3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bdd1243dSDimitry Andric //
7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
8bdd1243dSDimitry Andric 
9bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
1006c3fb27SDimitry Andric #include "clang/AST/Decl.h"
11*5f757f3fSDimitry Andric #include "clang/AST/Expr.h"
12bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
13*5f757f3fSDimitry Andric #include "clang/AST/StmtVisitor.h"
14bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
1506c3fb27SDimitry Andric #include "clang/Lex/Lexer.h"
1606c3fb27SDimitry Andric #include "clang/Lex/Preprocessor.h"
17bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h"
18bdd1243dSDimitry Andric #include <memory>
19bdd1243dSDimitry Andric #include <optional>
2006c3fb27SDimitry Andric #include <sstream>
2106c3fb27SDimitry Andric #include <queue>
22bdd1243dSDimitry Andric 
23bdd1243dSDimitry Andric using namespace llvm;
24bdd1243dSDimitry Andric using namespace clang;
25bdd1243dSDimitry Andric using namespace ast_matchers;
26bdd1243dSDimitry Andric 
27*5f757f3fSDimitry Andric #ifndef NDEBUG
28*5f757f3fSDimitry Andric namespace {
29*5f757f3fSDimitry Andric class StmtDebugPrinter
30*5f757f3fSDimitry Andric     : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
31*5f757f3fSDimitry Andric public:
32*5f757f3fSDimitry Andric   std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
33*5f757f3fSDimitry Andric 
34*5f757f3fSDimitry Andric   std::string VisitBinaryOperator(const BinaryOperator *BO) {
35*5f757f3fSDimitry Andric     return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
36*5f757f3fSDimitry Andric   }
37*5f757f3fSDimitry Andric 
38*5f757f3fSDimitry Andric   std::string VisitUnaryOperator(const UnaryOperator *UO) {
39*5f757f3fSDimitry Andric     return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
40*5f757f3fSDimitry Andric   }
41*5f757f3fSDimitry Andric 
42*5f757f3fSDimitry Andric   std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
43*5f757f3fSDimitry Andric     return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
44*5f757f3fSDimitry Andric   }
45*5f757f3fSDimitry Andric };
46*5f757f3fSDimitry Andric 
47*5f757f3fSDimitry Andric // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
48*5f757f3fSDimitry Andric // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
49*5f757f3fSDimitry Andric static std::string getDREAncestorString(const DeclRefExpr *DRE,
50*5f757f3fSDimitry Andric                                         ASTContext &Ctx) {
51*5f757f3fSDimitry Andric   std::stringstream SS;
52*5f757f3fSDimitry Andric   const Stmt *St = DRE;
53*5f757f3fSDimitry Andric   StmtDebugPrinter StmtPriner;
54*5f757f3fSDimitry Andric 
55*5f757f3fSDimitry Andric   do {
56*5f757f3fSDimitry Andric     SS << StmtPriner.Visit(St);
57*5f757f3fSDimitry Andric 
58*5f757f3fSDimitry Andric     DynTypedNodeList StParents = Ctx.getParents(*St);
59*5f757f3fSDimitry Andric 
60*5f757f3fSDimitry Andric     if (StParents.size() > 1)
61*5f757f3fSDimitry Andric       return "unavailable due to multiple parents";
62*5f757f3fSDimitry Andric     if (StParents.size() == 0)
63*5f757f3fSDimitry Andric       break;
64*5f757f3fSDimitry Andric     St = StParents.begin()->get<Stmt>();
65*5f757f3fSDimitry Andric     if (St)
66*5f757f3fSDimitry Andric       SS << " ==> ";
67*5f757f3fSDimitry Andric   } while (St);
68*5f757f3fSDimitry Andric   return SS.str();
69*5f757f3fSDimitry Andric }
70*5f757f3fSDimitry Andric } // namespace
71*5f757f3fSDimitry Andric #endif /* NDEBUG */
72*5f757f3fSDimitry Andric 
73bdd1243dSDimitry Andric namespace clang::ast_matchers {
74bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
75bdd1243dSDimitry Andric // except for those belonging to a different callable of "n".
76bdd1243dSDimitry Andric class MatchDescendantVisitor
77bdd1243dSDimitry Andric     : public RecursiveASTVisitor<MatchDescendantVisitor> {
78bdd1243dSDimitry Andric public:
79bdd1243dSDimitry Andric   typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase;
80bdd1243dSDimitry Andric 
81bdd1243dSDimitry Andric   // Creates an AST visitor that matches `Matcher` on all
82bdd1243dSDimitry Andric   // descendants of a given node "n" except for the ones
83bdd1243dSDimitry Andric   // belonging to a different callable of "n".
84bdd1243dSDimitry Andric   MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
85bdd1243dSDimitry Andric                          internal::ASTMatchFinder *Finder,
86bdd1243dSDimitry Andric                          internal::BoundNodesTreeBuilder *Builder,
8706c3fb27SDimitry Andric                          internal::ASTMatchFinder::BindKind Bind,
8806c3fb27SDimitry Andric                          const bool ignoreUnevaluatedContext)
89bdd1243dSDimitry Andric       : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
9006c3fb27SDimitry Andric         Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {}
91bdd1243dSDimitry Andric 
92bdd1243dSDimitry Andric   // Returns true if a match is found in a subtree of `DynNode`, which belongs
93bdd1243dSDimitry Andric   // to the same callable of `DynNode`.
94bdd1243dSDimitry Andric   bool findMatch(const DynTypedNode &DynNode) {
95bdd1243dSDimitry Andric     Matches = false;
96bdd1243dSDimitry Andric     if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
97bdd1243dSDimitry Andric       TraverseStmt(const_cast<Stmt *>(StmtNode));
98bdd1243dSDimitry Andric       *Builder = ResultBindings;
99bdd1243dSDimitry Andric       return Matches;
100bdd1243dSDimitry Andric     }
101bdd1243dSDimitry Andric     return false;
102bdd1243dSDimitry Andric   }
103bdd1243dSDimitry Andric 
104bdd1243dSDimitry Andric   // The following are overriding methods from the base visitor class.
105bdd1243dSDimitry Andric   // They are public only to allow CRTP to work. They are *not *part
106bdd1243dSDimitry Andric   // of the public API of this class.
107bdd1243dSDimitry Andric 
108bdd1243dSDimitry Andric   // For the matchers so far used in safe buffers, we only need to match
109bdd1243dSDimitry Andric   // `Stmt`s.  To override more as needed.
110bdd1243dSDimitry Andric 
111bdd1243dSDimitry Andric   bool TraverseDecl(Decl *Node) {
112bdd1243dSDimitry Andric     if (!Node)
113bdd1243dSDimitry Andric       return true;
114bdd1243dSDimitry Andric     if (!match(*Node))
115bdd1243dSDimitry Andric       return false;
116bdd1243dSDimitry Andric     // To skip callables:
117bdd1243dSDimitry Andric     if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
118bdd1243dSDimitry Andric       return true;
119bdd1243dSDimitry Andric     // Traverse descendants
120bdd1243dSDimitry Andric     return VisitorBase::TraverseDecl(Node);
121bdd1243dSDimitry Andric   }
122bdd1243dSDimitry Andric 
12306c3fb27SDimitry Andric   bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) {
12406c3fb27SDimitry Andric     // These are unevaluated, except the result expression.
12506c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
12606c3fb27SDimitry Andric       return TraverseStmt(Node->getResultExpr());
12706c3fb27SDimitry Andric     return VisitorBase::TraverseGenericSelectionExpr(Node);
12806c3fb27SDimitry Andric   }
12906c3fb27SDimitry Andric 
13006c3fb27SDimitry Andric   bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) {
13106c3fb27SDimitry Andric     // Unevaluated context.
13206c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
13306c3fb27SDimitry Andric       return true;
13406c3fb27SDimitry Andric     return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node);
13506c3fb27SDimitry Andric   }
13606c3fb27SDimitry Andric 
13706c3fb27SDimitry Andric   bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) {
13806c3fb27SDimitry Andric     // Unevaluated context.
13906c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
14006c3fb27SDimitry Andric       return true;
14106c3fb27SDimitry Andric     return VisitorBase::TraverseTypeOfExprTypeLoc(Node);
14206c3fb27SDimitry Andric   }
14306c3fb27SDimitry Andric 
14406c3fb27SDimitry Andric   bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) {
14506c3fb27SDimitry Andric     // Unevaluated context.
14606c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
14706c3fb27SDimitry Andric       return true;
14806c3fb27SDimitry Andric     return VisitorBase::TraverseDecltypeTypeLoc(Node);
14906c3fb27SDimitry Andric   }
15006c3fb27SDimitry Andric 
15106c3fb27SDimitry Andric   bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) {
15206c3fb27SDimitry Andric     // Unevaluated context.
15306c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
15406c3fb27SDimitry Andric       return true;
15506c3fb27SDimitry Andric     return VisitorBase::TraverseCXXNoexceptExpr(Node);
15606c3fb27SDimitry Andric   }
15706c3fb27SDimitry Andric 
15806c3fb27SDimitry Andric   bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) {
15906c3fb27SDimitry Andric     // Unevaluated context.
16006c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
16106c3fb27SDimitry Andric       return true;
16206c3fb27SDimitry Andric     return VisitorBase::TraverseCXXTypeidExpr(Node);
16306c3fb27SDimitry Andric   }
16406c3fb27SDimitry Andric 
165bdd1243dSDimitry Andric   bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
166bdd1243dSDimitry Andric     if (!Node)
167bdd1243dSDimitry Andric       return true;
168bdd1243dSDimitry Andric     if (!match(*Node))
169bdd1243dSDimitry Andric       return false;
170bdd1243dSDimitry Andric     return VisitorBase::TraverseStmt(Node);
171bdd1243dSDimitry Andric   }
172bdd1243dSDimitry Andric 
173bdd1243dSDimitry Andric   bool shouldVisitTemplateInstantiations() const { return true; }
174bdd1243dSDimitry Andric   bool shouldVisitImplicitCode() const {
175bdd1243dSDimitry Andric     // TODO: let's ignore implicit code for now
176bdd1243dSDimitry Andric     return false;
177bdd1243dSDimitry Andric   }
178bdd1243dSDimitry Andric 
179bdd1243dSDimitry Andric private:
180bdd1243dSDimitry Andric   // Sets 'Matched' to true if 'Matcher' matches 'Node'
181bdd1243dSDimitry Andric   //
182bdd1243dSDimitry Andric   // Returns 'true' if traversal should continue after this function
183bdd1243dSDimitry Andric   // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
184bdd1243dSDimitry Andric   template <typename T> bool match(const T &Node) {
185bdd1243dSDimitry Andric     internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
186bdd1243dSDimitry Andric 
187bdd1243dSDimitry Andric     if (Matcher->matches(DynTypedNode::create(Node), Finder,
188bdd1243dSDimitry Andric                          &RecursiveBuilder)) {
189bdd1243dSDimitry Andric       ResultBindings.addMatch(RecursiveBuilder);
190bdd1243dSDimitry Andric       Matches = true;
191bdd1243dSDimitry Andric       if (Bind != internal::ASTMatchFinder::BK_All)
192bdd1243dSDimitry Andric         return false; // Abort as soon as a match is found.
193bdd1243dSDimitry Andric     }
194bdd1243dSDimitry Andric     return true;
195bdd1243dSDimitry Andric   }
196bdd1243dSDimitry Andric 
197bdd1243dSDimitry Andric   const internal::DynTypedMatcher *const Matcher;
198bdd1243dSDimitry Andric   internal::ASTMatchFinder *const Finder;
199bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder *const Builder;
200bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder ResultBindings;
201bdd1243dSDimitry Andric   const internal::ASTMatchFinder::BindKind Bind;
202bdd1243dSDimitry Andric   bool Matches;
20306c3fb27SDimitry Andric   bool ignoreUnevaluatedContext;
204bdd1243dSDimitry Andric };
205bdd1243dSDimitry Andric 
20606c3fb27SDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that.
20706c3fb27SDimitry Andric static auto hasPointerType() {
20806c3fb27SDimitry Andric     return hasType(hasCanonicalType(pointerType()));
20906c3fb27SDimitry Andric }
21006c3fb27SDimitry Andric 
21106c3fb27SDimitry Andric static auto hasArrayType() {
21206c3fb27SDimitry Andric     return hasType(hasCanonicalType(arrayType()));
21306c3fb27SDimitry Andric }
21406c3fb27SDimitry Andric 
21506c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>, innerMatcher) {
216bdd1243dSDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
217bdd1243dSDimitry Andric 
21806c3fb27SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, true);
219bdd1243dSDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
220bdd1243dSDimitry Andric }
22106c3fb27SDimitry Andric 
22206c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>, innerMatcher) {
22306c3fb27SDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
22406c3fb27SDimitry Andric 
22506c3fb27SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, false);
22606c3fb27SDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
22706c3fb27SDimitry Andric }
22806c3fb27SDimitry Andric 
22906c3fb27SDimitry Andric // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
23006c3fb27SDimitry Andric AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *,
23106c3fb27SDimitry Andric               Handler) {
23206c3fb27SDimitry Andric   return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
23306c3fb27SDimitry Andric }
23406c3fb27SDimitry Andric 
23506c3fb27SDimitry Andric AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
23606c3fb27SDimitry Andric   return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
23706c3fb27SDimitry Andric }
23806c3fb27SDimitry Andric 
23906c3fb27SDimitry Andric // Matches a `UnaryOperator` whose operator is pre-increment:
24006c3fb27SDimitry Andric AST_MATCHER(UnaryOperator, isPreInc) {
24106c3fb27SDimitry Andric   return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
24206c3fb27SDimitry Andric }
24306c3fb27SDimitry Andric 
24406c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
24506c3fb27SDimitry Andric // matches 'e' and 'e' is in an Unspecified Lvalue Context.
24606c3fb27SDimitry Andric static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) {
24706c3fb27SDimitry Andric   // clang-format off
24806c3fb27SDimitry Andric   return
24906c3fb27SDimitry Andric     expr(anyOf(
25006c3fb27SDimitry Andric       implicitCastExpr(
25106c3fb27SDimitry Andric         hasCastKind(CastKind::CK_LValueToRValue),
25206c3fb27SDimitry Andric         castSubExpr(innerMatcher)),
25306c3fb27SDimitry Andric       binaryOperator(
25406c3fb27SDimitry Andric         hasAnyOperatorName("="),
25506c3fb27SDimitry Andric         hasLHS(innerMatcher)
25606c3fb27SDimitry Andric       )
25706c3fb27SDimitry Andric     ));
25806c3fb27SDimitry Andric // clang-format on
25906c3fb27SDimitry Andric }
26006c3fb27SDimitry Andric 
26106c3fb27SDimitry Andric 
26206c3fb27SDimitry Andric // Returns a matcher that matches any expression `e` such that `InnerMatcher`
26306c3fb27SDimitry Andric // matches `e` and `e` is in an Unspecified Pointer Context (UPC).
26406c3fb27SDimitry Andric static internal::Matcher<Stmt>
26506c3fb27SDimitry Andric isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
26606c3fb27SDimitry Andric   // A UPC can be
26706c3fb27SDimitry Andric   // 1. an argument of a function call (except the callee has [[unsafe_...]]
26806c3fb27SDimitry Andric   //    attribute), or
26906c3fb27SDimitry Andric   // 2. the operand of a pointer-to-(integer or bool) cast operation; or
27006c3fb27SDimitry Andric   // 3. the operand of a comparator operation; or
27106c3fb27SDimitry Andric   // 4. the operand of a pointer subtraction operation
27206c3fb27SDimitry Andric   //    (i.e., computing the distance between two pointers); or ...
27306c3fb27SDimitry Andric 
27406c3fb27SDimitry Andric   auto CallArgMatcher =
27506c3fb27SDimitry Andric       callExpr(forEachArgumentWithParam(InnerMatcher,
27606c3fb27SDimitry Andric                   hasPointerType() /* array also decays to pointer type*/),
27706c3fb27SDimitry Andric           unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
27806c3fb27SDimitry Andric 
27906c3fb27SDimitry Andric   auto CastOperandMatcher =
28006c3fb27SDimitry Andric       castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
28106c3fb27SDimitry Andric 		     hasCastKind(CastKind::CK_PointerToBoolean)),
28206c3fb27SDimitry Andric 	       castSubExpr(allOf(hasPointerType(), InnerMatcher)));
28306c3fb27SDimitry Andric 
28406c3fb27SDimitry Andric   auto CompOperandMatcher =
28506c3fb27SDimitry Andric       binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
28606c3fb27SDimitry Andric                      eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
28706c3fb27SDimitry Andric                             hasRHS(allOf(hasPointerType(), InnerMatcher))));
28806c3fb27SDimitry Andric 
28906c3fb27SDimitry Andric   // A matcher that matches pointer subtractions:
29006c3fb27SDimitry Andric   auto PtrSubtractionMatcher =
29106c3fb27SDimitry Andric       binaryOperator(hasOperatorName("-"),
29206c3fb27SDimitry Andric 		     // Note that here we need both LHS and RHS to be
29306c3fb27SDimitry Andric 		     // pointer. Then the inner matcher can match any of
29406c3fb27SDimitry Andric 		     // them:
29506c3fb27SDimitry Andric 		     allOf(hasLHS(hasPointerType()),
29606c3fb27SDimitry Andric 			   hasRHS(hasPointerType())),
29706c3fb27SDimitry Andric 		     eachOf(hasLHS(InnerMatcher),
29806c3fb27SDimitry Andric 			    hasRHS(InnerMatcher)));
29906c3fb27SDimitry Andric 
30006c3fb27SDimitry Andric   return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
30106c3fb27SDimitry Andric 		    PtrSubtractionMatcher));
30206c3fb27SDimitry Andric   // FIXME: any more cases? (UPC excludes the RHS of an assignment.  For now we
30306c3fb27SDimitry Andric   // don't have to check that.)
30406c3fb27SDimitry Andric }
30506c3fb27SDimitry Andric 
30606c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
30706c3fb27SDimitry Andric // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression
30806c3fb27SDimitry Andric // 'e' isn't evaluated to an RValue). For example, consider the following code:
30906c3fb27SDimitry Andric //    int *p = new int[4];
31006c3fb27SDimitry Andric //    int *q = new int[4];
31106c3fb27SDimitry Andric //    if ((p = q)) {}
31206c3fb27SDimitry Andric //    p = q;
31306c3fb27SDimitry Andric // The expression `p = q` in the conditional of the `if` statement
31406c3fb27SDimitry Andric // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
31506c3fb27SDimitry Andric // in the assignment statement is in an untyped context.
31606c3fb27SDimitry Andric static internal::Matcher<Stmt>
31706c3fb27SDimitry Andric isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
31806c3fb27SDimitry Andric   // An unspecified context can be
31906c3fb27SDimitry Andric   // 1. A compound statement,
32006c3fb27SDimitry Andric   // 2. The body of an if statement
32106c3fb27SDimitry Andric   // 3. Body of a loop
32206c3fb27SDimitry Andric   auto CompStmt = compoundStmt(forEach(InnerMatcher));
32306c3fb27SDimitry Andric   auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
32406c3fb27SDimitry Andric   auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
32506c3fb27SDimitry Andric   // FIXME: Handle loop bodies.
32606c3fb27SDimitry Andric   return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
32706c3fb27SDimitry Andric }
328bdd1243dSDimitry Andric } // namespace clang::ast_matchers
329bdd1243dSDimitry Andric 
330bdd1243dSDimitry Andric namespace {
331bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to
332bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs).
333bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
334bdd1243dSDimitry Andric 
335bdd1243dSDimitry Andric // Convenience typedef.
336bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>;
337bdd1243dSDimitry Andric 
338bdd1243dSDimitry Andric // Defined below.
339bdd1243dSDimitry Andric class Strategy;
340bdd1243dSDimitry Andric } // namespace
341bdd1243dSDimitry Andric 
342bdd1243dSDimitry Andric namespace {
343bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to
344bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific
345bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object.
346bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand
347bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it.
348bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not
349bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify
350bdd1243dSDimitry Andric /// it warrants a fixit).
351bdd1243dSDimitry Andric class Gadget {
352bdd1243dSDimitry Andric public:
353bdd1243dSDimitry Andric   enum class Kind {
354bdd1243dSDimitry Andric #define GADGET(x) x,
355bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
356bdd1243dSDimitry Andric   };
357bdd1243dSDimitry Andric 
358bdd1243dSDimitry Andric   /// Common type of ASTMatchers used for discovering gadgets.
359bdd1243dSDimitry Andric   /// Useful for implementing the static matcher() methods
360bdd1243dSDimitry Andric   /// that are expected from all non-abstract subclasses.
361bdd1243dSDimitry Andric   using Matcher = decltype(stmt());
362bdd1243dSDimitry Andric 
363bdd1243dSDimitry Andric   Gadget(Kind K) : K(K) {}
364bdd1243dSDimitry Andric 
365bdd1243dSDimitry Andric   Kind getKind() const { return K; }
366bdd1243dSDimitry Andric 
367*5f757f3fSDimitry Andric #ifndef NDEBUG
368*5f757f3fSDimitry Andric   StringRef getDebugName() const {
369*5f757f3fSDimitry Andric     switch (K) {
370*5f757f3fSDimitry Andric #define GADGET(x) case Kind::x: return #x;
371*5f757f3fSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
372*5f757f3fSDimitry Andric     }
373*5f757f3fSDimitry Andric     llvm_unreachable("Unhandled Gadget::Kind enum");
374*5f757f3fSDimitry Andric   }
375*5f757f3fSDimitry Andric #endif
376*5f757f3fSDimitry Andric 
377bdd1243dSDimitry Andric   virtual bool isWarningGadget() const = 0;
378bdd1243dSDimitry Andric   virtual const Stmt *getBaseStmt() const = 0;
379bdd1243dSDimitry Andric 
380bdd1243dSDimitry Andric   /// Returns the list of pointer-type variables on which this gadget performs
381bdd1243dSDimitry Andric   /// its operation. Typically, there's only one variable. This isn't a list
382bdd1243dSDimitry Andric   /// of all DeclRefExprs in the gadget's AST!
383bdd1243dSDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const = 0;
384bdd1243dSDimitry Andric 
385bdd1243dSDimitry Andric   virtual ~Gadget() = default;
386bdd1243dSDimitry Andric 
387bdd1243dSDimitry Andric private:
388bdd1243dSDimitry Andric   Kind K;
389bdd1243dSDimitry Andric };
390bdd1243dSDimitry Andric 
391bdd1243dSDimitry Andric 
392bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants
393bdd1243dSDimitry Andric /// an immediate warning.
394bdd1243dSDimitry Andric class WarningGadget : public Gadget {
395bdd1243dSDimitry Andric public:
396bdd1243dSDimitry Andric   WarningGadget(Kind K) : Gadget(K) {}
397bdd1243dSDimitry Andric 
398bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return G->isWarningGadget(); }
399bdd1243dSDimitry Andric   bool isWarningGadget() const final { return true; }
400bdd1243dSDimitry Andric };
401bdd1243dSDimitry Andric 
402bdd1243dSDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but need to be
403bdd1243dSDimitry Andric /// properly recognized in order to emit fixes. For example, if a raw pointer-type
404bdd1243dSDimitry Andric /// variable is replaced by a safe C++ container, every use of such variable must be
405bdd1243dSDimitry Andric /// carefully considered and possibly updated.
406bdd1243dSDimitry Andric class FixableGadget : public Gadget {
407bdd1243dSDimitry Andric public:
408bdd1243dSDimitry Andric   FixableGadget(Kind K) : Gadget(K) {}
409bdd1243dSDimitry Andric 
410bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
411bdd1243dSDimitry Andric   bool isWarningGadget() const final { return false; }
412bdd1243dSDimitry Andric 
413bdd1243dSDimitry Andric   /// Returns a fixit that would fix the current gadget according to
41406c3fb27SDimitry Andric   /// the current strategy. Returns std::nullopt if the fix cannot be produced;
415bdd1243dSDimitry Andric   /// returns an empty list if no fixes are necessary.
416bdd1243dSDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &) const {
417bdd1243dSDimitry Andric     return std::nullopt;
418bdd1243dSDimitry Andric   }
41906c3fb27SDimitry Andric 
42006c3fb27SDimitry Andric   /// Returns a list of two elements where the first element is the LHS of a pointer assignment
42106c3fb27SDimitry Andric   /// statement and the second element is the RHS. This two-element list represents the fact that
42206c3fb27SDimitry Andric   /// the LHS buffer gets its bounds information from the RHS buffer. This information will be used
42306c3fb27SDimitry Andric   /// later to group all those variables whose types must be modified together to prevent type
42406c3fb27SDimitry Andric   /// mismatches.
42506c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
42606c3fb27SDimitry Andric   getStrategyImplications() const {
42706c3fb27SDimitry Andric     return std::nullopt;
42806c3fb27SDimitry Andric   }
429bdd1243dSDimitry Andric };
430bdd1243dSDimitry Andric 
431*5f757f3fSDimitry Andric static auto toSupportedVariable() {
432*5f757f3fSDimitry Andric   return to(varDecl());
433*5f757f3fSDimitry Andric }
434*5f757f3fSDimitry Andric 
435bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
436bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
437bdd1243dSDimitry Andric 
438bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer
439bdd1243dSDimitry Andric /// out of bounds.
440bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget {
441bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
442bdd1243dSDimitry Andric   const UnaryOperator *Op;
443bdd1243dSDimitry Andric 
444bdd1243dSDimitry Andric public:
445bdd1243dSDimitry Andric   IncrementGadget(const MatchFinder::MatchResult &Result)
446bdd1243dSDimitry Andric       : WarningGadget(Kind::Increment),
447bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
448bdd1243dSDimitry Andric 
449bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
450bdd1243dSDimitry Andric     return G->getKind() == Kind::Increment;
451bdd1243dSDimitry Andric   }
452bdd1243dSDimitry Andric 
453bdd1243dSDimitry Andric   static Matcher matcher() {
454bdd1243dSDimitry Andric     return stmt(unaryOperator(
455bdd1243dSDimitry Andric       hasOperatorName("++"),
456bdd1243dSDimitry Andric       hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
457bdd1243dSDimitry Andric     ).bind(OpTag));
458bdd1243dSDimitry Andric   }
459bdd1243dSDimitry Andric 
460bdd1243dSDimitry Andric   const UnaryOperator *getBaseStmt() const override { return Op; }
461bdd1243dSDimitry Andric 
462bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
463bdd1243dSDimitry Andric     SmallVector<const DeclRefExpr *, 2> Uses;
464bdd1243dSDimitry Andric     if (const auto *DRE =
465bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
466bdd1243dSDimitry Andric       Uses.push_back(DRE);
467bdd1243dSDimitry Andric     }
468bdd1243dSDimitry Andric 
469bdd1243dSDimitry Andric     return std::move(Uses);
470bdd1243dSDimitry Andric   }
471bdd1243dSDimitry Andric };
472bdd1243dSDimitry Andric 
473bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer
474bdd1243dSDimitry Andric /// out of bounds.
475bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget {
476bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
477bdd1243dSDimitry Andric   const UnaryOperator *Op;
478bdd1243dSDimitry Andric 
479bdd1243dSDimitry Andric public:
480bdd1243dSDimitry Andric   DecrementGadget(const MatchFinder::MatchResult &Result)
481bdd1243dSDimitry Andric       : WarningGadget(Kind::Decrement),
482bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
483bdd1243dSDimitry Andric 
484bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
485bdd1243dSDimitry Andric     return G->getKind() == Kind::Decrement;
486bdd1243dSDimitry Andric   }
487bdd1243dSDimitry Andric 
488bdd1243dSDimitry Andric   static Matcher matcher() {
489bdd1243dSDimitry Andric     return stmt(unaryOperator(
490bdd1243dSDimitry Andric       hasOperatorName("--"),
491bdd1243dSDimitry Andric       hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
492bdd1243dSDimitry Andric     ).bind(OpTag));
493bdd1243dSDimitry Andric   }
494bdd1243dSDimitry Andric 
495bdd1243dSDimitry Andric   const UnaryOperator *getBaseStmt() const override { return Op; }
496bdd1243dSDimitry Andric 
497bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
498bdd1243dSDimitry Andric     if (const auto *DRE =
499bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
500bdd1243dSDimitry Andric       return {DRE};
501bdd1243dSDimitry Andric     }
502bdd1243dSDimitry Andric 
503bdd1243dSDimitry Andric     return {};
504bdd1243dSDimitry Andric   }
505bdd1243dSDimitry Andric };
506bdd1243dSDimitry Andric 
507bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
508bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array.
509bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget {
51006c3fb27SDimitry Andric   static constexpr const char *const ArraySubscrTag = "ArraySubscript";
511bdd1243dSDimitry Andric   const ArraySubscriptExpr *ASE;
512bdd1243dSDimitry Andric 
513bdd1243dSDimitry Andric public:
514bdd1243dSDimitry Andric   ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
515bdd1243dSDimitry Andric       : WarningGadget(Kind::ArraySubscript),
516bdd1243dSDimitry Andric         ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
517bdd1243dSDimitry Andric 
518bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
519bdd1243dSDimitry Andric     return G->getKind() == Kind::ArraySubscript;
520bdd1243dSDimitry Andric   }
521bdd1243dSDimitry Andric 
522bdd1243dSDimitry Andric   static Matcher matcher() {
523bdd1243dSDimitry Andric     // FIXME: What if the index is integer literal 0? Should this be
524bdd1243dSDimitry Andric     // a safe gadget in this case?
525bdd1243dSDimitry Andric       // clang-format off
526bdd1243dSDimitry Andric       return stmt(arraySubscriptExpr(
527bdd1243dSDimitry Andric             hasBase(ignoringParenImpCasts(
528bdd1243dSDimitry Andric               anyOf(hasPointerType(), hasArrayType()))),
52906c3fb27SDimitry Andric             unless(hasIndex(
53006c3fb27SDimitry Andric                 anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
53106c3fb27SDimitry Andric              )))
532bdd1243dSDimitry Andric             .bind(ArraySubscrTag));
533bdd1243dSDimitry Andric     // clang-format on
534bdd1243dSDimitry Andric   }
535bdd1243dSDimitry Andric 
536bdd1243dSDimitry Andric   const ArraySubscriptExpr *getBaseStmt() const override { return ASE; }
537bdd1243dSDimitry Andric 
538bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
539bdd1243dSDimitry Andric     if (const auto *DRE =
540bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
541bdd1243dSDimitry Andric       return {DRE};
542bdd1243dSDimitry Andric     }
543bdd1243dSDimitry Andric 
544bdd1243dSDimitry Andric     return {};
545bdd1243dSDimitry Andric   }
546bdd1243dSDimitry Andric };
547bdd1243dSDimitry Andric 
548bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms:
549bdd1243dSDimitry Andric ///  \code
550bdd1243dSDimitry Andric ///  ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
551bdd1243dSDimitry Andric ///  \endcode
552bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget {
553bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticTag = "ptrAdd";
554bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
555bdd1243dSDimitry Andric   const BinaryOperator *PA; // pointer arithmetic expression
556bdd1243dSDimitry Andric   const Expr *Ptr;          // the pointer expression in `PA`
557bdd1243dSDimitry Andric 
558bdd1243dSDimitry Andric public:
559bdd1243dSDimitry Andric   PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
560bdd1243dSDimitry Andric       : WarningGadget(Kind::PointerArithmetic),
561bdd1243dSDimitry Andric         PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
562bdd1243dSDimitry Andric         Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
563bdd1243dSDimitry Andric 
564bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
565bdd1243dSDimitry Andric     return G->getKind() == Kind::PointerArithmetic;
566bdd1243dSDimitry Andric   }
567bdd1243dSDimitry Andric 
568bdd1243dSDimitry Andric   static Matcher matcher() {
56906c3fb27SDimitry Andric     auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
57006c3fb27SDimitry Andric     auto PtrAtRight =
57106c3fb27SDimitry Andric         allOf(hasOperatorName("+"),
572bdd1243dSDimitry Andric               hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
573bdd1243dSDimitry Andric               hasLHS(HasIntegerType));
57406c3fb27SDimitry Andric     auto PtrAtLeft =
57506c3fb27SDimitry Andric         allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
576bdd1243dSDimitry Andric                     hasOperatorName("+="), hasOperatorName("-=")),
577bdd1243dSDimitry Andric               hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
578bdd1243dSDimitry Andric               hasRHS(HasIntegerType));
579bdd1243dSDimitry Andric 
58006c3fb27SDimitry Andric     return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight))
58106c3fb27SDimitry Andric                     .bind(PointerArithmeticTag));
582bdd1243dSDimitry Andric   }
583bdd1243dSDimitry Andric 
584bdd1243dSDimitry Andric   const Stmt *getBaseStmt() const override { return PA; }
585bdd1243dSDimitry Andric 
586bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
58706c3fb27SDimitry Andric     if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
588bdd1243dSDimitry Andric       return {DRE};
589bdd1243dSDimitry Andric     }
590bdd1243dSDimitry Andric 
591bdd1243dSDimitry Andric     return {};
592bdd1243dSDimitry Andric   }
593bdd1243dSDimitry Andric   // FIXME: pointer adding zero should be fine
594bdd1243dSDimitry Andric   // FIXME: this gadge will need a fix-it
595bdd1243dSDimitry Andric };
59606c3fb27SDimitry Andric 
59706c3fb27SDimitry Andric /// A pointer initialization expression of the form:
59806c3fb27SDimitry Andric ///  \code
59906c3fb27SDimitry Andric ///  int *p = q;
60006c3fb27SDimitry Andric ///  \endcode
60106c3fb27SDimitry Andric class PointerInitGadget : public FixableGadget {
60206c3fb27SDimitry Andric private:
60306c3fb27SDimitry Andric   static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
60406c3fb27SDimitry Andric   static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
60506c3fb27SDimitry Andric   const VarDecl * PtrInitLHS;         // the LHS pointer expression in `PI`
60606c3fb27SDimitry Andric   const DeclRefExpr * PtrInitRHS;         // the RHS pointer expression in `PI`
60706c3fb27SDimitry Andric 
60806c3fb27SDimitry Andric public:
60906c3fb27SDimitry Andric   PointerInitGadget(const MatchFinder::MatchResult &Result)
61006c3fb27SDimitry Andric       : FixableGadget(Kind::PointerInit),
61106c3fb27SDimitry Andric     PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
61206c3fb27SDimitry Andric     PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
61306c3fb27SDimitry Andric 
61406c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
61506c3fb27SDimitry Andric     return G->getKind() == Kind::PointerInit;
61606c3fb27SDimitry Andric   }
61706c3fb27SDimitry Andric 
61806c3fb27SDimitry Andric   static Matcher matcher() {
61906c3fb27SDimitry Andric     auto PtrInitStmt = declStmt(hasSingleDecl(varDecl(
62006c3fb27SDimitry Andric                                  hasInitializer(ignoringImpCasts(declRefExpr(
621*5f757f3fSDimitry Andric                                                   hasPointerType(),
622*5f757f3fSDimitry Andric                                                     toSupportedVariable()).
62306c3fb27SDimitry Andric                                                   bind(PointerInitRHSTag)))).
62406c3fb27SDimitry Andric                                               bind(PointerInitLHSTag)));
62506c3fb27SDimitry Andric 
62606c3fb27SDimitry Andric     return stmt(PtrInitStmt);
62706c3fb27SDimitry Andric   }
62806c3fb27SDimitry Andric 
62906c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
63006c3fb27SDimitry Andric 
631*5f757f3fSDimitry Andric   virtual const Stmt *getBaseStmt() const override {
632*5f757f3fSDimitry Andric     // FIXME: This needs to be the entire DeclStmt, assuming that this method
633*5f757f3fSDimitry Andric     // makes sense at all on a FixableGadget.
634*5f757f3fSDimitry Andric     return PtrInitRHS;
635*5f757f3fSDimitry Andric   }
63606c3fb27SDimitry Andric 
63706c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
63806c3fb27SDimitry Andric     return DeclUseList{PtrInitRHS};
63906c3fb27SDimitry Andric   }
64006c3fb27SDimitry Andric 
64106c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
64206c3fb27SDimitry Andric   getStrategyImplications() const override {
64306c3fb27SDimitry Andric       return std::make_pair(PtrInitLHS,
64406c3fb27SDimitry Andric                             cast<VarDecl>(PtrInitRHS->getDecl()));
64506c3fb27SDimitry Andric   }
64606c3fb27SDimitry Andric };
64706c3fb27SDimitry Andric 
64806c3fb27SDimitry Andric /// A pointer assignment expression of the form:
64906c3fb27SDimitry Andric ///  \code
65006c3fb27SDimitry Andric ///  p = q;
65106c3fb27SDimitry Andric ///  \endcode
65206c3fb27SDimitry Andric class PointerAssignmentGadget : public FixableGadget {
65306c3fb27SDimitry Andric private:
65406c3fb27SDimitry Andric   static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
65506c3fb27SDimitry Andric   static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
65606c3fb27SDimitry Andric   const DeclRefExpr * PtrLHS;         // the LHS pointer expression in `PA`
65706c3fb27SDimitry Andric   const DeclRefExpr * PtrRHS;         // the RHS pointer expression in `PA`
65806c3fb27SDimitry Andric 
65906c3fb27SDimitry Andric public:
66006c3fb27SDimitry Andric   PointerAssignmentGadget(const MatchFinder::MatchResult &Result)
66106c3fb27SDimitry Andric       : FixableGadget(Kind::PointerAssignment),
66206c3fb27SDimitry Andric     PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
66306c3fb27SDimitry Andric     PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
66406c3fb27SDimitry Andric 
66506c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
66606c3fb27SDimitry Andric     return G->getKind() == Kind::PointerAssignment;
66706c3fb27SDimitry Andric   }
66806c3fb27SDimitry Andric 
66906c3fb27SDimitry Andric   static Matcher matcher() {
67006c3fb27SDimitry Andric     auto PtrAssignExpr = binaryOperator(allOf(hasOperatorName("="),
67106c3fb27SDimitry Andric       hasRHS(ignoringParenImpCasts(declRefExpr(hasPointerType(),
672*5f757f3fSDimitry Andric                                                toSupportedVariable()).
67306c3fb27SDimitry Andric                                    bind(PointerAssignRHSTag))),
67406c3fb27SDimitry Andric                                    hasLHS(declRefExpr(hasPointerType(),
675*5f757f3fSDimitry Andric                                                       toSupportedVariable()).
67606c3fb27SDimitry Andric                                           bind(PointerAssignLHSTag))));
67706c3fb27SDimitry Andric 
67806c3fb27SDimitry Andric     return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
67906c3fb27SDimitry Andric   }
68006c3fb27SDimitry Andric 
68106c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
68206c3fb27SDimitry Andric 
683*5f757f3fSDimitry Andric   virtual const Stmt *getBaseStmt() const override {
684*5f757f3fSDimitry Andric     // FIXME: This should be the binary operator, assuming that this method
685*5f757f3fSDimitry Andric     // makes sense at all on a FixableGadget.
686*5f757f3fSDimitry Andric     return PtrLHS;
687*5f757f3fSDimitry Andric   }
68806c3fb27SDimitry Andric 
68906c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
69006c3fb27SDimitry Andric     return DeclUseList{PtrLHS, PtrRHS};
69106c3fb27SDimitry Andric   }
69206c3fb27SDimitry Andric 
69306c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
69406c3fb27SDimitry Andric   getStrategyImplications() const override {
69506c3fb27SDimitry Andric     return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
69606c3fb27SDimitry Andric                           cast<VarDecl>(PtrRHS->getDecl()));
69706c3fb27SDimitry Andric   }
69806c3fb27SDimitry Andric };
69906c3fb27SDimitry Andric 
70006c3fb27SDimitry Andric /// A call of a function or method that performs unchecked buffer operations
70106c3fb27SDimitry Andric /// over one of its pointer parameters.
70206c3fb27SDimitry Andric class UnsafeBufferUsageAttrGadget : public WarningGadget {
70306c3fb27SDimitry Andric   constexpr static const char *const OpTag = "call_expr";
70406c3fb27SDimitry Andric   const CallExpr *Op;
70506c3fb27SDimitry Andric 
70606c3fb27SDimitry Andric public:
70706c3fb27SDimitry Andric   UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
70806c3fb27SDimitry Andric       : WarningGadget(Kind::UnsafeBufferUsageAttr),
70906c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {}
71006c3fb27SDimitry Andric 
71106c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
71206c3fb27SDimitry Andric     return G->getKind() == Kind::UnsafeBufferUsageAttr;
71306c3fb27SDimitry Andric   }
71406c3fb27SDimitry Andric 
71506c3fb27SDimitry Andric   static Matcher matcher() {
71606c3fb27SDimitry Andric     return stmt(callExpr(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))))
71706c3fb27SDimitry Andric                     .bind(OpTag));
71806c3fb27SDimitry Andric   }
71906c3fb27SDimitry Andric   const Stmt *getBaseStmt() const override { return Op; }
72006c3fb27SDimitry Andric 
72106c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override { return {}; }
72206c3fb27SDimitry Andric };
72306c3fb27SDimitry Andric 
72406c3fb27SDimitry Andric // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
72506c3fb27SDimitry Andric // Context (see `isInUnspecifiedLvalueContext`).
72606c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
72706c3fb27SDimitry Andric class ULCArraySubscriptGadget : public FixableGadget {
72806c3fb27SDimitry Andric private:
72906c3fb27SDimitry Andric   static constexpr const char *const ULCArraySubscriptTag =
73006c3fb27SDimitry Andric       "ArraySubscriptUnderULC";
73106c3fb27SDimitry Andric   const ArraySubscriptExpr *Node;
73206c3fb27SDimitry Andric 
73306c3fb27SDimitry Andric public:
73406c3fb27SDimitry Andric   ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
73506c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
73606c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
73706c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
73806c3fb27SDimitry Andric   }
73906c3fb27SDimitry Andric 
74006c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
74106c3fb27SDimitry Andric     return G->getKind() == Kind::ULCArraySubscript;
74206c3fb27SDimitry Andric   }
74306c3fb27SDimitry Andric 
74406c3fb27SDimitry Andric   static Matcher matcher() {
74506c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
74606c3fb27SDimitry Andric     auto BaseIsArrayOrPtrDRE =
747*5f757f3fSDimitry Andric         hasBase(ignoringParenImpCasts(declRefExpr(ArrayOrPtr,
748*5f757f3fSDimitry Andric                                                   toSupportedVariable())));
74906c3fb27SDimitry Andric     auto Target =
75006c3fb27SDimitry Andric         arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
75106c3fb27SDimitry Andric 
75206c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
75306c3fb27SDimitry Andric   }
75406c3fb27SDimitry Andric 
75506c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
75606c3fb27SDimitry Andric 
75706c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
75806c3fb27SDimitry Andric 
75906c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
76006c3fb27SDimitry Andric     if (const auto *DRE =
76106c3fb27SDimitry Andric             dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
76206c3fb27SDimitry Andric       return {DRE};
76306c3fb27SDimitry Andric     }
76406c3fb27SDimitry Andric     return {};
76506c3fb27SDimitry Andric   }
76606c3fb27SDimitry Andric };
76706c3fb27SDimitry Andric 
76806c3fb27SDimitry Andric // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
76906c3fb27SDimitry Andric // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
77006c3fb27SDimitry Andric // fixit of the form `UPC(DRE.data())`.
77106c3fb27SDimitry Andric class UPCStandalonePointerGadget : public FixableGadget {
77206c3fb27SDimitry Andric private:
77306c3fb27SDimitry Andric   static constexpr const char *const DeclRefExprTag = "StandalonePointer";
77406c3fb27SDimitry Andric   const DeclRefExpr *Node;
77506c3fb27SDimitry Andric 
77606c3fb27SDimitry Andric public:
77706c3fb27SDimitry Andric   UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
77806c3fb27SDimitry Andric       : FixableGadget(Kind::UPCStandalonePointer),
77906c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
78006c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
78106c3fb27SDimitry Andric   }
78206c3fb27SDimitry Andric 
78306c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
78406c3fb27SDimitry Andric     return G->getKind() == Kind::UPCStandalonePointer;
78506c3fb27SDimitry Andric   }
78606c3fb27SDimitry Andric 
78706c3fb27SDimitry Andric   static Matcher matcher() {
78806c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
78906c3fb27SDimitry Andric     auto target = expr(
790*5f757f3fSDimitry Andric         ignoringParenImpCasts(declRefExpr(allOf(ArrayOrPtr,
791*5f757f3fSDimitry Andric                               toSupportedVariable())).bind(DeclRefExprTag)));
79206c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(target));
79306c3fb27SDimitry Andric   }
79406c3fb27SDimitry Andric 
79506c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
79606c3fb27SDimitry Andric 
79706c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
79806c3fb27SDimitry Andric 
79906c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
80006c3fb27SDimitry Andric     return {Node};
80106c3fb27SDimitry Andric   }
80206c3fb27SDimitry Andric };
80306c3fb27SDimitry Andric 
80406c3fb27SDimitry Andric class PointerDereferenceGadget : public FixableGadget {
80506c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
80606c3fb27SDimitry Andric   static constexpr const char *const OperatorTag = "op";
80706c3fb27SDimitry Andric 
80806c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
80906c3fb27SDimitry Andric   const UnaryOperator *Op = nullptr;
81006c3fb27SDimitry Andric 
81106c3fb27SDimitry Andric public:
81206c3fb27SDimitry Andric   PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
81306c3fb27SDimitry Andric       : FixableGadget(Kind::PointerDereference),
81406c3fb27SDimitry Andric         BaseDeclRefExpr(
81506c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
81606c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
81706c3fb27SDimitry Andric 
81806c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
81906c3fb27SDimitry Andric     return G->getKind() == Kind::PointerDereference;
82006c3fb27SDimitry Andric   }
82106c3fb27SDimitry Andric 
82206c3fb27SDimitry Andric   static Matcher matcher() {
82306c3fb27SDimitry Andric     auto Target =
82406c3fb27SDimitry Andric         unaryOperator(
82506c3fb27SDimitry Andric             hasOperatorName("*"),
82606c3fb27SDimitry Andric             has(expr(ignoringParenImpCasts(
827*5f757f3fSDimitry Andric                 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
82806c3fb27SDimitry Andric             .bind(OperatorTag);
82906c3fb27SDimitry Andric 
83006c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
83106c3fb27SDimitry Andric   }
83206c3fb27SDimitry Andric 
83306c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
83406c3fb27SDimitry Andric     return {BaseDeclRefExpr};
83506c3fb27SDimitry Andric   }
83606c3fb27SDimitry Andric 
83706c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const final { return Op; }
83806c3fb27SDimitry Andric 
83906c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
84006c3fb27SDimitry Andric };
84106c3fb27SDimitry Andric 
84206c3fb27SDimitry Andric // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
84306c3fb27SDimitry Andric // Context (see `isInUnspecifiedPointerContext`).
84406c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
84506c3fb27SDimitry Andric class UPCAddressofArraySubscriptGadget : public FixableGadget {
84606c3fb27SDimitry Andric private:
84706c3fb27SDimitry Andric   static constexpr const char *const UPCAddressofArraySubscriptTag =
84806c3fb27SDimitry Andric       "AddressofArraySubscriptUnderUPC";
84906c3fb27SDimitry Andric   const UnaryOperator *Node; // the `&DRE[any]` node
85006c3fb27SDimitry Andric 
85106c3fb27SDimitry Andric public:
85206c3fb27SDimitry Andric   UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
85306c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
85406c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<UnaryOperator>(
85506c3fb27SDimitry Andric             UPCAddressofArraySubscriptTag)) {
85606c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
85706c3fb27SDimitry Andric   }
85806c3fb27SDimitry Andric 
85906c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
86006c3fb27SDimitry Andric     return G->getKind() == Kind::UPCAddressofArraySubscript;
86106c3fb27SDimitry Andric   }
86206c3fb27SDimitry Andric 
86306c3fb27SDimitry Andric   static Matcher matcher() {
86406c3fb27SDimitry Andric     return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
86506c3fb27SDimitry Andric         unaryOperator(hasOperatorName("&"),
86606c3fb27SDimitry Andric                       hasUnaryOperand(arraySubscriptExpr(
867*5f757f3fSDimitry Andric                           hasBase(ignoringParenImpCasts(declRefExpr(
868*5f757f3fSDimitry Andric                                                   toSupportedVariable()))))))
86906c3fb27SDimitry Andric             .bind(UPCAddressofArraySubscriptTag)))));
87006c3fb27SDimitry Andric   }
87106c3fb27SDimitry Andric 
87206c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &) const override;
87306c3fb27SDimitry Andric 
87406c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
87506c3fb27SDimitry Andric 
87606c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
87706c3fb27SDimitry Andric     const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
87806c3fb27SDimitry Andric     const auto *DRE =
87906c3fb27SDimitry Andric         cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreImpCasts());
88006c3fb27SDimitry Andric     return {DRE};
88106c3fb27SDimitry Andric   }
88206c3fb27SDimitry Andric };
883bdd1243dSDimitry Andric } // namespace
884bdd1243dSDimitry Andric 
885bdd1243dSDimitry Andric namespace {
886bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect
88706c3fb27SDimitry Andric // declarations to its uses and make sure we've covered all uses with our
88806c3fb27SDimitry Andric // analysis before we try to fix the declaration.
889bdd1243dSDimitry Andric class DeclUseTracker {
890bdd1243dSDimitry Andric   using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
891bdd1243dSDimitry Andric   using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
892bdd1243dSDimitry Andric 
893bdd1243dSDimitry Andric   // Allocate on the heap for easier move.
894bdd1243dSDimitry Andric   std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
895bdd1243dSDimitry Andric   DefMapTy Defs{};
896bdd1243dSDimitry Andric 
897bdd1243dSDimitry Andric public:
898bdd1243dSDimitry Andric   DeclUseTracker() = default;
899bdd1243dSDimitry Andric   DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
90006c3fb27SDimitry Andric   DeclUseTracker &operator=(const DeclUseTracker &) = delete;
901bdd1243dSDimitry Andric   DeclUseTracker(DeclUseTracker &&) = default;
902bdd1243dSDimitry Andric   DeclUseTracker &operator=(DeclUseTracker &&) = default;
903bdd1243dSDimitry Andric 
904bdd1243dSDimitry Andric   // Start tracking a freshly discovered DRE.
905bdd1243dSDimitry Andric   void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
906bdd1243dSDimitry Andric 
907bdd1243dSDimitry Andric   // Stop tracking the DRE as it's been fully figured out.
908bdd1243dSDimitry Andric   void claimUse(const DeclRefExpr *DRE) {
909bdd1243dSDimitry Andric     assert(Uses->count(DRE) &&
910bdd1243dSDimitry Andric            "DRE not found or claimed by multiple matchers!");
911bdd1243dSDimitry Andric     Uses->erase(DRE);
912bdd1243dSDimitry Andric   }
913bdd1243dSDimitry Andric 
914bdd1243dSDimitry Andric   // A variable is unclaimed if at least one use is unclaimed.
915bdd1243dSDimitry Andric   bool hasUnclaimedUses(const VarDecl *VD) const {
916bdd1243dSDimitry Andric     // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
917bdd1243dSDimitry Andric     return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
918bdd1243dSDimitry Andric       return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
919bdd1243dSDimitry Andric     });
920bdd1243dSDimitry Andric   }
921bdd1243dSDimitry Andric 
922*5f757f3fSDimitry Andric   UseSetTy getUnclaimedUses(const VarDecl *VD) const {
923*5f757f3fSDimitry Andric     UseSetTy ReturnSet;
924*5f757f3fSDimitry Andric     for (auto use : *Uses) {
925*5f757f3fSDimitry Andric       if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
926*5f757f3fSDimitry Andric         ReturnSet.insert(use);
927*5f757f3fSDimitry Andric       }
928*5f757f3fSDimitry Andric     }
929*5f757f3fSDimitry Andric     return ReturnSet;
930*5f757f3fSDimitry Andric   }
931*5f757f3fSDimitry Andric 
932bdd1243dSDimitry Andric   void discoverDecl(const DeclStmt *DS) {
933bdd1243dSDimitry Andric     for (const Decl *D : DS->decls()) {
934bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(D)) {
935bdd1243dSDimitry Andric         // FIXME: Assertion temporarily disabled due to a bug in
936bdd1243dSDimitry Andric         // ASTMatcher internal behavior in presence of GNU
937bdd1243dSDimitry Andric         // statement-expressions. We need to properly investigate this
938bdd1243dSDimitry Andric         // because it can screw up our algorithm in other ways.
939bdd1243dSDimitry Andric         // assert(Defs.count(VD) == 0 && "Definition already discovered!");
940bdd1243dSDimitry Andric         Defs[VD] = DS;
941bdd1243dSDimitry Andric       }
942bdd1243dSDimitry Andric     }
943bdd1243dSDimitry Andric   }
944bdd1243dSDimitry Andric 
945bdd1243dSDimitry Andric   const DeclStmt *lookupDecl(const VarDecl *VD) const {
946*5f757f3fSDimitry Andric     return Defs.lookup(VD);
947bdd1243dSDimitry Andric   }
948bdd1243dSDimitry Andric };
949bdd1243dSDimitry Andric } // namespace
950bdd1243dSDimitry Andric 
951bdd1243dSDimitry Andric namespace {
952bdd1243dSDimitry Andric // Strategy is a map from variables to the way we plan to emit fixes for
953bdd1243dSDimitry Andric // these variables. It is figured out gradually by trying different fixes
954bdd1243dSDimitry Andric // for different variables depending on gadgets in which these variables
955bdd1243dSDimitry Andric // participate.
956bdd1243dSDimitry Andric class Strategy {
957bdd1243dSDimitry Andric public:
958bdd1243dSDimitry Andric   enum class Kind {
959bdd1243dSDimitry Andric     Wontfix,  // We don't plan to emit a fixit for this variable.
960bdd1243dSDimitry Andric     Span,     // We recommend replacing the variable with std::span.
961bdd1243dSDimitry Andric     Iterator, // We recommend replacing the variable with std::span::iterator.
962bdd1243dSDimitry Andric     Array,    // We recommend replacing the variable with std::array.
963bdd1243dSDimitry Andric     Vector    // We recommend replacing the variable with std::vector.
964bdd1243dSDimitry Andric   };
965bdd1243dSDimitry Andric 
966bdd1243dSDimitry Andric private:
967bdd1243dSDimitry Andric   using MapTy = llvm::DenseMap<const VarDecl *, Kind>;
968bdd1243dSDimitry Andric 
969bdd1243dSDimitry Andric   MapTy Map;
970bdd1243dSDimitry Andric 
971bdd1243dSDimitry Andric public:
972bdd1243dSDimitry Andric   Strategy() = default;
973bdd1243dSDimitry Andric   Strategy(const Strategy &) = delete; // Let's avoid copies.
97406c3fb27SDimitry Andric   Strategy &operator=(const Strategy &) = delete;
975bdd1243dSDimitry Andric   Strategy(Strategy &&) = default;
97606c3fb27SDimitry Andric   Strategy &operator=(Strategy &&) = default;
977bdd1243dSDimitry Andric 
97806c3fb27SDimitry Andric   void set(const VarDecl *VD, Kind K) { Map[VD] = K; }
979bdd1243dSDimitry Andric 
980bdd1243dSDimitry Andric   Kind lookup(const VarDecl *VD) const {
981bdd1243dSDimitry Andric     auto I = Map.find(VD);
982bdd1243dSDimitry Andric     if (I == Map.end())
983bdd1243dSDimitry Andric       return Kind::Wontfix;
984bdd1243dSDimitry Andric 
985bdd1243dSDimitry Andric     return I->second;
986bdd1243dSDimitry Andric   }
987bdd1243dSDimitry Andric };
988bdd1243dSDimitry Andric } // namespace
989bdd1243dSDimitry Andric 
99006c3fb27SDimitry Andric 
99106c3fb27SDimitry Andric // Representing a pointer type expression of the form `++Ptr` in an Unspecified
99206c3fb27SDimitry Andric // Pointer Context (UPC):
99306c3fb27SDimitry Andric class UPCPreIncrementGadget : public FixableGadget {
99406c3fb27SDimitry Andric private:
99506c3fb27SDimitry Andric   static constexpr const char *const UPCPreIncrementTag =
99606c3fb27SDimitry Andric     "PointerPreIncrementUnderUPC";
99706c3fb27SDimitry Andric   const UnaryOperator *Node; // the `++Ptr` node
99806c3fb27SDimitry Andric 
99906c3fb27SDimitry Andric public:
100006c3fb27SDimitry Andric   UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
100106c3fb27SDimitry Andric     : FixableGadget(Kind::UPCPreIncrement),
100206c3fb27SDimitry Andric       Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
100306c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
100406c3fb27SDimitry Andric   }
100506c3fb27SDimitry Andric 
100606c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
100706c3fb27SDimitry Andric     return G->getKind() == Kind::UPCPreIncrement;
100806c3fb27SDimitry Andric   }
100906c3fb27SDimitry Andric 
101006c3fb27SDimitry Andric   static Matcher matcher() {
101106c3fb27SDimitry Andric     // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
101206c3fb27SDimitry Andric     // Although currently we can only provide fix-its when `Ptr` is a DRE, we
101306c3fb27SDimitry Andric     // can have the matcher be general, so long as `getClaimedVarUseSites` does
101406c3fb27SDimitry Andric     // things right.
101506c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
101606c3fb27SDimitry Andric 								    unaryOperator(isPreInc(),
1017*5f757f3fSDimitry Andric 										  hasUnaryOperand(declRefExpr(
1018*5f757f3fSDimitry Andric                                                     toSupportedVariable()))
101906c3fb27SDimitry Andric 										  ).bind(UPCPreIncrementTag)))));
102006c3fb27SDimitry Andric   }
102106c3fb27SDimitry Andric 
102206c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
102306c3fb27SDimitry Andric 
102406c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
102506c3fb27SDimitry Andric 
102606c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
102706c3fb27SDimitry Andric     return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
102806c3fb27SDimitry Andric   }
102906c3fb27SDimitry Andric };
103006c3fb27SDimitry Andric 
1031*5f757f3fSDimitry Andric // Representing a pointer type expression of the form `Ptr += n` in an
1032*5f757f3fSDimitry Andric // Unspecified Untyped Context (UUC):
1033*5f757f3fSDimitry Andric class UUCAddAssignGadget : public FixableGadget {
1034*5f757f3fSDimitry Andric private:
1035*5f757f3fSDimitry Andric   static constexpr const char *const UUCAddAssignTag =
1036*5f757f3fSDimitry Andric       "PointerAddAssignUnderUUC";
1037*5f757f3fSDimitry Andric   static constexpr const char *const OffsetTag = "Offset";
1038*5f757f3fSDimitry Andric 
1039*5f757f3fSDimitry Andric   const BinaryOperator *Node; // the `Ptr += n` node
1040*5f757f3fSDimitry Andric   const Expr *Offset = nullptr;
1041*5f757f3fSDimitry Andric 
1042*5f757f3fSDimitry Andric public:
1043*5f757f3fSDimitry Andric   UUCAddAssignGadget(const MatchFinder::MatchResult &Result)
1044*5f757f3fSDimitry Andric       : FixableGadget(Kind::UUCAddAssign),
1045*5f757f3fSDimitry Andric         Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1046*5f757f3fSDimitry Andric         Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1047*5f757f3fSDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
1048*5f757f3fSDimitry Andric   }
1049*5f757f3fSDimitry Andric 
1050*5f757f3fSDimitry Andric   static bool classof(const Gadget *G) {
1051*5f757f3fSDimitry Andric     return G->getKind() == Kind::UUCAddAssign;
1052*5f757f3fSDimitry Andric   }
1053*5f757f3fSDimitry Andric 
1054*5f757f3fSDimitry Andric   static Matcher matcher() {
1055*5f757f3fSDimitry Andric     return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
1056*5f757f3fSDimitry Andric         binaryOperator(hasOperatorName("+="),
1057*5f757f3fSDimitry Andric                        hasLHS(declRefExpr(toSupportedVariable())),
1058*5f757f3fSDimitry Andric                        hasRHS(expr().bind(OffsetTag)))
1059*5f757f3fSDimitry Andric             .bind(UUCAddAssignTag)))));
1060*5f757f3fSDimitry Andric   }
1061*5f757f3fSDimitry Andric 
1062*5f757f3fSDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
1063*5f757f3fSDimitry Andric 
1064*5f757f3fSDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
1065*5f757f3fSDimitry Andric 
1066*5f757f3fSDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
1067*5f757f3fSDimitry Andric     return {dyn_cast<DeclRefExpr>(Node->getLHS())};
1068*5f757f3fSDimitry Andric   }
1069*5f757f3fSDimitry Andric };
1070*5f757f3fSDimitry Andric 
107106c3fb27SDimitry Andric // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
107206c3fb27SDimitry Andric // ptr)`:
107306c3fb27SDimitry Andric class DerefSimplePtrArithFixableGadget : public FixableGadget {
107406c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
107506c3fb27SDimitry Andric   static constexpr const char *const DerefOpTag = "DerefOp";
107606c3fb27SDimitry Andric   static constexpr const char *const AddOpTag = "AddOp";
107706c3fb27SDimitry Andric   static constexpr const char *const OffsetTag = "Offset";
107806c3fb27SDimitry Andric 
107906c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
108006c3fb27SDimitry Andric   const UnaryOperator *DerefOp = nullptr;
108106c3fb27SDimitry Andric   const BinaryOperator *AddOp = nullptr;
108206c3fb27SDimitry Andric   const IntegerLiteral *Offset = nullptr;
108306c3fb27SDimitry Andric 
108406c3fb27SDimitry Andric public:
108506c3fb27SDimitry Andric   DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)
108606c3fb27SDimitry Andric       : FixableGadget(Kind::DerefSimplePtrArithFixable),
108706c3fb27SDimitry Andric         BaseDeclRefExpr(
108806c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
108906c3fb27SDimitry Andric         DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
109006c3fb27SDimitry Andric         AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
109106c3fb27SDimitry Andric         Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
109206c3fb27SDimitry Andric 
109306c3fb27SDimitry Andric   static Matcher matcher() {
109406c3fb27SDimitry Andric     // clang-format off
109506c3fb27SDimitry Andric     auto ThePtr = expr(hasPointerType(),
1096*5f757f3fSDimitry Andric                        ignoringImpCasts(declRefExpr(toSupportedVariable()).
1097*5f757f3fSDimitry Andric                                         bind(BaseDeclRefExprTag)));
109806c3fb27SDimitry Andric     auto PlusOverPtrAndInteger = expr(anyOf(
109906c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
110006c3fb27SDimitry Andric                          hasRHS(integerLiteral().bind(OffsetTag)))
110106c3fb27SDimitry Andric                          .bind(AddOpTag),
110206c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
110306c3fb27SDimitry Andric                          hasLHS(integerLiteral().bind(OffsetTag)))
110406c3fb27SDimitry Andric                          .bind(AddOpTag)));
110506c3fb27SDimitry Andric     return isInUnspecifiedLvalueContext(unaryOperator(
110606c3fb27SDimitry Andric         hasOperatorName("*"),
110706c3fb27SDimitry Andric         hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
110806c3fb27SDimitry Andric         .bind(DerefOpTag));
110906c3fb27SDimitry Andric     // clang-format on
111006c3fb27SDimitry Andric   }
111106c3fb27SDimitry Andric 
111206c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &s) const final;
111306c3fb27SDimitry Andric 
111406c3fb27SDimitry Andric   // TODO remove this method from FixableGadget interface
111506c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const final { return nullptr; }
111606c3fb27SDimitry Andric 
111706c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const final {
111806c3fb27SDimitry Andric     return {BaseDeclRefExpr};
111906c3fb27SDimitry Andric   }
112006c3fb27SDimitry Andric };
112106c3fb27SDimitry Andric 
1122bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits.
112306c3fb27SDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
112406c3fb27SDimitry Andric findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
112506c3fb27SDimitry Andric             bool EmitSuggestions) {
1126bdd1243dSDimitry Andric 
1127bdd1243dSDimitry Andric   struct GadgetFinderCallback : MatchFinder::MatchCallback {
1128bdd1243dSDimitry Andric     FixableGadgetList FixableGadgets;
1129bdd1243dSDimitry Andric     WarningGadgetList WarningGadgets;
1130bdd1243dSDimitry Andric     DeclUseTracker Tracker;
1131bdd1243dSDimitry Andric 
1132bdd1243dSDimitry Andric     void run(const MatchFinder::MatchResult &Result) override {
1133bdd1243dSDimitry Andric       // In debug mode, assert that we've found exactly one gadget.
1134bdd1243dSDimitry Andric       // This helps us avoid conflicts in .bind() tags.
1135bdd1243dSDimitry Andric #if NDEBUG
1136bdd1243dSDimitry Andric #define NEXT return
1137bdd1243dSDimitry Andric #else
1138bdd1243dSDimitry Andric       [[maybe_unused]] int numFound = 0;
1139bdd1243dSDimitry Andric #define NEXT ++numFound
1140bdd1243dSDimitry Andric #endif
1141bdd1243dSDimitry Andric 
1142bdd1243dSDimitry Andric       if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
1143bdd1243dSDimitry Andric         Tracker.discoverUse(DRE);
1144bdd1243dSDimitry Andric         NEXT;
1145bdd1243dSDimitry Andric       }
1146bdd1243dSDimitry Andric 
1147bdd1243dSDimitry Andric       if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
1148bdd1243dSDimitry Andric         Tracker.discoverDecl(DS);
1149bdd1243dSDimitry Andric         NEXT;
1150bdd1243dSDimitry Andric       }
1151bdd1243dSDimitry Andric 
1152bdd1243dSDimitry Andric       // Figure out which matcher we've found, and call the appropriate
1153bdd1243dSDimitry Andric       // subclass constructor.
1154bdd1243dSDimitry Andric       // FIXME: Can we do this more logarithmically?
1155bdd1243dSDimitry Andric #define FIXABLE_GADGET(name)                                                   \
1156bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1157bdd1243dSDimitry Andric     FixableGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1158bdd1243dSDimitry Andric     NEXT;                                                                      \
1159bdd1243dSDimitry Andric   }
1160bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1161bdd1243dSDimitry Andric #define WARNING_GADGET(name)                                                   \
1162bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1163bdd1243dSDimitry Andric     WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1164bdd1243dSDimitry Andric     NEXT;                                                                      \
1165bdd1243dSDimitry Andric   }
1166bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1167bdd1243dSDimitry Andric 
1168bdd1243dSDimitry Andric       assert(numFound >= 1 && "Gadgets not found in match result!");
1169bdd1243dSDimitry Andric       assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
1170bdd1243dSDimitry Andric     }
1171bdd1243dSDimitry Andric   };
1172bdd1243dSDimitry Andric 
1173bdd1243dSDimitry Andric   MatchFinder M;
1174bdd1243dSDimitry Andric   GadgetFinderCallback CB;
1175bdd1243dSDimitry Andric 
1176bdd1243dSDimitry Andric   // clang-format off
1177bdd1243dSDimitry Andric   M.addMatcher(
117806c3fb27SDimitry Andric       stmt(
117906c3fb27SDimitry Andric         forEachDescendantEvaluatedStmt(stmt(anyOf(
1180bdd1243dSDimitry Andric           // Add Gadget::matcher() for every gadget in the registry.
118106c3fb27SDimitry Andric #define WARNING_GADGET(x)                                                      \
118206c3fb27SDimitry Andric           allOf(x ## Gadget::matcher().bind(#x),                               \
118306c3fb27SDimitry Andric                 notInSafeBufferOptOut(&Handler)),
118406c3fb27SDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
118506c3fb27SDimitry Andric             // Avoid a hanging comma.
118606c3fb27SDimitry Andric             unless(stmt())
118706c3fb27SDimitry Andric         )))
118806c3fb27SDimitry Andric     ),
118906c3fb27SDimitry Andric     &CB
119006c3fb27SDimitry Andric   );
119106c3fb27SDimitry Andric   // clang-format on
119206c3fb27SDimitry Andric 
119306c3fb27SDimitry Andric   if (EmitSuggestions) {
119406c3fb27SDimitry Andric     // clang-format off
119506c3fb27SDimitry Andric     M.addMatcher(
119606c3fb27SDimitry Andric         stmt(
119706c3fb27SDimitry Andric           forEachDescendantStmt(stmt(eachOf(
119806c3fb27SDimitry Andric #define FIXABLE_GADGET(x)                                                      \
1199bdd1243dSDimitry Andric             x ## Gadget::matcher().bind(#x),
1200bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1201bdd1243dSDimitry Andric             // In parallel, match all DeclRefExprs so that to find out
1202bdd1243dSDimitry Andric             // whether there are any uncovered by gadgets.
1203bdd1243dSDimitry Andric             declRefExpr(anyOf(hasPointerType(), hasArrayType()),
1204*5f757f3fSDimitry Andric                         to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
1205bdd1243dSDimitry Andric             // Also match DeclStmts because we'll need them when fixing
1206bdd1243dSDimitry Andric             // their underlying VarDecls that otherwise don't have
1207bdd1243dSDimitry Andric             // any backreferences to DeclStmts.
1208bdd1243dSDimitry Andric             declStmt().bind("any_ds")
120906c3fb27SDimitry Andric           )))
121006c3fb27SDimitry Andric       ),
1211bdd1243dSDimitry Andric       &CB
1212bdd1243dSDimitry Andric     );
1213bdd1243dSDimitry Andric     // clang-format on
121406c3fb27SDimitry Andric   }
1215bdd1243dSDimitry Andric 
1216bdd1243dSDimitry Andric   M.match(*D->getBody(), D->getASTContext());
121706c3fb27SDimitry Andric   return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
121806c3fb27SDimitry Andric           std::move(CB.Tracker)};
1219bdd1243dSDimitry Andric }
1220bdd1243dSDimitry Andric 
122106c3fb27SDimitry Andric // Compares AST nodes by source locations.
122206c3fb27SDimitry Andric template <typename NodeTy> struct CompareNode {
122306c3fb27SDimitry Andric   bool operator()(const NodeTy *N1, const NodeTy *N2) const {
122406c3fb27SDimitry Andric     return N1->getBeginLoc().getRawEncoding() <
122506c3fb27SDimitry Andric            N2->getBeginLoc().getRawEncoding();
122606c3fb27SDimitry Andric   }
122706c3fb27SDimitry Andric };
122806c3fb27SDimitry Andric 
1229bdd1243dSDimitry Andric struct WarningGadgetSets {
123006c3fb27SDimitry Andric   std::map<const VarDecl *, std::set<const WarningGadget *>,
123106c3fb27SDimitry Andric            // To keep keys sorted by their locations in the map so that the
123206c3fb27SDimitry Andric            // order is deterministic:
123306c3fb27SDimitry Andric            CompareNode<VarDecl>>
123406c3fb27SDimitry Andric       byVar;
1235bdd1243dSDimitry Andric   // These Gadgets are not related to pointer variables (e. g. temporaries).
123606c3fb27SDimitry Andric   llvm::SmallVector<const WarningGadget *, 16> noVar;
1237bdd1243dSDimitry Andric };
1238bdd1243dSDimitry Andric 
1239bdd1243dSDimitry Andric static WarningGadgetSets
124006c3fb27SDimitry Andric groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
1241bdd1243dSDimitry Andric   WarningGadgetSets result;
1242bdd1243dSDimitry Andric   // If some gadgets cover more than one
1243bdd1243dSDimitry Andric   // variable, they'll appear more than once in the map.
1244bdd1243dSDimitry Andric   for (auto &G : AllUnsafeOperations) {
1245bdd1243dSDimitry Andric     DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
1246bdd1243dSDimitry Andric 
1247bdd1243dSDimitry Andric     bool AssociatedWithVarDecl = false;
1248bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
1249bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
125006c3fb27SDimitry Andric         result.byVar[VD].insert(G.get());
1251bdd1243dSDimitry Andric         AssociatedWithVarDecl = true;
1252bdd1243dSDimitry Andric       }
1253bdd1243dSDimitry Andric     }
1254bdd1243dSDimitry Andric 
1255bdd1243dSDimitry Andric     if (!AssociatedWithVarDecl) {
125606c3fb27SDimitry Andric       result.noVar.push_back(G.get());
1257bdd1243dSDimitry Andric       continue;
1258bdd1243dSDimitry Andric     }
1259bdd1243dSDimitry Andric   }
1260bdd1243dSDimitry Andric   return result;
1261bdd1243dSDimitry Andric }
1262bdd1243dSDimitry Andric 
1263bdd1243dSDimitry Andric struct FixableGadgetSets {
1264*5f757f3fSDimitry Andric   std::map<const VarDecl *, std::set<const FixableGadget *>,
1265*5f757f3fSDimitry Andric            // To keep keys sorted by their locations in the map so that the
1266*5f757f3fSDimitry Andric            // order is deterministic:
1267*5f757f3fSDimitry Andric            CompareNode<VarDecl>>
1268*5f757f3fSDimitry Andric       byVar;
1269bdd1243dSDimitry Andric };
1270bdd1243dSDimitry Andric 
1271bdd1243dSDimitry Andric static FixableGadgetSets
1272bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
1273bdd1243dSDimitry Andric   FixableGadgetSets FixablesForUnsafeVars;
1274bdd1243dSDimitry Andric   for (auto &F : AllFixableOperations) {
1275bdd1243dSDimitry Andric     DeclUseList DREs = F->getClaimedVarUseSites();
1276bdd1243dSDimitry Andric 
1277bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : DREs) {
1278bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
127906c3fb27SDimitry Andric         FixablesForUnsafeVars.byVar[VD].insert(F.get());
1280bdd1243dSDimitry Andric       }
1281bdd1243dSDimitry Andric     }
1282bdd1243dSDimitry Andric   }
1283bdd1243dSDimitry Andric   return FixablesForUnsafeVars;
1284bdd1243dSDimitry Andric }
1285bdd1243dSDimitry Andric 
128606c3fb27SDimitry Andric bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts,
128706c3fb27SDimitry Andric                                   const SourceManager &SM) {
128806c3fb27SDimitry Andric   // A simple interval overlap detection algorithm.  Sorts all ranges by their
128906c3fb27SDimitry Andric   // begin location then finds the first overlap in one pass.
129006c3fb27SDimitry Andric   std::vector<const FixItHint *> All; // a copy of `FixIts`
129106c3fb27SDimitry Andric 
129206c3fb27SDimitry Andric   for (const FixItHint &H : FixIts)
129306c3fb27SDimitry Andric     All.push_back(&H);
129406c3fb27SDimitry Andric   std::sort(All.begin(), All.end(),
129506c3fb27SDimitry Andric             [&SM](const FixItHint *H1, const FixItHint *H2) {
129606c3fb27SDimitry Andric               return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
129706c3fb27SDimitry Andric                                                   H2->RemoveRange.getBegin());
129806c3fb27SDimitry Andric             });
129906c3fb27SDimitry Andric 
130006c3fb27SDimitry Andric   const FixItHint *CurrHint = nullptr;
130106c3fb27SDimitry Andric 
130206c3fb27SDimitry Andric   for (const FixItHint *Hint : All) {
130306c3fb27SDimitry Andric     if (!CurrHint ||
130406c3fb27SDimitry Andric         SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
130506c3fb27SDimitry Andric                                      Hint->RemoveRange.getBegin())) {
130606c3fb27SDimitry Andric       // Either to initialize `CurrHint` or `CurrHint` does not
130706c3fb27SDimitry Andric       // overlap with `Hint`:
130806c3fb27SDimitry Andric       CurrHint = Hint;
130906c3fb27SDimitry Andric     } else
131006c3fb27SDimitry Andric       // In case `Hint` overlaps the `CurrHint`, we found at least one
131106c3fb27SDimitry Andric       // conflict:
131206c3fb27SDimitry Andric       return true;
131306c3fb27SDimitry Andric   }
131406c3fb27SDimitry Andric   return false;
131506c3fb27SDimitry Andric }
131606c3fb27SDimitry Andric 
131706c3fb27SDimitry Andric std::optional<FixItList>
131806c3fb27SDimitry Andric PointerAssignmentGadget::getFixits(const Strategy &S) const {
131906c3fb27SDimitry Andric   const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
132006c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
132106c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
132206c3fb27SDimitry Andric     case Strategy::Kind::Span:
132306c3fb27SDimitry Andric       if (S.lookup(RightVD) == Strategy::Kind::Span)
132406c3fb27SDimitry Andric         return FixItList{};
132506c3fb27SDimitry Andric       return std::nullopt;
132606c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
132706c3fb27SDimitry Andric       return std::nullopt;
132806c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
132906c3fb27SDimitry Andric     case Strategy::Kind::Array:
133006c3fb27SDimitry Andric     case Strategy::Kind::Vector:
133106c3fb27SDimitry Andric       llvm_unreachable("unsupported strategies for FixableGadgets");
133206c3fb27SDimitry Andric   }
133306c3fb27SDimitry Andric   return std::nullopt;
133406c3fb27SDimitry Andric }
133506c3fb27SDimitry Andric 
133606c3fb27SDimitry Andric std::optional<FixItList>
133706c3fb27SDimitry Andric PointerInitGadget::getFixits(const Strategy &S) const {
133806c3fb27SDimitry Andric   const auto *LeftVD = PtrInitLHS;
133906c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
134006c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
134106c3fb27SDimitry Andric     case Strategy::Kind::Span:
134206c3fb27SDimitry Andric       if (S.lookup(RightVD) == Strategy::Kind::Span)
134306c3fb27SDimitry Andric         return FixItList{};
134406c3fb27SDimitry Andric       return std::nullopt;
134506c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
134606c3fb27SDimitry Andric       return std::nullopt;
134706c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
134806c3fb27SDimitry Andric     case Strategy::Kind::Array:
134906c3fb27SDimitry Andric     case Strategy::Kind::Vector:
135006c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
135106c3fb27SDimitry Andric   }
135206c3fb27SDimitry Andric   return std::nullopt;
135306c3fb27SDimitry Andric }
135406c3fb27SDimitry Andric 
1355*5f757f3fSDimitry Andric static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
1356*5f757f3fSDimitry Andric                                      const ASTContext &Ctx) {
1357*5f757f3fSDimitry Andric   if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
1358*5f757f3fSDimitry Andric     if (ConstVal->isNegative())
1359*5f757f3fSDimitry Andric       return false;
1360*5f757f3fSDimitry Andric   } else if (!Expr->getType()->isUnsignedIntegerType())
1361*5f757f3fSDimitry Andric     return false;
1362*5f757f3fSDimitry Andric   return true;
1363*5f757f3fSDimitry Andric }
1364*5f757f3fSDimitry Andric 
136506c3fb27SDimitry Andric std::optional<FixItList>
136606c3fb27SDimitry Andric ULCArraySubscriptGadget::getFixits(const Strategy &S) const {
136706c3fb27SDimitry Andric   if (const auto *DRE =
136806c3fb27SDimitry Andric           dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
136906c3fb27SDimitry Andric     if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
137006c3fb27SDimitry Andric       switch (S.lookup(VD)) {
137106c3fb27SDimitry Andric       case Strategy::Kind::Span: {
1372*5f757f3fSDimitry Andric 
137306c3fb27SDimitry Andric         // If the index has a negative constant value, we give up as no valid
137406c3fb27SDimitry Andric         // fix-it can be generated:
137506c3fb27SDimitry Andric         const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
137606c3fb27SDimitry Andric             VD->getASTContext();
1377*5f757f3fSDimitry Andric         if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
137806c3fb27SDimitry Andric           return std::nullopt;
137906c3fb27SDimitry Andric         // no-op is a good fix-it, otherwise
138006c3fb27SDimitry Andric         return FixItList{};
138106c3fb27SDimitry Andric       }
138206c3fb27SDimitry Andric       case Strategy::Kind::Wontfix:
138306c3fb27SDimitry Andric       case Strategy::Kind::Iterator:
138406c3fb27SDimitry Andric       case Strategy::Kind::Array:
138506c3fb27SDimitry Andric       case Strategy::Kind::Vector:
138606c3fb27SDimitry Andric         llvm_unreachable("unsupported strategies for FixableGadgets");
138706c3fb27SDimitry Andric       }
138806c3fb27SDimitry Andric     }
138906c3fb27SDimitry Andric   return std::nullopt;
139006c3fb27SDimitry Andric }
139106c3fb27SDimitry Andric 
139206c3fb27SDimitry Andric static std::optional<FixItList> // forward declaration
139306c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
139406c3fb27SDimitry Andric 
139506c3fb27SDimitry Andric std::optional<FixItList>
139606c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget::getFixits(const Strategy &S) const {
139706c3fb27SDimitry Andric   auto DREs = getClaimedVarUseSites();
139806c3fb27SDimitry Andric   const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
139906c3fb27SDimitry Andric 
140006c3fb27SDimitry Andric   switch (S.lookup(VD)) {
140106c3fb27SDimitry Andric   case Strategy::Kind::Span:
140206c3fb27SDimitry Andric     return fixUPCAddressofArraySubscriptWithSpan(Node);
140306c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
140406c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
140506c3fb27SDimitry Andric   case Strategy::Kind::Array:
140606c3fb27SDimitry Andric   case Strategy::Kind::Vector:
140706c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
140806c3fb27SDimitry Andric   }
140906c3fb27SDimitry Andric   return std::nullopt; // something went wrong, no fix-it
141006c3fb27SDimitry Andric }
141106c3fb27SDimitry Andric 
141206c3fb27SDimitry Andric // FIXME: this function should be customizable through format
141306c3fb27SDimitry Andric static StringRef getEndOfLine() {
141406c3fb27SDimitry Andric   static const char *const EOL = "\n";
141506c3fb27SDimitry Andric   return EOL;
141606c3fb27SDimitry Andric }
141706c3fb27SDimitry Andric 
141806c3fb27SDimitry Andric // Returns the text indicating that the user needs to provide input there:
141906c3fb27SDimitry Andric std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
142006c3fb27SDimitry Andric   std::string s = std::string("<# ");
142106c3fb27SDimitry Andric   s += HintTextToUser;
142206c3fb27SDimitry Andric   s += " #>";
142306c3fb27SDimitry Andric   return s;
142406c3fb27SDimitry Andric }
142506c3fb27SDimitry Andric 
142606c3fb27SDimitry Andric // Return the text representation of the given `APInt Val`:
142706c3fb27SDimitry Andric static std::string getAPIntText(APInt Val) {
142806c3fb27SDimitry Andric   SmallVector<char> Txt;
142906c3fb27SDimitry Andric   Val.toString(Txt, 10, true);
143006c3fb27SDimitry Andric   // APInt::toString does not add '\0' to the end of the string for us:
143106c3fb27SDimitry Andric   Txt.push_back('\0');
143206c3fb27SDimitry Andric   return Txt.data();
143306c3fb27SDimitry Andric }
143406c3fb27SDimitry Andric 
143506c3fb27SDimitry Andric // Return the source location of the last character of the AST `Node`.
143606c3fb27SDimitry Andric template <typename NodeTy>
143706c3fb27SDimitry Andric static std::optional<SourceLocation>
143806c3fb27SDimitry Andric getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
143906c3fb27SDimitry Andric               const LangOptions &LangOpts) {
144006c3fb27SDimitry Andric   unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
144106c3fb27SDimitry Andric   SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
144206c3fb27SDimitry Andric 
144306c3fb27SDimitry Andric   if (Loc.isValid())
144406c3fb27SDimitry Andric     return Loc;
144506c3fb27SDimitry Andric 
144606c3fb27SDimitry Andric   return std::nullopt;
144706c3fb27SDimitry Andric }
144806c3fb27SDimitry Andric 
144906c3fb27SDimitry Andric // Return the source location just past the last character of the AST `Node`.
145006c3fb27SDimitry Andric template <typename NodeTy>
145106c3fb27SDimitry Andric static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
145206c3fb27SDimitry Andric                                                 const SourceManager &SM,
145306c3fb27SDimitry Andric                                                 const LangOptions &LangOpts) {
145406c3fb27SDimitry Andric   SourceLocation Loc =
145506c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
145606c3fb27SDimitry Andric   if (Loc.isValid())
145706c3fb27SDimitry Andric     return Loc;
145806c3fb27SDimitry Andric   return std::nullopt;
145906c3fb27SDimitry Andric }
146006c3fb27SDimitry Andric 
146106c3fb27SDimitry Andric // Return text representation of an `Expr`.
146206c3fb27SDimitry Andric static std::optional<StringRef> getExprText(const Expr *E,
146306c3fb27SDimitry Andric                                             const SourceManager &SM,
146406c3fb27SDimitry Andric                                             const LangOptions &LangOpts) {
146506c3fb27SDimitry Andric   std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
146606c3fb27SDimitry Andric 
146706c3fb27SDimitry Andric   if (LastCharLoc)
146806c3fb27SDimitry Andric     return Lexer::getSourceText(
146906c3fb27SDimitry Andric         CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
147006c3fb27SDimitry Andric         LangOpts);
147106c3fb27SDimitry Andric 
147206c3fb27SDimitry Andric   return std::nullopt;
147306c3fb27SDimitry Andric }
147406c3fb27SDimitry Andric 
147506c3fb27SDimitry Andric // Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
147606c3fb27SDimitry Andric static std::optional<StringRef> getRangeText(SourceRange SR,
147706c3fb27SDimitry Andric                                              const SourceManager &SM,
147806c3fb27SDimitry Andric                                              const LangOptions &LangOpts) {
147906c3fb27SDimitry Andric   bool Invalid = false;
1480*5f757f3fSDimitry Andric   CharSourceRange CSR = CharSourceRange::getCharRange(SR);
148106c3fb27SDimitry Andric   StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
148206c3fb27SDimitry Andric 
148306c3fb27SDimitry Andric   if (!Invalid)
148406c3fb27SDimitry Andric     return Text;
148506c3fb27SDimitry Andric   return std::nullopt;
148606c3fb27SDimitry Andric }
148706c3fb27SDimitry Andric 
1488*5f757f3fSDimitry Andric // Returns the begin location of the identifier of the given variable
1489*5f757f3fSDimitry Andric // declaration.
1490*5f757f3fSDimitry Andric static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) {
1491*5f757f3fSDimitry Andric   // According to the implementation of `VarDecl`, `VD->getLocation()` actually
1492*5f757f3fSDimitry Andric   // returns the begin location of the identifier of the declaration:
1493*5f757f3fSDimitry Andric   return VD->getLocation();
1494*5f757f3fSDimitry Andric }
1495*5f757f3fSDimitry Andric 
1496*5f757f3fSDimitry Andric // Returns the literal text of the identifier of the given variable declaration.
1497*5f757f3fSDimitry Andric static std::optional<StringRef>
1498*5f757f3fSDimitry Andric getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM,
1499*5f757f3fSDimitry Andric                          const LangOptions &LangOpts) {
1500*5f757f3fSDimitry Andric   SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
1501*5f757f3fSDimitry Andric   SourceLocation ParmIdentEndLoc =
1502*5f757f3fSDimitry Andric       Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
1503*5f757f3fSDimitry Andric 
1504*5f757f3fSDimitry Andric   if (ParmIdentEndLoc.isMacroID() &&
1505*5f757f3fSDimitry Andric       !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
1506*5f757f3fSDimitry Andric     return std::nullopt;
1507*5f757f3fSDimitry Andric   return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
1508*5f757f3fSDimitry Andric }
1509*5f757f3fSDimitry Andric 
1510*5f757f3fSDimitry Andric // We cannot fix a variable declaration if it has some other specifiers than the
1511*5f757f3fSDimitry Andric // type specifier.  Because the source ranges of those specifiers could overlap
1512*5f757f3fSDimitry Andric // with the source range that is being replaced using fix-its.  Especially when
1513*5f757f3fSDimitry Andric // we often cannot obtain accurate source ranges of cv-qualified type
1514*5f757f3fSDimitry Andric // specifiers.
1515*5f757f3fSDimitry Andric // FIXME: also deal with type attributes
1516*5f757f3fSDimitry Andric static bool hasUnsupportedSpecifiers(const VarDecl *VD,
1517*5f757f3fSDimitry Andric                                      const SourceManager &SM) {
1518*5f757f3fSDimitry Andric   // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
1519*5f757f3fSDimitry Andric   // source range of `VD`:
1520*5f757f3fSDimitry Andric   bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
1521*5f757f3fSDimitry Andric     return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
1522*5f757f3fSDimitry Andric                                           VD->getBeginLoc())) &&
1523*5f757f3fSDimitry Andric            !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
1524*5f757f3fSDimitry Andric                                           At->getRange().getBegin()));
1525*5f757f3fSDimitry Andric   });
1526*5f757f3fSDimitry Andric   return VD->isInlineSpecified() || VD->isConstexpr() ||
1527*5f757f3fSDimitry Andric          VD->hasConstantInitialization() || !VD->hasLocalStorage() ||
1528*5f757f3fSDimitry Andric          AttrRangeOverlapping;
1529*5f757f3fSDimitry Andric }
1530*5f757f3fSDimitry Andric 
1531*5f757f3fSDimitry Andric // Returns the `SourceRange` of `D`.  The reason why this function exists is
1532*5f757f3fSDimitry Andric // that `D->getSourceRange()` may return a range where the end location is the
1533*5f757f3fSDimitry Andric // starting location of the last token.  The end location of the source range
1534*5f757f3fSDimitry Andric // returned by this function is the last location of the last token.
1535*5f757f3fSDimitry Andric static SourceRange getSourceRangeToTokenEnd(const Decl *D,
1536*5f757f3fSDimitry Andric                                             const SourceManager &SM,
1537*5f757f3fSDimitry Andric                                             const LangOptions &LangOpts) {
1538*5f757f3fSDimitry Andric   SourceLocation Begin = D->getBeginLoc();
1539*5f757f3fSDimitry Andric   SourceLocation
1540*5f757f3fSDimitry Andric     End = // `D->getEndLoc` should always return the starting location of the
1541*5f757f3fSDimitry Andric     // last token, so we should get the end of the token
1542*5f757f3fSDimitry Andric     Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
1543*5f757f3fSDimitry Andric 
1544*5f757f3fSDimitry Andric   return SourceRange(Begin, End);
1545*5f757f3fSDimitry Andric }
1546*5f757f3fSDimitry Andric 
154706c3fb27SDimitry Andric // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
154806c3fb27SDimitry Andric // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does not
154906c3fb27SDimitry Andric // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
155006c3fb27SDimitry Andric // :( ), `Qualifiers` of the pointee type is returned separately through the
155106c3fb27SDimitry Andric // output parameter `QualifiersToAppend`.
155206c3fb27SDimitry Andric static std::optional<std::string>
1553*5f757f3fSDimitry Andric getPointeeTypeText(const VarDecl *VD, const SourceManager &SM,
155406c3fb27SDimitry Andric                    const LangOptions &LangOpts,
155506c3fb27SDimitry Andric                    std::optional<Qualifiers> *QualifiersToAppend) {
155606c3fb27SDimitry Andric   QualType Ty = VD->getType();
155706c3fb27SDimitry Andric   QualType PteTy;
155806c3fb27SDimitry Andric 
155906c3fb27SDimitry Andric   assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
156006c3fb27SDimitry Andric          "Expecting a VarDecl of type of pointer to object type");
156106c3fb27SDimitry Andric   PteTy = Ty->getPointeeType();
1562*5f757f3fSDimitry Andric 
1563*5f757f3fSDimitry Andric   TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
1564*5f757f3fSDimitry Andric   TypeLoc PteTyLoc;
1565*5f757f3fSDimitry Andric 
1566*5f757f3fSDimitry Andric   // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
1567*5f757f3fSDimitry Andric   // the `TypeLoc` of the pointee type:
1568*5f757f3fSDimitry Andric   switch (TyLoc.getTypeLocClass()) {
1569*5f757f3fSDimitry Andric   case TypeLoc::ConstantArray:
1570*5f757f3fSDimitry Andric   case TypeLoc::IncompleteArray:
1571*5f757f3fSDimitry Andric   case TypeLoc::VariableArray:
1572*5f757f3fSDimitry Andric   case TypeLoc::DependentSizedArray:
1573*5f757f3fSDimitry Andric   case TypeLoc::Decayed:
1574*5f757f3fSDimitry Andric     assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
1575*5f757f3fSDimitry Andric                                    "pointer type unless it decays.");
1576*5f757f3fSDimitry Andric     PteTyLoc = TyLoc.getNextTypeLoc();
1577*5f757f3fSDimitry Andric     break;
1578*5f757f3fSDimitry Andric   case TypeLoc::Pointer:
1579*5f757f3fSDimitry Andric     PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
1580*5f757f3fSDimitry Andric     break;
1581*5f757f3fSDimitry Andric   default:
1582*5f757f3fSDimitry Andric     return std::nullopt;
1583*5f757f3fSDimitry Andric   }
1584*5f757f3fSDimitry Andric   if (PteTyLoc.isNull())
1585*5f757f3fSDimitry Andric     // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
1586*5f757f3fSDimitry Andric     // when the pointer type is `auto`.
158706c3fb27SDimitry Andric     return std::nullopt;
158806c3fb27SDimitry Andric 
1589*5f757f3fSDimitry Andric   SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD);
159006c3fb27SDimitry Andric 
1591*5f757f3fSDimitry Andric   if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
159206c3fb27SDimitry Andric     // We are expecting these locations to be valid. But in some cases, they are
159306c3fb27SDimitry Andric     // not all valid. It is a Clang bug to me and we are not responsible for
159406c3fb27SDimitry Andric     // fixing it.  So we will just give up for now when it happens.
159506c3fb27SDimitry Andric     return std::nullopt;
159606c3fb27SDimitry Andric   }
159706c3fb27SDimitry Andric 
159806c3fb27SDimitry Andric   // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
159906c3fb27SDimitry Andric   SourceLocation PteEndOfTokenLoc =
160006c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
160106c3fb27SDimitry Andric 
1602*5f757f3fSDimitry Andric   if (!PteEndOfTokenLoc.isValid())
1603*5f757f3fSDimitry Andric     // Sometimes we cannot get the end location of the pointee type, e.g., when
1604*5f757f3fSDimitry Andric     // there are macros involved.
1605*5f757f3fSDimitry Andric     return std::nullopt;
1606*5f757f3fSDimitry Andric   if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
160706c3fb27SDimitry Andric     // We only deal with the cases where the source text of the pointee type
160806c3fb27SDimitry Andric     // appears on the left-hand side of the variable identifier completely,
160906c3fb27SDimitry Andric     // including the following forms:
161006c3fb27SDimitry Andric     // `T ident`,
161106c3fb27SDimitry Andric     // `T ident[]`, where `T` is any type.
161206c3fb27SDimitry Andric     // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
161306c3fb27SDimitry Andric     return std::nullopt;
161406c3fb27SDimitry Andric   }
161506c3fb27SDimitry Andric   if (PteTy.hasQualifiers()) {
161606c3fb27SDimitry Andric     // TypeLoc does not provide source ranges for qualifiers (it says it's
161706c3fb27SDimitry Andric     // intentional but seems fishy to me), so we cannot get the full text
161806c3fb27SDimitry Andric     // `PteTy` via source ranges.
161906c3fb27SDimitry Andric     *QualifiersToAppend = PteTy.getQualifiers();
162006c3fb27SDimitry Andric   }
162106c3fb27SDimitry Andric   return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
162206c3fb27SDimitry Andric       ->str();
162306c3fb27SDimitry Andric }
162406c3fb27SDimitry Andric 
162506c3fb27SDimitry Andric // Returns the text of the name (with qualifiers) of a `FunctionDecl`.
162606c3fb27SDimitry Andric static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
162706c3fb27SDimitry Andric                                                const SourceManager &SM,
162806c3fb27SDimitry Andric                                                const LangOptions &LangOpts) {
162906c3fb27SDimitry Andric   SourceLocation BeginLoc = FD->getQualifier()
163006c3fb27SDimitry Andric                                 ? FD->getQualifierLoc().getBeginLoc()
163106c3fb27SDimitry Andric                                 : FD->getNameInfo().getBeginLoc();
163206c3fb27SDimitry Andric   // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
163306c3fb27SDimitry Andric   // last token:
163406c3fb27SDimitry Andric   SourceLocation EndLoc = Lexer::getLocForEndOfToken(
163506c3fb27SDimitry Andric       FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
163606c3fb27SDimitry Andric   SourceRange NameRange{BeginLoc, EndLoc};
163706c3fb27SDimitry Andric 
163806c3fb27SDimitry Andric   return getRangeText(NameRange, SM, LangOpts);
163906c3fb27SDimitry Andric }
164006c3fb27SDimitry Andric 
1641*5f757f3fSDimitry Andric // Returns the text representing a `std::span` type where the element type is
1642*5f757f3fSDimitry Andric // represented by `EltTyText`.
1643*5f757f3fSDimitry Andric //
1644*5f757f3fSDimitry Andric // Note the optional parameter `Qualifiers`: one needs to pass qualifiers
1645*5f757f3fSDimitry Andric // explicitly if the element type needs to be qualified.
1646*5f757f3fSDimitry Andric static std::string
1647*5f757f3fSDimitry Andric getSpanTypeText(StringRef EltTyText,
1648*5f757f3fSDimitry Andric                 std::optional<Qualifiers> Quals = std::nullopt) {
1649*5f757f3fSDimitry Andric   const char *const SpanOpen = "std::span<";
1650*5f757f3fSDimitry Andric 
1651*5f757f3fSDimitry Andric   if (Quals)
1652*5f757f3fSDimitry Andric     return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
1653*5f757f3fSDimitry Andric   return SpanOpen + EltTyText.str() + '>';
1654*5f757f3fSDimitry Andric }
1655*5f757f3fSDimitry Andric 
165606c3fb27SDimitry Andric std::optional<FixItList>
165706c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget::getFixits(const Strategy &s) const {
165806c3fb27SDimitry Andric   const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
165906c3fb27SDimitry Andric 
166006c3fb27SDimitry Andric   if (VD && s.lookup(VD) == Strategy::Kind::Span) {
166106c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
166206c3fb27SDimitry Andric     // std::span can't represent elements before its begin()
166306c3fb27SDimitry Andric     if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
166406c3fb27SDimitry Andric       if (ConstVal->isNegative())
166506c3fb27SDimitry Andric         return std::nullopt;
166606c3fb27SDimitry Andric 
166706c3fb27SDimitry Andric     // note that the expr may (oddly) has multiple layers of parens
166806c3fb27SDimitry Andric     // example:
166906c3fb27SDimitry Andric     //   *((..(pointer + 123)..))
167006c3fb27SDimitry Andric     // goal:
167106c3fb27SDimitry Andric     //   pointer[123]
167206c3fb27SDimitry Andric     // Fix-It:
167306c3fb27SDimitry Andric     //   remove '*('
167406c3fb27SDimitry Andric     //   replace ' + ' with '['
167506c3fb27SDimitry Andric     //   replace ')' with ']'
167606c3fb27SDimitry Andric 
167706c3fb27SDimitry Andric     // example:
167806c3fb27SDimitry Andric     //   *((..(123 + pointer)..))
167906c3fb27SDimitry Andric     // goal:
168006c3fb27SDimitry Andric     //   123[pointer]
168106c3fb27SDimitry Andric     // Fix-It:
168206c3fb27SDimitry Andric     //   remove '*('
168306c3fb27SDimitry Andric     //   replace ' + ' with '['
168406c3fb27SDimitry Andric     //   replace ')' with ']'
168506c3fb27SDimitry Andric 
168606c3fb27SDimitry Andric     const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
168706c3fb27SDimitry Andric     const SourceManager &SM = Ctx.getSourceManager();
168806c3fb27SDimitry Andric     const LangOptions &LangOpts = Ctx.getLangOpts();
168906c3fb27SDimitry Andric     CharSourceRange StarWithTrailWhitespace =
169006c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
169106c3fb27SDimitry Andric                                              LHS->getBeginLoc());
169206c3fb27SDimitry Andric 
169306c3fb27SDimitry Andric     std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
169406c3fb27SDimitry Andric     if (!LHSLocation)
169506c3fb27SDimitry Andric       return std::nullopt;
169606c3fb27SDimitry Andric 
169706c3fb27SDimitry Andric     CharSourceRange PlusWithSurroundingWhitespace =
169806c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
169906c3fb27SDimitry Andric 
170006c3fb27SDimitry Andric     std::optional<SourceLocation> AddOpLocation =
170106c3fb27SDimitry Andric         getPastLoc(AddOp, SM, LangOpts);
170206c3fb27SDimitry Andric     std::optional<SourceLocation> DerefOpLocation =
170306c3fb27SDimitry Andric         getPastLoc(DerefOp, SM, LangOpts);
170406c3fb27SDimitry Andric 
170506c3fb27SDimitry Andric     if (!AddOpLocation || !DerefOpLocation)
170606c3fb27SDimitry Andric       return std::nullopt;
170706c3fb27SDimitry Andric 
170806c3fb27SDimitry Andric     CharSourceRange ClosingParenWithPrecWhitespace =
170906c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
171006c3fb27SDimitry Andric 
171106c3fb27SDimitry Andric     return FixItList{
171206c3fb27SDimitry Andric         {FixItHint::CreateRemoval(StarWithTrailWhitespace),
171306c3fb27SDimitry Andric          FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
171406c3fb27SDimitry Andric          FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
171506c3fb27SDimitry Andric   }
171606c3fb27SDimitry Andric   return std::nullopt; // something wrong or unsupported, give up
171706c3fb27SDimitry Andric }
171806c3fb27SDimitry Andric 
171906c3fb27SDimitry Andric std::optional<FixItList>
172006c3fb27SDimitry Andric PointerDereferenceGadget::getFixits(const Strategy &S) const {
172106c3fb27SDimitry Andric   const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
172206c3fb27SDimitry Andric   switch (S.lookup(VD)) {
172306c3fb27SDimitry Andric   case Strategy::Kind::Span: {
172406c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
172506c3fb27SDimitry Andric     SourceManager &SM = Ctx.getSourceManager();
172606c3fb27SDimitry Andric     // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
172706c3fb27SDimitry Andric     // Deletes the *operand
172806c3fb27SDimitry Andric     CharSourceRange derefRange = clang::CharSourceRange::getCharRange(
172906c3fb27SDimitry Andric         Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
173006c3fb27SDimitry Andric     // Inserts the [0]
1731*5f757f3fSDimitry Andric     if (auto LocPastOperand =
1732*5f757f3fSDimitry Andric             getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
173306c3fb27SDimitry Andric       return FixItList{{FixItHint::CreateRemoval(derefRange),
1734*5f757f3fSDimitry Andric 			FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
173506c3fb27SDimitry Andric     }
1736*5f757f3fSDimitry Andric     break;
173706c3fb27SDimitry Andric   }
173806c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
173906c3fb27SDimitry Andric   case Strategy::Kind::Array:
174006c3fb27SDimitry Andric   case Strategy::Kind::Vector:
174106c3fb27SDimitry Andric     llvm_unreachable("Strategy not implemented yet!");
174206c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
174306c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
174406c3fb27SDimitry Andric   }
174506c3fb27SDimitry Andric 
174606c3fb27SDimitry Andric   return std::nullopt;
174706c3fb27SDimitry Andric }
174806c3fb27SDimitry Andric 
174906c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form UPC(DRE) with
175006c3fb27SDimitry Andric // `DRE.data()`
175106c3fb27SDimitry Andric std::optional<FixItList> UPCStandalonePointerGadget::getFixits(const Strategy &S)
175206c3fb27SDimitry Andric       const {
175306c3fb27SDimitry Andric   const auto VD = cast<VarDecl>(Node->getDecl());
175406c3fb27SDimitry Andric   switch (S.lookup(VD)) {
175506c3fb27SDimitry Andric     case Strategy::Kind::Span: {
175606c3fb27SDimitry Andric       ASTContext &Ctx = VD->getASTContext();
175706c3fb27SDimitry Andric       SourceManager &SM = Ctx.getSourceManager();
175806c3fb27SDimitry Andric       // Inserts the .data() after the DRE
175906c3fb27SDimitry Andric       std::optional<SourceLocation> EndOfOperand =
176006c3fb27SDimitry Andric           getPastLoc(Node, SM, Ctx.getLangOpts());
176106c3fb27SDimitry Andric 
176206c3fb27SDimitry Andric       if (EndOfOperand)
176306c3fb27SDimitry Andric         return FixItList{{FixItHint::CreateInsertion(
176406c3fb27SDimitry Andric             *EndOfOperand, ".data()")}};
1765*5f757f3fSDimitry Andric       // FIXME: Points inside a macro expansion.
1766*5f757f3fSDimitry Andric       break;
176706c3fb27SDimitry Andric     }
176806c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
176906c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
177006c3fb27SDimitry Andric     case Strategy::Kind::Array:
177106c3fb27SDimitry Andric     case Strategy::Kind::Vector:
177206c3fb27SDimitry Andric       llvm_unreachable("unsupported strategies for FixableGadgets");
177306c3fb27SDimitry Andric   }
177406c3fb27SDimitry Andric 
177506c3fb27SDimitry Andric   return std::nullopt;
177606c3fb27SDimitry Andric }
177706c3fb27SDimitry Andric 
177806c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form `&DRE[e]` with
177906c3fb27SDimitry Andric // `&DRE.data()[e]`:
178006c3fb27SDimitry Andric static std::optional<FixItList>
178106c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) {
178206c3fb27SDimitry Andric   const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
178306c3fb27SDimitry Andric   const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
178406c3fb27SDimitry Andric   // FIXME: this `getASTContext` call is costly, we should pass the
178506c3fb27SDimitry Andric   // ASTContext in:
178606c3fb27SDimitry Andric   const ASTContext &Ctx = DRE->getDecl()->getASTContext();
178706c3fb27SDimitry Andric   const Expr *Idx = ArraySub->getIdx();
178806c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
178906c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
179006c3fb27SDimitry Andric   std::stringstream SS;
179106c3fb27SDimitry Andric   bool IdxIsLitZero = false;
179206c3fb27SDimitry Andric 
179306c3fb27SDimitry Andric   if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
179406c3fb27SDimitry Andric     if ((*ICE).isZero())
179506c3fb27SDimitry Andric       IdxIsLitZero = true;
179606c3fb27SDimitry Andric   std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
179706c3fb27SDimitry Andric   if (!DreString)
179806c3fb27SDimitry Andric     return std::nullopt;
179906c3fb27SDimitry Andric 
180006c3fb27SDimitry Andric   if (IdxIsLitZero) {
180106c3fb27SDimitry Andric     // If the index is literal zero, we produce the most concise fix-it:
180206c3fb27SDimitry Andric     SS << (*DreString).str() << ".data()";
180306c3fb27SDimitry Andric   } else {
180406c3fb27SDimitry Andric     std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
180506c3fb27SDimitry Andric     if (!IndexString)
180606c3fb27SDimitry Andric       return std::nullopt;
180706c3fb27SDimitry Andric 
180806c3fb27SDimitry Andric     SS << "&" << (*DreString).str() << ".data()"
180906c3fb27SDimitry Andric        << "[" << (*IndexString).str() << "]";
181006c3fb27SDimitry Andric   }
181106c3fb27SDimitry Andric   return FixItList{
181206c3fb27SDimitry Andric       FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
181306c3fb27SDimitry Andric }
181406c3fb27SDimitry Andric 
1815*5f757f3fSDimitry Andric std::optional<FixItList>
1816*5f757f3fSDimitry Andric UUCAddAssignGadget::getFixits(const Strategy &S) const {
1817*5f757f3fSDimitry Andric   DeclUseList DREs = getClaimedVarUseSites();
1818*5f757f3fSDimitry Andric 
1819*5f757f3fSDimitry Andric   if (DREs.size() != 1)
1820*5f757f3fSDimitry Andric     return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
1821*5f757f3fSDimitry Andric                          // give up
1822*5f757f3fSDimitry Andric   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
1823*5f757f3fSDimitry Andric     if (S.lookup(VD) == Strategy::Kind::Span) {
1824*5f757f3fSDimitry Andric       FixItList Fixes;
1825*5f757f3fSDimitry Andric 
1826*5f757f3fSDimitry Andric       const Stmt *AddAssignNode = getBaseStmt();
1827*5f757f3fSDimitry Andric       StringRef varName = VD->getName();
1828*5f757f3fSDimitry Andric       const ASTContext &Ctx = VD->getASTContext();
1829*5f757f3fSDimitry Andric 
1830*5f757f3fSDimitry Andric       if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
1831*5f757f3fSDimitry Andric         return std::nullopt;
1832*5f757f3fSDimitry Andric 
1833*5f757f3fSDimitry Andric       // To transform UUC(p += n) to UUC(p = p.subspan(..)):
1834*5f757f3fSDimitry Andric       bool NotParenExpr =
1835*5f757f3fSDimitry Andric           (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
1836*5f757f3fSDimitry Andric       std::string SS = varName.str() + " = " + varName.str() + ".subspan";
1837*5f757f3fSDimitry Andric       if (NotParenExpr)
1838*5f757f3fSDimitry Andric         SS += "(";
1839*5f757f3fSDimitry Andric 
1840*5f757f3fSDimitry Andric       std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
1841*5f757f3fSDimitry Andric           AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
1842*5f757f3fSDimitry Andric       if (!AddAssignLocation)
1843*5f757f3fSDimitry Andric         return std::nullopt;
1844*5f757f3fSDimitry Andric 
1845*5f757f3fSDimitry Andric       Fixes.push_back(FixItHint::CreateReplacement(
1846*5f757f3fSDimitry Andric           SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
1847*5f757f3fSDimitry Andric           SS));
1848*5f757f3fSDimitry Andric       if (NotParenExpr)
1849*5f757f3fSDimitry Andric         Fixes.push_back(FixItHint::CreateInsertion(
1850*5f757f3fSDimitry Andric             Offset->getEndLoc().getLocWithOffset(1), ")"));
1851*5f757f3fSDimitry Andric       return Fixes;
1852*5f757f3fSDimitry Andric     }
1853*5f757f3fSDimitry Andric   }
1854*5f757f3fSDimitry Andric   return std::nullopt; // Not in the cases that we can handle for now, give up.
1855*5f757f3fSDimitry Andric }
185606c3fb27SDimitry Andric 
185706c3fb27SDimitry Andric std::optional<FixItList> UPCPreIncrementGadget::getFixits(const Strategy &S) const {
185806c3fb27SDimitry Andric   DeclUseList DREs = getClaimedVarUseSites();
185906c3fb27SDimitry Andric 
186006c3fb27SDimitry Andric   if (DREs.size() != 1)
186106c3fb27SDimitry Andric     return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
186206c3fb27SDimitry Andric                          // give up
186306c3fb27SDimitry Andric   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
186406c3fb27SDimitry Andric     if (S.lookup(VD) == Strategy::Kind::Span) {
186506c3fb27SDimitry Andric       FixItList Fixes;
186606c3fb27SDimitry Andric       std::stringstream SS;
186706c3fb27SDimitry Andric       const Stmt *PreIncNode = getBaseStmt();
186806c3fb27SDimitry Andric       StringRef varName = VD->getName();
186906c3fb27SDimitry Andric       const ASTContext &Ctx = VD->getASTContext();
187006c3fb27SDimitry Andric 
187106c3fb27SDimitry Andric       // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
187206c3fb27SDimitry Andric       SS << "(" << varName.data() << " = " << varName.data()
187306c3fb27SDimitry Andric          << ".subspan(1)).data()";
187406c3fb27SDimitry Andric       std::optional<SourceLocation> PreIncLocation =
187506c3fb27SDimitry Andric           getEndCharLoc(PreIncNode, Ctx.getSourceManager(), Ctx.getLangOpts());
187606c3fb27SDimitry Andric       if (!PreIncLocation)
187706c3fb27SDimitry Andric         return std::nullopt;
187806c3fb27SDimitry Andric 
187906c3fb27SDimitry Andric       Fixes.push_back(FixItHint::CreateReplacement(
188006c3fb27SDimitry Andric           SourceRange(PreIncNode->getBeginLoc(), *PreIncLocation), SS.str()));
188106c3fb27SDimitry Andric       return Fixes;
188206c3fb27SDimitry Andric     }
188306c3fb27SDimitry Andric   }
188406c3fb27SDimitry Andric   return std::nullopt; // Not in the cases that we can handle for now, give up.
188506c3fb27SDimitry Andric }
188606c3fb27SDimitry Andric 
188706c3fb27SDimitry Andric 
188806c3fb27SDimitry Andric // For a non-null initializer `Init` of `T *` type, this function returns
188906c3fb27SDimitry Andric // `FixItHint`s producing a list initializer `{Init,  S}` as a part of a fix-it
189006c3fb27SDimitry Andric // to output stream.
189106c3fb27SDimitry Andric // In many cases, this function cannot figure out the actual extent `S`.  It
189206c3fb27SDimitry Andric // then will use a place holder to replace `S` to ask users to fill `S` in.  The
189306c3fb27SDimitry Andric // initializer shall be used to initialize a variable of type `std::span<T>`.
189406c3fb27SDimitry Andric //
189506c3fb27SDimitry Andric // FIXME: Support multi-level pointers
189606c3fb27SDimitry Andric //
189706c3fb27SDimitry Andric // Parameters:
189806c3fb27SDimitry Andric //   `Init` a pointer to the initializer expression
189906c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
190006c3fb27SDimitry Andric static FixItList
1901*5f757f3fSDimitry Andric FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
190206c3fb27SDimitry Andric                                  const StringRef UserFillPlaceHolder) {
190306c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
190406c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
190506c3fb27SDimitry Andric 
190606c3fb27SDimitry Andric   // If `Init` has a constant value that is (or equivalent to) a
190706c3fb27SDimitry Andric   // NULL pointer, we use the default constructor to initialize the span
190806c3fb27SDimitry Andric   // object, i.e., a `std:span` variable declaration with no initializer.
190906c3fb27SDimitry Andric   // So the fix-it is just to remove the initializer.
191006c3fb27SDimitry Andric   if (Init->isNullPointerConstant(Ctx,
191106c3fb27SDimitry Andric           // FIXME: Why does this function not ask for `const ASTContext
191206c3fb27SDimitry Andric           // &`? It should. Maybe worth an NFC patch later.
191306c3fb27SDimitry Andric           Expr::NullPointerConstantValueDependence::
191406c3fb27SDimitry Andric               NPC_ValueDependentIsNotNull)) {
191506c3fb27SDimitry Andric     std::optional<SourceLocation> InitLocation =
191606c3fb27SDimitry Andric         getEndCharLoc(Init, SM, LangOpts);
191706c3fb27SDimitry Andric     if (!InitLocation)
191806c3fb27SDimitry Andric       return {};
191906c3fb27SDimitry Andric 
192006c3fb27SDimitry Andric     SourceRange SR(Init->getBeginLoc(), *InitLocation);
192106c3fb27SDimitry Andric 
192206c3fb27SDimitry Andric     return {FixItHint::CreateRemoval(SR)};
192306c3fb27SDimitry Andric   }
192406c3fb27SDimitry Andric 
192506c3fb27SDimitry Andric   FixItList FixIts{};
192606c3fb27SDimitry Andric   std::string ExtentText = UserFillPlaceHolder.data();
192706c3fb27SDimitry Andric   StringRef One = "1";
192806c3fb27SDimitry Andric 
192906c3fb27SDimitry Andric   // Insert `{` before `Init`:
193006c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
193106c3fb27SDimitry Andric   // Try to get the data extent. Break into different cases:
193206c3fb27SDimitry Andric   if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
193306c3fb27SDimitry Andric     // In cases `Init` is `new T[n]` and there is no explicit cast over
193406c3fb27SDimitry Andric     // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
193506c3fb27SDimitry Andric     // of `T`. So the extent is `n` unless `n` has side effects.  Similar but
193606c3fb27SDimitry Andric     // simpler for the case where `Init` is `new T`.
193706c3fb27SDimitry Andric     if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
193806c3fb27SDimitry Andric       if (!Ext->HasSideEffects(Ctx)) {
193906c3fb27SDimitry Andric         std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
194006c3fb27SDimitry Andric         if (!ExtentString)
194106c3fb27SDimitry Andric           return {};
194206c3fb27SDimitry Andric         ExtentText = *ExtentString;
194306c3fb27SDimitry Andric       }
194406c3fb27SDimitry Andric     } else if (!CxxNew->isArray())
194506c3fb27SDimitry Andric       // Although the initializer is not allocating a buffer, the pointer
194606c3fb27SDimitry Andric       // variable could still be used in buffer access operations.
194706c3fb27SDimitry Andric       ExtentText = One;
194806c3fb27SDimitry Andric   } else if (const auto *CArrTy = Ctx.getAsConstantArrayType(
194906c3fb27SDimitry Andric                  Init->IgnoreImpCasts()->getType())) {
195006c3fb27SDimitry Andric     // In cases `Init` is of an array type after stripping off implicit casts,
195106c3fb27SDimitry Andric     // the extent is the array size.  Note that if the array size is not a
195206c3fb27SDimitry Andric     // constant, we cannot use it as the extent.
195306c3fb27SDimitry Andric     ExtentText = getAPIntText(CArrTy->getSize());
195406c3fb27SDimitry Andric   } else {
195506c3fb27SDimitry Andric     // In cases `Init` is of the form `&Var` after stripping of implicit
195606c3fb27SDimitry Andric     // casts, where `&` is the built-in operator, the extent is 1.
195706c3fb27SDimitry Andric     if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
195806c3fb27SDimitry Andric       if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
195906c3fb27SDimitry Andric           isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
196006c3fb27SDimitry Andric         ExtentText = One;
196106c3fb27SDimitry Andric     // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
196206c3fb27SDimitry Andric     // and explicit casting, etc. etc.
196306c3fb27SDimitry Andric   }
196406c3fb27SDimitry Andric 
196506c3fb27SDimitry Andric   SmallString<32> StrBuffer{};
196606c3fb27SDimitry Andric   std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
196706c3fb27SDimitry Andric 
196806c3fb27SDimitry Andric   if (!LocPassInit)
196906c3fb27SDimitry Andric     return {};
197006c3fb27SDimitry Andric 
197106c3fb27SDimitry Andric   StrBuffer.append(", ");
197206c3fb27SDimitry Andric   StrBuffer.append(ExtentText);
197306c3fb27SDimitry Andric   StrBuffer.append("}");
197406c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
197506c3fb27SDimitry Andric   return FixIts;
197606c3fb27SDimitry Andric }
197706c3fb27SDimitry Andric 
1978*5f757f3fSDimitry Andric #ifndef NDEBUG
1979*5f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg)  \
1980*5f757f3fSDimitry Andric Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), "failed to produce fixit for declaration '" + (D)->getNameAsString() + "'" + (Msg))
1981*5f757f3fSDimitry Andric #else
1982*5f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg)
1983*5f757f3fSDimitry Andric #endif
1984*5f757f3fSDimitry Andric 
1985*5f757f3fSDimitry Andric // For the given variable declaration with a pointer-to-T type, returns the text
1986*5f757f3fSDimitry Andric // `std::span<T>`.  If it is unable to generate the text, returns
1987*5f757f3fSDimitry Andric // `std::nullopt`.
1988*5f757f3fSDimitry Andric static std::optional<std::string> createSpanTypeForVarDecl(const VarDecl *VD,
1989*5f757f3fSDimitry Andric                                                            const ASTContext &Ctx) {
1990*5f757f3fSDimitry Andric   assert(VD->getType()->isPointerType());
1991*5f757f3fSDimitry Andric 
1992*5f757f3fSDimitry Andric   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
1993*5f757f3fSDimitry Andric   std::optional<std::string> PteTyText = getPointeeTypeText(
1994*5f757f3fSDimitry Andric       VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
1995*5f757f3fSDimitry Andric 
1996*5f757f3fSDimitry Andric   if (!PteTyText)
1997*5f757f3fSDimitry Andric     return std::nullopt;
1998*5f757f3fSDimitry Andric 
1999*5f757f3fSDimitry Andric   std::string SpanTyText = "std::span<";
2000*5f757f3fSDimitry Andric 
2001*5f757f3fSDimitry Andric   SpanTyText.append(*PteTyText);
2002*5f757f3fSDimitry Andric   // Append qualifiers to span element type if any:
2003*5f757f3fSDimitry Andric   if (PteTyQualifiers) {
2004*5f757f3fSDimitry Andric     SpanTyText.append(" ");
2005*5f757f3fSDimitry Andric     SpanTyText.append(PteTyQualifiers->getAsString());
2006*5f757f3fSDimitry Andric   }
2007*5f757f3fSDimitry Andric   SpanTyText.append(">");
2008*5f757f3fSDimitry Andric   return SpanTyText;
2009*5f757f3fSDimitry Andric }
2010*5f757f3fSDimitry Andric 
201106c3fb27SDimitry Andric // For a `VarDecl` of the form `T  * var (= Init)?`, this
2012*5f757f3fSDimitry Andric // function generates fix-its that
2013*5f757f3fSDimitry Andric //  1) replace `T * var` with `std::span<T> var`; and
2014*5f757f3fSDimitry Andric //  2) change `Init` accordingly to a span constructor, if it exists.
201506c3fb27SDimitry Andric //
201606c3fb27SDimitry Andric // FIXME: support Multi-level pointers
201706c3fb27SDimitry Andric //
201806c3fb27SDimitry Andric // Parameters:
201906c3fb27SDimitry Andric //   `D` a pointer the variable declaration node
202006c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
2021*5f757f3fSDimitry Andric //   `UserFillPlaceHolder` the user-input placeholder text
202206c3fb27SDimitry Andric // Returns:
2023*5f757f3fSDimitry Andric //    the non-empty fix-it list, if fix-its are successfuly generated; empty
2024*5f757f3fSDimitry Andric //    list otherwise.
2025*5f757f3fSDimitry Andric static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
2026*5f757f3fSDimitry Andric 					 const StringRef UserFillPlaceHolder,
2027*5f757f3fSDimitry Andric 					 UnsafeBufferUsageHandler &Handler) {
2028*5f757f3fSDimitry Andric   if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager()))
2029*5f757f3fSDimitry Andric     return {};
203006c3fb27SDimitry Andric 
203106c3fb27SDimitry Andric   FixItList FixIts{};
2032*5f757f3fSDimitry Andric   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
203306c3fb27SDimitry Andric 
2034*5f757f3fSDimitry Andric   if (!SpanTyText) {
2035*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
2036*5f757f3fSDimitry Andric     return {};
2037*5f757f3fSDimitry Andric   }
2038*5f757f3fSDimitry Andric 
2039*5f757f3fSDimitry Andric   // Will hold the text for `std::span<T> Ident`:
2040*5f757f3fSDimitry Andric   std::stringstream SS;
2041*5f757f3fSDimitry Andric 
2042*5f757f3fSDimitry Andric   SS << *SpanTyText;
2043*5f757f3fSDimitry Andric   // Append qualifiers to the type of `D`, if any:
2044*5f757f3fSDimitry Andric   if (D->getType().hasQualifiers())
2045*5f757f3fSDimitry Andric     SS << " " << D->getType().getQualifiers().getAsString();
2046*5f757f3fSDimitry Andric 
2047*5f757f3fSDimitry Andric   // The end of the range of the original source that will be replaced
2048*5f757f3fSDimitry Andric   // by `std::span<T> ident`:
2049*5f757f3fSDimitry Andric   SourceLocation EndLocForReplacement = D->getEndLoc();
2050*5f757f3fSDimitry Andric   std::optional<StringRef> IdentText =
2051*5f757f3fSDimitry Andric       getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
2052*5f757f3fSDimitry Andric 
2053*5f757f3fSDimitry Andric   if (!IdentText) {
2054*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
2055*5f757f3fSDimitry Andric     return {};
2056*5f757f3fSDimitry Andric   }
2057*5f757f3fSDimitry Andric   // Fix the initializer if it exists:
205806c3fb27SDimitry Andric   if (const Expr *Init = D->getInit()) {
205906c3fb27SDimitry Andric     FixItList InitFixIts =
2060*5f757f3fSDimitry Andric         FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
206106c3fb27SDimitry Andric     if (InitFixIts.empty())
206206c3fb27SDimitry Andric       return {};
206306c3fb27SDimitry Andric     FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts.begin()),
206406c3fb27SDimitry Andric                   std::make_move_iterator(InitFixIts.end()));
2065*5f757f3fSDimitry Andric     // If the declaration has the form `T *ident = init`, we want to replace
2066*5f757f3fSDimitry Andric     // `T *ident = ` with `std::span<T> ident`:
2067*5f757f3fSDimitry Andric     EndLocForReplacement = Init->getBeginLoc().getLocWithOffset(-1);
2068*5f757f3fSDimitry Andric   }
2069*5f757f3fSDimitry Andric   SS << " " << IdentText->str();
2070*5f757f3fSDimitry Andric   if (!EndLocForReplacement.isValid()) {
2071*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
207206c3fb27SDimitry Andric     return {};
2073*5f757f3fSDimitry Andric   }
207406c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateReplacement(
2075*5f757f3fSDimitry Andric       SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
207606c3fb27SDimitry Andric   return FixIts;
207706c3fb27SDimitry Andric }
207806c3fb27SDimitry Andric 
207906c3fb27SDimitry Andric static bool hasConflictingOverload(const FunctionDecl *FD) {
208006c3fb27SDimitry Andric   return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
208106c3fb27SDimitry Andric }
208206c3fb27SDimitry Andric 
2083*5f757f3fSDimitry Andric // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
2084*5f757f3fSDimitry Andric // types, this function produces fix-its to make the change self-contained.  Let
2085*5f757f3fSDimitry Andric // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
2086*5f757f3fSDimitry Andric // entity defined by the `FunctionDecl` after the change to the parameters.
2087*5f757f3fSDimitry Andric // Fix-its produced by this function are
208806c3fb27SDimitry Andric //   1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
208906c3fb27SDimitry Andric //   of 'F';
209006c3fb27SDimitry Andric //   2. Create a declaration of "NewF" next to each declaration of `F`;
209106c3fb27SDimitry Andric //   3. Create a definition of "F" (as its' original definition is now belongs
209206c3fb27SDimitry Andric //      to "NewF") next to its original definition.  The body of the creating
209306c3fb27SDimitry Andric //      definition calls to "NewF".
209406c3fb27SDimitry Andric //
209506c3fb27SDimitry Andric // Example:
209606c3fb27SDimitry Andric //
209706c3fb27SDimitry Andric // void f(int *p);  // original declaration
209806c3fb27SDimitry Andric // void f(int *p) { // original definition
209906c3fb27SDimitry Andric //    p[5];
210006c3fb27SDimitry Andric // }
210106c3fb27SDimitry Andric //
210206c3fb27SDimitry Andric // To change the parameter `p` to be of `std::span<int>` type, we
210306c3fb27SDimitry Andric // also add overloads:
210406c3fb27SDimitry Andric //
210506c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
210606c3fb27SDimitry Andric // void f(std::span<int> p);                      // added overload decl
210706c3fb27SDimitry Andric // void f(std::span<int> p) {     // original def where param is changed
210806c3fb27SDimitry Andric //    p[5];
210906c3fb27SDimitry Andric // }
211006c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p) {  // added def
211106c3fb27SDimitry Andric //   return f(std::span(p, <# size #>));
211206c3fb27SDimitry Andric // }
211306c3fb27SDimitry Andric //
211406c3fb27SDimitry Andric static std::optional<FixItList>
2115*5f757f3fSDimitry Andric createOverloadsForFixedParams(const Strategy &S, const FunctionDecl *FD,
2116*5f757f3fSDimitry Andric                               const ASTContext &Ctx,
211706c3fb27SDimitry Andric                               UnsafeBufferUsageHandler &Handler) {
211806c3fb27SDimitry Andric   // FIXME: need to make this conflict checking better:
211906c3fb27SDimitry Andric   if (hasConflictingOverload(FD))
212006c3fb27SDimitry Andric     return std::nullopt;
212106c3fb27SDimitry Andric 
212206c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
212306c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
2124*5f757f3fSDimitry Andric   const unsigned NumParms = FD->getNumParams();
2125*5f757f3fSDimitry Andric   std::vector<std::string> NewTysTexts(NumParms);
2126*5f757f3fSDimitry Andric   std::vector<bool> ParmsMask(NumParms, false);
2127*5f757f3fSDimitry Andric   bool AtLeastOneParmToFix = false;
2128*5f757f3fSDimitry Andric 
2129*5f757f3fSDimitry Andric   for (unsigned i = 0; i < NumParms; i++) {
2130*5f757f3fSDimitry Andric     const ParmVarDecl *PVD = FD->getParamDecl(i);
2131*5f757f3fSDimitry Andric 
2132*5f757f3fSDimitry Andric     if (S.lookup(PVD) == Strategy::Kind::Wontfix)
2133*5f757f3fSDimitry Andric       continue;
2134*5f757f3fSDimitry Andric     if (S.lookup(PVD) != Strategy::Kind::Span)
2135*5f757f3fSDimitry Andric       // Not supported, not suppose to happen:
2136*5f757f3fSDimitry Andric       return std::nullopt;
2137*5f757f3fSDimitry Andric 
2138*5f757f3fSDimitry Andric     std::optional<Qualifiers> PteTyQuals = std::nullopt;
2139*5f757f3fSDimitry Andric     std::optional<std::string> PteTyText =
2140*5f757f3fSDimitry Andric         getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
2141*5f757f3fSDimitry Andric 
2142*5f757f3fSDimitry Andric     if (!PteTyText)
2143*5f757f3fSDimitry Andric       // something wrong in obtaining the text of the pointee type, give up
2144*5f757f3fSDimitry Andric       return std::nullopt;
2145*5f757f3fSDimitry Andric     // FIXME: whether we should create std::span type depends on the Strategy.
2146*5f757f3fSDimitry Andric     NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
2147*5f757f3fSDimitry Andric     ParmsMask[i] = true;
2148*5f757f3fSDimitry Andric     AtLeastOneParmToFix = true;
2149*5f757f3fSDimitry Andric   }
2150*5f757f3fSDimitry Andric   if (!AtLeastOneParmToFix)
2151*5f757f3fSDimitry Andric     // No need to create function overloads:
2152*5f757f3fSDimitry Andric     return {};
215306c3fb27SDimitry Andric   // FIXME Respect indentation of the original code.
215406c3fb27SDimitry Andric 
215506c3fb27SDimitry Andric   // A lambda that creates the text representation of a function declaration
2156*5f757f3fSDimitry Andric   // with the new type signatures:
215706c3fb27SDimitry Andric   const auto NewOverloadSignatureCreator =
2158*5f757f3fSDimitry Andric       [&SM, &LangOpts, &NewTysTexts,
2159*5f757f3fSDimitry Andric        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
216006c3fb27SDimitry Andric     std::stringstream SS;
216106c3fb27SDimitry Andric 
216206c3fb27SDimitry Andric     SS << ";";
216306c3fb27SDimitry Andric     SS << getEndOfLine().str();
216406c3fb27SDimitry Andric     // Append: ret-type func-name "("
216506c3fb27SDimitry Andric     if (auto Prefix = getRangeText(
216606c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
216706c3fb27SDimitry Andric             SM, LangOpts))
216806c3fb27SDimitry Andric       SS << Prefix->str();
216906c3fb27SDimitry Andric     else
217006c3fb27SDimitry Andric       return std::nullopt; // give up
217106c3fb27SDimitry Andric     // Append: parameter-type-list
217206c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
217306c3fb27SDimitry Andric 
217406c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
217506c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
217606c3fb27SDimitry Andric 
217706c3fb27SDimitry Andric       if (Parm->isImplicit())
217806c3fb27SDimitry Andric         continue;
2179*5f757f3fSDimitry Andric       if (ParmsMask[i]) {
2180*5f757f3fSDimitry Andric         // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
2181*5f757f3fSDimitry Andric         // new type:
2182*5f757f3fSDimitry Andric         SS << NewTysTexts[i];
218306c3fb27SDimitry Andric         // print parameter name if provided:
218406c3fb27SDimitry Andric         if (IdentifierInfo *II = Parm->getIdentifier())
2185*5f757f3fSDimitry Andric           SS << ' ' << II->getName().str();
2186*5f757f3fSDimitry Andric       } else if (auto ParmTypeText = getRangeText(
2187*5f757f3fSDimitry Andric                      getSourceRangeToTokenEnd(Parm, SM, LangOpts),
2188*5f757f3fSDimitry Andric                      SM, LangOpts)) {
218906c3fb27SDimitry Andric         // print the whole `Parm` without modification:
219006c3fb27SDimitry Andric         SS << ParmTypeText->str();
219106c3fb27SDimitry Andric       } else
219206c3fb27SDimitry Andric         return std::nullopt; // something wrong, give up
219306c3fb27SDimitry Andric       if (i != NumParms - 1)
219406c3fb27SDimitry Andric         SS << ", ";
219506c3fb27SDimitry Andric     }
219606c3fb27SDimitry Andric     SS << ")";
219706c3fb27SDimitry Andric     return SS.str();
219806c3fb27SDimitry Andric   };
219906c3fb27SDimitry Andric 
220006c3fb27SDimitry Andric   // A lambda that creates the text representation of a function definition with
220106c3fb27SDimitry Andric   // the original signature:
220206c3fb27SDimitry Andric   const auto OldOverloadDefCreator =
2203*5f757f3fSDimitry Andric       [&Handler, &SM, &LangOpts, &NewTysTexts,
2204*5f757f3fSDimitry Andric        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
220506c3fb27SDimitry Andric     std::stringstream SS;
220606c3fb27SDimitry Andric 
220706c3fb27SDimitry Andric     SS << getEndOfLine().str();
220806c3fb27SDimitry Andric     // Append: attr-name ret-type func-name "(" param-list ")" "{"
220906c3fb27SDimitry Andric     if (auto FDPrefix = getRangeText(
221006c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
221106c3fb27SDimitry Andric             LangOpts))
221206c3fb27SDimitry Andric       SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
221306c3fb27SDimitry Andric          << FDPrefix->str() << "{";
221406c3fb27SDimitry Andric     else
221506c3fb27SDimitry Andric       return std::nullopt;
221606c3fb27SDimitry Andric     // Append: "return" func-name "("
221706c3fb27SDimitry Andric     if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
221806c3fb27SDimitry Andric       SS << "return " << FunQualName->str() << "(";
221906c3fb27SDimitry Andric     else
222006c3fb27SDimitry Andric       return std::nullopt;
222106c3fb27SDimitry Andric 
222206c3fb27SDimitry Andric     // Append: arg-list
222306c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
222406c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
222506c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
222606c3fb27SDimitry Andric 
222706c3fb27SDimitry Andric       if (Parm->isImplicit())
222806c3fb27SDimitry Andric         continue;
222906c3fb27SDimitry Andric       // FIXME: If a parameter has no name, it is unused in the
223006c3fb27SDimitry Andric       // definition. So we could just leave it as it is.
223106c3fb27SDimitry Andric       if (!Parm->getIdentifier())
223206c3fb27SDimitry Andric         // If a parameter of a function definition has no name:
223306c3fb27SDimitry Andric         return std::nullopt;
2234*5f757f3fSDimitry Andric       if (ParmsMask[i])
223506c3fb27SDimitry Andric         // This is our spanified paramter!
2236*5f757f3fSDimitry Andric         SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
2237*5f757f3fSDimitry Andric            << ", " << getUserFillPlaceHolder("size") << ")";
223806c3fb27SDimitry Andric       else
223906c3fb27SDimitry Andric         SS << Parm->getIdentifier()->getName().str();
224006c3fb27SDimitry Andric       if (i != NumParms - 1)
224106c3fb27SDimitry Andric         SS << ", ";
224206c3fb27SDimitry Andric     }
224306c3fb27SDimitry Andric     // finish call and the body
224406c3fb27SDimitry Andric     SS << ");}" << getEndOfLine().str();
224506c3fb27SDimitry Andric     // FIXME: 80-char line formatting?
224606c3fb27SDimitry Andric     return SS.str();
224706c3fb27SDimitry Andric   };
224806c3fb27SDimitry Andric 
224906c3fb27SDimitry Andric   FixItList FixIts{};
225006c3fb27SDimitry Andric   for (FunctionDecl *FReDecl : FD->redecls()) {
225106c3fb27SDimitry Andric     std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
225206c3fb27SDimitry Andric 
225306c3fb27SDimitry Andric     if (!Loc)
225406c3fb27SDimitry Andric       return {};
225506c3fb27SDimitry Andric     if (FReDecl->isThisDeclarationADefinition()) {
225606c3fb27SDimitry Andric       assert(FReDecl == FD && "inconsistent function definition");
225706c3fb27SDimitry Andric       // Inserts a definition with the old signature to the end of
225806c3fb27SDimitry Andric       // `FReDecl`:
2259*5f757f3fSDimitry Andric       if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
226006c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
226106c3fb27SDimitry Andric       else
226206c3fb27SDimitry Andric         return {}; // give up
226306c3fb27SDimitry Andric     } else {
226406c3fb27SDimitry Andric       // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
226506c3fb27SDimitry Andric       if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
226606c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(
226706c3fb27SDimitry Andric             FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
226806c3fb27SDimitry Andric                                         FReDecl->getBeginLoc(), " ")));
226906c3fb27SDimitry Andric       }
227006c3fb27SDimitry Andric       // Inserts a declaration with the new signature to the end of `FReDecl`:
2271*5f757f3fSDimitry Andric       if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
227206c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
227306c3fb27SDimitry Andric       else
227406c3fb27SDimitry Andric         return {};
227506c3fb27SDimitry Andric     }
227606c3fb27SDimitry Andric   }
227706c3fb27SDimitry Andric   return FixIts;
227806c3fb27SDimitry Andric }
227906c3fb27SDimitry Andric 
2280*5f757f3fSDimitry Andric // To fix a `ParmVarDecl` to be of `std::span` type.
228106c3fb27SDimitry Andric static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
228206c3fb27SDimitry Andric                                   UnsafeBufferUsageHandler &Handler) {
2283*5f757f3fSDimitry Andric   if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) {
2284*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
2285*5f757f3fSDimitry Andric     return {};
2286*5f757f3fSDimitry Andric   }
2287*5f757f3fSDimitry Andric   if (PVD->hasDefaultArg()) {
228806c3fb27SDimitry Andric     // FIXME: generate fix-its for default values:
2289*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
229006c3fb27SDimitry Andric     return {};
2291*5f757f3fSDimitry Andric   }
229206c3fb27SDimitry Andric 
229306c3fb27SDimitry Andric   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
229406c3fb27SDimitry Andric   std::optional<std::string> PteTyText = getPointeeTypeText(
229506c3fb27SDimitry Andric       PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
229606c3fb27SDimitry Andric 
2297*5f757f3fSDimitry Andric   if (!PteTyText) {
2298*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
229906c3fb27SDimitry Andric     return {};
2300*5f757f3fSDimitry Andric   }
230106c3fb27SDimitry Andric 
230206c3fb27SDimitry Andric   std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
230306c3fb27SDimitry Andric 
2304*5f757f3fSDimitry Andric   if (!PVDNameText) {
2305*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
230606c3fb27SDimitry Andric     return {};
2307*5f757f3fSDimitry Andric   }
230806c3fb27SDimitry Andric 
230906c3fb27SDimitry Andric   std::stringstream SS;
2310*5f757f3fSDimitry Andric   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
231106c3fb27SDimitry Andric 
231206c3fb27SDimitry Andric   if (PteTyQualifiers)
2313*5f757f3fSDimitry Andric     // Append qualifiers if they exist:
2314*5f757f3fSDimitry Andric     SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
2315*5f757f3fSDimitry Andric   else
2316*5f757f3fSDimitry Andric     SS << getSpanTypeText(*PteTyText);
231706c3fb27SDimitry Andric   // Append qualifiers to the type of the parameter:
231806c3fb27SDimitry Andric   if (PVD->getType().hasQualifiers())
2319*5f757f3fSDimitry Andric     SS << ' ' << PVD->getType().getQualifiers().getAsString();
232006c3fb27SDimitry Andric   // Append parameter's name:
2321*5f757f3fSDimitry Andric   SS << ' ' << PVDNameText->str();
2322*5f757f3fSDimitry Andric   // Add replacement fix-it:
2323*5f757f3fSDimitry Andric   return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
232406c3fb27SDimitry Andric }
232506c3fb27SDimitry Andric 
232606c3fb27SDimitry Andric static FixItList fixVariableWithSpan(const VarDecl *VD,
232706c3fb27SDimitry Andric                                      const DeclUseTracker &Tracker,
232806c3fb27SDimitry Andric                                      ASTContext &Ctx,
232906c3fb27SDimitry Andric                                      UnsafeBufferUsageHandler &Handler) {
233006c3fb27SDimitry Andric   const DeclStmt *DS = Tracker.lookupDecl(VD);
2331*5f757f3fSDimitry Andric   if (!DS) {
2332*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : variables declared this way not implemented yet");
2333*5f757f3fSDimitry Andric     return {};
2334*5f757f3fSDimitry Andric   }
233506c3fb27SDimitry Andric   if (!DS->isSingleDecl()) {
233606c3fb27SDimitry Andric     // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2337*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
233806c3fb27SDimitry Andric     return {};
233906c3fb27SDimitry Andric   }
234006c3fb27SDimitry Andric   // Currently DS is an unused variable but we'll need it when
234106c3fb27SDimitry Andric   // non-single decls are implemented, where the pointee type name
234206c3fb27SDimitry Andric   // and the '*' are spread around the place.
234306c3fb27SDimitry Andric   (void)DS;
234406c3fb27SDimitry Andric 
234506c3fb27SDimitry Andric   // FIXME: handle cases where DS has multiple declarations
2346*5f757f3fSDimitry Andric   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
234706c3fb27SDimitry Andric }
234806c3fb27SDimitry Andric 
234906c3fb27SDimitry Andric // TODO: we should be consistent to use `std::nullopt` to represent no-fix due
235006c3fb27SDimitry Andric // to any unexpected problem.
235106c3fb27SDimitry Andric static FixItList
235206c3fb27SDimitry Andric fixVariable(const VarDecl *VD, Strategy::Kind K,
235306c3fb27SDimitry Andric             /* The function decl under analysis */ const Decl *D,
235406c3fb27SDimitry Andric             const DeclUseTracker &Tracker, ASTContext &Ctx,
235506c3fb27SDimitry Andric             UnsafeBufferUsageHandler &Handler) {
235606c3fb27SDimitry Andric   if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
235706c3fb27SDimitry Andric     auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
2358*5f757f3fSDimitry Andric     if (!FD || FD != D) {
235906c3fb27SDimitry Andric       // `FD != D` means that `PVD` belongs to a function that is not being
236006c3fb27SDimitry Andric       // analyzed currently.  Thus `FD` may not be complete.
2361*5f757f3fSDimitry Andric       DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
236206c3fb27SDimitry Andric       return {};
2363*5f757f3fSDimitry Andric     }
236406c3fb27SDimitry Andric 
236506c3fb27SDimitry Andric     // TODO If function has a try block we can't change params unless we check
236606c3fb27SDimitry Andric     // also its catch block for their use.
236706c3fb27SDimitry Andric     // FIXME We might support static class methods, some select methods,
236806c3fb27SDimitry Andric     // operators and possibly lamdas.
236906c3fb27SDimitry Andric     if (FD->isMain() || FD->isConstexpr() ||
237006c3fb27SDimitry Andric         FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
237106c3fb27SDimitry Andric         FD->isVariadic() ||
237206c3fb27SDimitry Andric         // also covers call-operator of lamdas
237306c3fb27SDimitry Andric         isa<CXXMethodDecl>(FD) ||
237406c3fb27SDimitry Andric         // skip when the function body is a try-block
237506c3fb27SDimitry Andric         (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
2376*5f757f3fSDimitry Andric         FD->isOverloadedOperator()) {
2377*5f757f3fSDimitry Andric       DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
237806c3fb27SDimitry Andric       return {}; // TODO test all these cases
237906c3fb27SDimitry Andric     }
2380*5f757f3fSDimitry Andric   }
238106c3fb27SDimitry Andric 
238206c3fb27SDimitry Andric   switch (K) {
238306c3fb27SDimitry Andric   case Strategy::Kind::Span: {
238406c3fb27SDimitry Andric     if (VD->getType()->isPointerType()) {
238506c3fb27SDimitry Andric       if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
238606c3fb27SDimitry Andric         return fixParamWithSpan(PVD, Ctx, Handler);
238706c3fb27SDimitry Andric 
238806c3fb27SDimitry Andric       if (VD->isLocalVarDecl())
238906c3fb27SDimitry Andric         return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
239006c3fb27SDimitry Andric     }
2391*5f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
239206c3fb27SDimitry Andric     return {};
239306c3fb27SDimitry Andric   }
239406c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
239506c3fb27SDimitry Andric   case Strategy::Kind::Array:
239606c3fb27SDimitry Andric   case Strategy::Kind::Vector:
239706c3fb27SDimitry Andric     llvm_unreachable("Strategy not implemented yet!");
239806c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
239906c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
240006c3fb27SDimitry Andric   }
240106c3fb27SDimitry Andric   llvm_unreachable("Unknown strategy!");
240206c3fb27SDimitry Andric }
240306c3fb27SDimitry Andric 
240406c3fb27SDimitry Andric // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
240506c3fb27SDimitry Andric // `RemoveRange` of 'h' overlaps with a macro use.
240606c3fb27SDimitry Andric static bool overlapWithMacro(const FixItList &FixIts) {
240706c3fb27SDimitry Andric   // FIXME: For now we only check if the range (or the first token) is (part of)
240806c3fb27SDimitry Andric   // a macro expansion.  Ideally, we want to check for all tokens in the range.
240906c3fb27SDimitry Andric   return llvm::any_of(FixIts, [](const FixItHint &Hint) {
241006c3fb27SDimitry Andric     auto Range = Hint.RemoveRange;
241106c3fb27SDimitry Andric     if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
241206c3fb27SDimitry Andric       // If the range (or the first token) is (part of) a macro expansion:
241306c3fb27SDimitry Andric       return true;
241406c3fb27SDimitry Andric     return false;
241506c3fb27SDimitry Andric   });
241606c3fb27SDimitry Andric }
241706c3fb27SDimitry Andric 
2418*5f757f3fSDimitry Andric // Returns true iff `VD` is a parameter of the declaration `D`:
2419*5f757f3fSDimitry Andric static bool isParameterOf(const VarDecl *VD, const Decl *D) {
2420*5f757f3fSDimitry Andric   return isa<ParmVarDecl>(VD) &&
2421*5f757f3fSDimitry Andric          VD->getDeclContext() == dyn_cast<DeclContext>(D);
242206c3fb27SDimitry Andric }
242306c3fb27SDimitry Andric 
2424*5f757f3fSDimitry Andric // Erases variables in `FixItsForVariable`, if such a variable has an unfixable
2425*5f757f3fSDimitry Andric // group mate.  A variable `v` is unfixable iff `FixItsForVariable` does not
2426*5f757f3fSDimitry Andric // contain `v`.
2427*5f757f3fSDimitry Andric static void eraseVarsForUnfixableGroupMates(
2428*5f757f3fSDimitry Andric     std::map<const VarDecl *, FixItList> &FixItsForVariable,
2429*5f757f3fSDimitry Andric     const VariableGroupsManager &VarGrpMgr) {
2430*5f757f3fSDimitry Andric   // Variables will be removed from `FixItsForVariable`:
2431*5f757f3fSDimitry Andric   SmallVector<const VarDecl *, 8> ToErase;
2432*5f757f3fSDimitry Andric 
2433*5f757f3fSDimitry Andric   for (const auto &[VD, Ignore] : FixItsForVariable) {
2434*5f757f3fSDimitry Andric     VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
2435*5f757f3fSDimitry Andric     if (llvm::any_of(Grp,
2436*5f757f3fSDimitry Andric                      [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
2437*5f757f3fSDimitry Andric                        return !FixItsForVariable.count(GrpMember);
2438*5f757f3fSDimitry Andric                      })) {
2439*5f757f3fSDimitry Andric       // At least one group member cannot be fixed, so we have to erase the
2440*5f757f3fSDimitry Andric       // whole group:
2441*5f757f3fSDimitry Andric       for (const VarDecl *Member : Grp)
2442*5f757f3fSDimitry Andric         ToErase.push_back(Member);
2443*5f757f3fSDimitry Andric     }
2444*5f757f3fSDimitry Andric   }
2445*5f757f3fSDimitry Andric   for (auto *VarToErase : ToErase)
2446*5f757f3fSDimitry Andric     FixItsForVariable.erase(VarToErase);
2447*5f757f3fSDimitry Andric }
2448*5f757f3fSDimitry Andric 
2449*5f757f3fSDimitry Andric // Returns the fix-its that create bounds-safe function overloads for the
2450*5f757f3fSDimitry Andric // function `D`, if `D`'s parameters will be changed to safe-types through
2451*5f757f3fSDimitry Andric // fix-its in `FixItsForVariable`.
2452*5f757f3fSDimitry Andric //
2453*5f757f3fSDimitry Andric // NOTE: In case `D`'s parameters will be changed but bounds-safe function
2454*5f757f3fSDimitry Andric // overloads cannot created, the whole group that contains the parameters will
2455*5f757f3fSDimitry Andric // be erased from `FixItsForVariable`.
2456*5f757f3fSDimitry Andric static FixItList createFunctionOverloadsForParms(
2457*5f757f3fSDimitry Andric     std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
2458*5f757f3fSDimitry Andric     const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
2459*5f757f3fSDimitry Andric     const Strategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler) {
2460*5f757f3fSDimitry Andric   FixItList FixItsSharedByParms{};
2461*5f757f3fSDimitry Andric 
2462*5f757f3fSDimitry Andric   std::optional<FixItList> OverloadFixes =
2463*5f757f3fSDimitry Andric       createOverloadsForFixedParams(S, FD, Ctx, Handler);
2464*5f757f3fSDimitry Andric 
2465*5f757f3fSDimitry Andric   if (OverloadFixes) {
2466*5f757f3fSDimitry Andric     FixItsSharedByParms.append(*OverloadFixes);
2467*5f757f3fSDimitry Andric   } else {
2468*5f757f3fSDimitry Andric     // Something wrong in generating `OverloadFixes`, need to remove the
2469*5f757f3fSDimitry Andric     // whole group, where parameters are in, from `FixItsForVariable` (Note
2470*5f757f3fSDimitry Andric     // that all parameters should be in the same group):
2471*5f757f3fSDimitry Andric     for (auto *Member : VarGrpMgr.getGroupOfParms())
2472*5f757f3fSDimitry Andric       FixItsForVariable.erase(Member);
2473*5f757f3fSDimitry Andric   }
2474*5f757f3fSDimitry Andric   return FixItsSharedByParms;
2475*5f757f3fSDimitry Andric }
2476*5f757f3fSDimitry Andric 
2477*5f757f3fSDimitry Andric // Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
2478bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList>
247906c3fb27SDimitry Andric getFixIts(FixableGadgetSets &FixablesForAllVars, const Strategy &S,
248006c3fb27SDimitry Andric 	  ASTContext &Ctx,
248106c3fb27SDimitry Andric           /* The function decl under analysis */ const Decl *D,
248206c3fb27SDimitry Andric           const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
2483*5f757f3fSDimitry Andric           const VariableGroupsManager &VarGrpMgr) {
2484*5f757f3fSDimitry Andric   // `FixItsForVariable` will map each variable to a set of fix-its directly
2485*5f757f3fSDimitry Andric   // associated to the variable itself.  Fix-its of distinct variables in
2486*5f757f3fSDimitry Andric   // `FixItsForVariable` are disjoint.
2487bdd1243dSDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariable;
2488*5f757f3fSDimitry Andric 
2489*5f757f3fSDimitry Andric   // Populate `FixItsForVariable` with fix-its directly associated with each
2490*5f757f3fSDimitry Andric   // variable.  Fix-its directly associated to a variable 'v' are the ones
2491*5f757f3fSDimitry Andric   // produced by the `FixableGadget`s whose claimed variable is 'v'.
249206c3fb27SDimitry Andric   for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
249306c3fb27SDimitry Andric     FixItsForVariable[VD] =
249406c3fb27SDimitry Andric         fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
249506c3fb27SDimitry Andric     // If we fail to produce Fix-It for the declaration we have to skip the
249606c3fb27SDimitry Andric     // variable entirely.
249706c3fb27SDimitry Andric     if (FixItsForVariable[VD].empty()) {
249806c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
249906c3fb27SDimitry Andric       continue;
250006c3fb27SDimitry Andric     }
2501bdd1243dSDimitry Andric     for (const auto &F : Fixables) {
250206c3fb27SDimitry Andric       std::optional<FixItList> Fixits = F->getFixits(S);
250306c3fb27SDimitry Andric 
2504*5f757f3fSDimitry Andric       if (Fixits) {
2505bdd1243dSDimitry Andric         FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
2506*5f757f3fSDimitry Andric                                      Fixits->begin(), Fixits->end());
2507*5f757f3fSDimitry Andric         continue;
2508*5f757f3fSDimitry Andric       }
2509*5f757f3fSDimitry Andric #ifndef NDEBUG
2510*5f757f3fSDimitry Andric       Handler.addDebugNoteForVar(
2511*5f757f3fSDimitry Andric           VD, F->getBaseStmt()->getBeginLoc(),
2512*5f757f3fSDimitry Andric           ("gadget '" + F->getDebugName() + "' refused to produce a fix")
2513*5f757f3fSDimitry Andric               .str());
2514*5f757f3fSDimitry Andric #endif
251506c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
2516*5f757f3fSDimitry Andric       break;
2517*5f757f3fSDimitry Andric     }
2518*5f757f3fSDimitry Andric   }
2519*5f757f3fSDimitry Andric 
2520*5f757f3fSDimitry Andric   // `FixItsForVariable` now contains only variables that can be
2521*5f757f3fSDimitry Andric   // fixed. A variable can be fixed if its' declaration and all Fixables
2522*5f757f3fSDimitry Andric   // associated to it can all be fixed.
2523*5f757f3fSDimitry Andric 
2524*5f757f3fSDimitry Andric   // To further remove from `FixItsForVariable` variables whose group mates
2525*5f757f3fSDimitry Andric   // cannot be fixed...
2526*5f757f3fSDimitry Andric   eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
2527*5f757f3fSDimitry Andric   // Now `FixItsForVariable` gets further reduced: a variable is in
2528*5f757f3fSDimitry Andric   // `FixItsForVariable` iff it can be fixed and all its group mates can be
2529*5f757f3fSDimitry Andric   // fixed.
2530*5f757f3fSDimitry Andric 
2531*5f757f3fSDimitry Andric   // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
2532*5f757f3fSDimitry Andric   // That is,  when fixing multiple parameters in one step,  these fix-its will
2533*5f757f3fSDimitry Andric   // be applied only once (instead of being applied per parameter).
2534*5f757f3fSDimitry Andric   FixItList FixItsSharedByParms{};
2535*5f757f3fSDimitry Andric 
2536*5f757f3fSDimitry Andric   if (auto *FD = dyn_cast<FunctionDecl>(D))
2537*5f757f3fSDimitry Andric     FixItsSharedByParms = createFunctionOverloadsForParms(
2538*5f757f3fSDimitry Andric         FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
2539*5f757f3fSDimitry Andric 
2540*5f757f3fSDimitry Andric   // The map that maps each variable `v` to fix-its for the whole group where
2541*5f757f3fSDimitry Andric   // `v` is in:
2542*5f757f3fSDimitry Andric   std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
2543*5f757f3fSDimitry Andric       FixItsForVariable};
2544*5f757f3fSDimitry Andric 
2545*5f757f3fSDimitry Andric   for (auto &[Var, Ignore] : FixItsForVariable) {
2546*5f757f3fSDimitry Andric     bool AnyParm = false;
2547*5f757f3fSDimitry Andric     const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
2548*5f757f3fSDimitry Andric 
2549*5f757f3fSDimitry Andric     for (const VarDecl *GrpMate : VarGroupForVD) {
2550*5f757f3fSDimitry Andric       if (Var == GrpMate)
255106c3fb27SDimitry Andric         continue;
2552*5f757f3fSDimitry Andric       if (FixItsForVariable.count(GrpMate))
2553*5f757f3fSDimitry Andric         FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
255406c3fb27SDimitry Andric     }
2555*5f757f3fSDimitry Andric     if (AnyParm) {
2556*5f757f3fSDimitry Andric       // This assertion should never fail.  Otherwise we have a bug.
2557*5f757f3fSDimitry Andric       assert(!FixItsSharedByParms.empty() &&
2558*5f757f3fSDimitry Andric              "Should not try to fix a parameter that does not belong to a "
2559*5f757f3fSDimitry Andric              "FunctionDecl");
2560*5f757f3fSDimitry Andric       FinalFixItsForVariable[Var].append(FixItsSharedByParms);
2561*5f757f3fSDimitry Andric     }
2562*5f757f3fSDimitry Andric   }
2563*5f757f3fSDimitry Andric   // Fix-its that will be applied in one step shall NOT:
2564*5f757f3fSDimitry Andric   // 1. overlap with macros or/and templates; or
2565*5f757f3fSDimitry Andric   // 2. conflict with each other.
2566*5f757f3fSDimitry Andric   // Otherwise, the fix-its will be dropped.
2567*5f757f3fSDimitry Andric   for (auto Iter = FinalFixItsForVariable.begin();
2568*5f757f3fSDimitry Andric        Iter != FinalFixItsForVariable.end();)
2569*5f757f3fSDimitry Andric     if (overlapWithMacro(Iter->second) ||
2570*5f757f3fSDimitry Andric         clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
2571*5f757f3fSDimitry Andric       Iter = FinalFixItsForVariable.erase(Iter);
2572*5f757f3fSDimitry Andric     } else
2573*5f757f3fSDimitry Andric       Iter++;
2574*5f757f3fSDimitry Andric   return FinalFixItsForVariable;
257506c3fb27SDimitry Andric }
257606c3fb27SDimitry Andric 
2577*5f757f3fSDimitry Andric template <typename VarDeclIterTy>
2578bdd1243dSDimitry Andric static Strategy
2579*5f757f3fSDimitry Andric getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
2580bdd1243dSDimitry Andric   Strategy S;
2581bdd1243dSDimitry Andric   for (const VarDecl *VD : UnsafeVars) {
2582bdd1243dSDimitry Andric     S.set(VD, Strategy::Kind::Span);
2583bdd1243dSDimitry Andric   }
2584bdd1243dSDimitry Andric   return S;
2585bdd1243dSDimitry Andric }
2586bdd1243dSDimitry Andric 
2587*5f757f3fSDimitry Andric //  Manages variable groups:
2588*5f757f3fSDimitry Andric class VariableGroupsManagerImpl : public VariableGroupsManager {
2589*5f757f3fSDimitry Andric   const std::vector<VarGrpTy> Groups;
2590*5f757f3fSDimitry Andric   const std::map<const VarDecl *, unsigned> &VarGrpMap;
2591*5f757f3fSDimitry Andric   const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
2592*5f757f3fSDimitry Andric 
2593*5f757f3fSDimitry Andric public:
2594*5f757f3fSDimitry Andric   VariableGroupsManagerImpl(
2595*5f757f3fSDimitry Andric       const std::vector<VarGrpTy> &Groups,
2596*5f757f3fSDimitry Andric       const std::map<const VarDecl *, unsigned> &VarGrpMap,
2597*5f757f3fSDimitry Andric       const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
2598*5f757f3fSDimitry Andric       : Groups(Groups), VarGrpMap(VarGrpMap),
2599*5f757f3fSDimitry Andric         GrpsUnionForParms(GrpsUnionForParms) {}
2600*5f757f3fSDimitry Andric 
2601*5f757f3fSDimitry Andric   VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
2602*5f757f3fSDimitry Andric     if (GrpsUnionForParms.contains(Var)) {
2603*5f757f3fSDimitry Andric       if (HasParm)
2604*5f757f3fSDimitry Andric         *HasParm = true;
2605*5f757f3fSDimitry Andric       return GrpsUnionForParms.getArrayRef();
2606*5f757f3fSDimitry Andric     }
2607*5f757f3fSDimitry Andric     if (HasParm)
2608*5f757f3fSDimitry Andric       *HasParm = false;
2609*5f757f3fSDimitry Andric 
2610*5f757f3fSDimitry Andric     auto It = VarGrpMap.find(Var);
2611*5f757f3fSDimitry Andric 
2612*5f757f3fSDimitry Andric     if (It == VarGrpMap.end())
2613*5f757f3fSDimitry Andric       return std::nullopt;
2614*5f757f3fSDimitry Andric     return Groups[It->second];
2615*5f757f3fSDimitry Andric   }
2616*5f757f3fSDimitry Andric 
2617*5f757f3fSDimitry Andric   VarGrpRef getGroupOfParms() const override {
2618*5f757f3fSDimitry Andric     return GrpsUnionForParms.getArrayRef();
2619*5f757f3fSDimitry Andric   }
2620*5f757f3fSDimitry Andric };
2621*5f757f3fSDimitry Andric 
2622bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D,
262306c3fb27SDimitry Andric                                    UnsafeBufferUsageHandler &Handler,
262406c3fb27SDimitry Andric                                    bool EmitSuggestions) {
2625*5f757f3fSDimitry Andric #ifndef NDEBUG
2626*5f757f3fSDimitry Andric   Handler.clearDebugNotes();
2627*5f757f3fSDimitry Andric #endif
2628bdd1243dSDimitry Andric 
2629*5f757f3fSDimitry Andric   assert(D && D->getBody());
263006c3fb27SDimitry Andric   // We do not want to visit a Lambda expression defined inside a method independently.
263106c3fb27SDimitry Andric   // Instead, it should be visited along with the outer method.
2632*5f757f3fSDimitry Andric   // FIXME: do we want to do the same thing for `BlockDecl`s?
263306c3fb27SDimitry Andric   if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
263406c3fb27SDimitry Andric     if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
263506c3fb27SDimitry Andric       return;
2636bdd1243dSDimitry Andric   }
2637bdd1243dSDimitry Andric 
263806c3fb27SDimitry Andric   // Do not emit fixit suggestions for functions declared in an
263906c3fb27SDimitry Andric   // extern "C" block.
264006c3fb27SDimitry Andric   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
264106c3fb27SDimitry Andric       for (FunctionDecl *FReDecl : FD->redecls()) {
264206c3fb27SDimitry Andric       if (FReDecl->isExternC()) {
264306c3fb27SDimitry Andric         EmitSuggestions = false;
264406c3fb27SDimitry Andric         break;
264506c3fb27SDimitry Andric       }
264606c3fb27SDimitry Andric     }
264706c3fb27SDimitry Andric   }
264806c3fb27SDimitry Andric 
264906c3fb27SDimitry Andric   WarningGadgetSets UnsafeOps;
265006c3fb27SDimitry Andric   FixableGadgetSets FixablesForAllVars;
265106c3fb27SDimitry Andric 
265206c3fb27SDimitry Andric   auto [FixableGadgets, WarningGadgets, Tracker] =
265306c3fb27SDimitry Andric     findGadgets(D, Handler, EmitSuggestions);
265406c3fb27SDimitry Andric 
265506c3fb27SDimitry Andric   if (!EmitSuggestions) {
265606c3fb27SDimitry Andric     // Our job is very easy without suggestions. Just warn about
265706c3fb27SDimitry Andric     // every problematic operation and consider it done. No need to deal
265806c3fb27SDimitry Andric     // with fixable gadgets, no need to group operations by variable.
265906c3fb27SDimitry Andric     for (const auto &G : WarningGadgets) {
266006c3fb27SDimitry Andric       Handler.handleUnsafeOperation(G->getBaseStmt(),
266106c3fb27SDimitry Andric                                     /*IsRelatedToDecl=*/false);
266206c3fb27SDimitry Andric     }
266306c3fb27SDimitry Andric 
266406c3fb27SDimitry Andric     // This return guarantees that most of the machine doesn't run when
266506c3fb27SDimitry Andric     // suggestions aren't requested.
266606c3fb27SDimitry Andric     assert(FixableGadgets.size() == 0 &&
266706c3fb27SDimitry Andric            "Fixable gadgets found but suggestions not requested!");
266806c3fb27SDimitry Andric     return;
266906c3fb27SDimitry Andric   }
267006c3fb27SDimitry Andric 
2671*5f757f3fSDimitry Andric   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
2672*5f757f3fSDimitry Andric   //  function under the analysis. No need to fix any Fixables.
2673*5f757f3fSDimitry Andric   if (!WarningGadgets.empty()) {
2674*5f757f3fSDimitry Andric     // Gadgets "claim" variables they're responsible for. Once this loop
2675*5f757f3fSDimitry Andric     // finishes, the tracker will only track DREs that weren't claimed by any
2676*5f757f3fSDimitry Andric     // gadgets, i.e. not understood by the analysis.
2677*5f757f3fSDimitry Andric     for (const auto &G : FixableGadgets) {
2678*5f757f3fSDimitry Andric       for (const auto *DRE : G->getClaimedVarUseSites()) {
2679*5f757f3fSDimitry Andric         Tracker.claimUse(DRE);
2680*5f757f3fSDimitry Andric       }
2681*5f757f3fSDimitry Andric     }
2682*5f757f3fSDimitry Andric   }
2683*5f757f3fSDimitry Andric 
2684*5f757f3fSDimitry Andric   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
2685*5f757f3fSDimitry Andric   // function under the analysis.  Thus, it early returns here as there is
2686*5f757f3fSDimitry Andric   // nothing needs to be fixed.
2687*5f757f3fSDimitry Andric   //
2688*5f757f3fSDimitry Andric   // Note this claim is based on the assumption that there is no unsafe
2689*5f757f3fSDimitry Andric   // variable whose declaration is invisible from the analyzing function.
2690*5f757f3fSDimitry Andric   // Otherwise, we need to consider if the uses of those unsafe varuables needs
2691*5f757f3fSDimitry Andric   // fix.
2692*5f757f3fSDimitry Andric   // So far, we are not fixing any global variables or class members. And,
2693*5f757f3fSDimitry Andric   // lambdas will be analyzed along with the enclosing function. So this early
2694*5f757f3fSDimitry Andric   // return is correct for now.
2695*5f757f3fSDimitry Andric   if (WarningGadgets.empty())
2696*5f757f3fSDimitry Andric     return;
2697*5f757f3fSDimitry Andric 
269806c3fb27SDimitry Andric   UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
269906c3fb27SDimitry Andric   FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
270006c3fb27SDimitry Andric 
270106c3fb27SDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
270206c3fb27SDimitry Andric 
2703bdd1243dSDimitry Andric   // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
270406c3fb27SDimitry Andric   for (auto it = FixablesForAllVars.byVar.cbegin();
270506c3fb27SDimitry Andric        it != FixablesForAllVars.byVar.cend();) {
270606c3fb27SDimitry Andric       // FIXME: need to deal with global variables later
2707*5f757f3fSDimitry Andric       if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
2708*5f757f3fSDimitry Andric #ifndef NDEBUG
2709*5f757f3fSDimitry Andric           Handler.addDebugNoteForVar(
2710*5f757f3fSDimitry Andric               it->first, it->first->getBeginLoc(),
2711*5f757f3fSDimitry Andric               ("failed to produce fixit for '" + it->first->getNameAsString() +
2712*5f757f3fSDimitry Andric                "' : neither local nor a parameter"));
2713*5f757f3fSDimitry Andric #endif
2714*5f757f3fSDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
2715*5f757f3fSDimitry Andric       } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
2716*5f757f3fSDimitry Andric #ifndef NDEBUG
2717*5f757f3fSDimitry Andric         Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
2718*5f757f3fSDimitry Andric                                    ("failed to produce fixit for '" +
2719*5f757f3fSDimitry Andric                                     it->first->getNameAsString() +
2720*5f757f3fSDimitry Andric                                     "' : has a reference type"));
2721*5f757f3fSDimitry Andric #endif
2722*5f757f3fSDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
2723*5f757f3fSDimitry Andric       } else if (Tracker.hasUnclaimedUses(it->first)) {
2724*5f757f3fSDimitry Andric #ifndef NDEBUG
2725*5f757f3fSDimitry Andric         auto AllUnclaimed = Tracker.getUnclaimedUses(it->first);
2726*5f757f3fSDimitry Andric         for (auto UnclaimedDRE : AllUnclaimed) {
2727*5f757f3fSDimitry Andric         std::string UnclaimedUseTrace =
2728*5f757f3fSDimitry Andric             getDREAncestorString(UnclaimedDRE, D->getASTContext());
2729*5f757f3fSDimitry Andric 
2730*5f757f3fSDimitry Andric         Handler.addDebugNoteForVar(
2731*5f757f3fSDimitry Andric             it->first, UnclaimedDRE->getBeginLoc(),
2732*5f757f3fSDimitry Andric             ("failed to produce fixit for '" + it->first->getNameAsString() +
2733*5f757f3fSDimitry Andric              "' : has an unclaimed use\nThe unclaimed DRE trace: " +
2734*5f757f3fSDimitry Andric              UnclaimedUseTrace));
2735*5f757f3fSDimitry Andric         }
2736*5f757f3fSDimitry Andric #endif
2737*5f757f3fSDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
2738*5f757f3fSDimitry Andric       } else if (it->first->isInitCapture()) {
2739*5f757f3fSDimitry Andric #ifndef NDEBUG
2740*5f757f3fSDimitry Andric         Handler.addDebugNoteForVar(
2741*5f757f3fSDimitry Andric             it->first, it->first->getBeginLoc(),
2742*5f757f3fSDimitry Andric                                    ("failed to produce fixit for '" + it->first->getNameAsString() +
2743*5f757f3fSDimitry Andric                                     "' : init capture"));
2744*5f757f3fSDimitry Andric #endif
274506c3fb27SDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
2746bdd1243dSDimitry Andric       }else {
2747bdd1243dSDimitry Andric       ++it;
2748bdd1243dSDimitry Andric     }
2749bdd1243dSDimitry Andric   }
2750bdd1243dSDimitry Andric 
275106c3fb27SDimitry Andric   // Fixpoint iteration for pointer assignments
2752*5f757f3fSDimitry Andric   using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
275306c3fb27SDimitry Andric   DepMapTy DependenciesMap{};
275406c3fb27SDimitry Andric   DepMapTy PtrAssignmentGraph{};
275506c3fb27SDimitry Andric 
275606c3fb27SDimitry Andric   for (auto it : FixablesForAllVars.byVar) {
275706c3fb27SDimitry Andric     for (const FixableGadget *fixable : it.second) {
275806c3fb27SDimitry Andric       std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
275906c3fb27SDimitry Andric                                   fixable->getStrategyImplications();
276006c3fb27SDimitry Andric       if (ImplPair) {
2761*5f757f3fSDimitry Andric         std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
276206c3fb27SDimitry Andric         PtrAssignmentGraph[Impl.first].insert(Impl.second);
276306c3fb27SDimitry Andric       }
276406c3fb27SDimitry Andric     }
276506c3fb27SDimitry Andric   }
276606c3fb27SDimitry Andric 
276706c3fb27SDimitry Andric   /*
276806c3fb27SDimitry Andric    The following code does a BFS traversal of the `PtrAssignmentGraph`
276906c3fb27SDimitry Andric    considering all unsafe vars as starting nodes and constructs an undirected
277006c3fb27SDimitry Andric    graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
277106c3fb27SDimitry Andric    elimiates all variables that are unreachable from any unsafe var. In other
277206c3fb27SDimitry Andric    words, this removes all dependencies that don't include any unsafe variable
277306c3fb27SDimitry Andric    and consequently don't need any fixit generation.
277406c3fb27SDimitry Andric    Note: A careful reader would observe that the code traverses
277506c3fb27SDimitry Andric    `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
277606c3fb27SDimitry Andric    `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
277706c3fb27SDimitry Andric    achieve the same result but the one used here dramatically cuts the
277806c3fb27SDimitry Andric    amount of hoops the second part of the algorithm needs to jump, given that
277906c3fb27SDimitry Andric    a lot of these connections become "direct". The reader is advised not to
278006c3fb27SDimitry Andric    imagine how the graph is transformed because of using `Var` instead of
278106c3fb27SDimitry Andric    `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
278206c3fb27SDimitry Andric    and think about why it's equivalent later.
278306c3fb27SDimitry Andric    */
278406c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVarsDirected{};
278506c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
278606c3fb27SDimitry Andric     if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
278706c3fb27SDimitry Andric 
278806c3fb27SDimitry Andric       std::queue<const VarDecl*> QueueDirected{};
278906c3fb27SDimitry Andric       QueueDirected.push(Var);
279006c3fb27SDimitry Andric       while(!QueueDirected.empty()) {
279106c3fb27SDimitry Andric         const VarDecl* CurrentVar = QueueDirected.front();
279206c3fb27SDimitry Andric         QueueDirected.pop();
279306c3fb27SDimitry Andric         VisitedVarsDirected.insert(CurrentVar);
279406c3fb27SDimitry Andric         auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
279506c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
279606c3fb27SDimitry Andric           if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
279706c3fb27SDimitry Andric             QueueDirected.push(Adj);
279806c3fb27SDimitry Andric           }
279906c3fb27SDimitry Andric           DependenciesMap[Var].insert(Adj);
280006c3fb27SDimitry Andric           DependenciesMap[Adj].insert(Var);
280106c3fb27SDimitry Andric         }
280206c3fb27SDimitry Andric       }
280306c3fb27SDimitry Andric     }
280406c3fb27SDimitry Andric   }
280506c3fb27SDimitry Andric 
2806*5f757f3fSDimitry Andric   // `Groups` stores the set of Connected Components in the graph.
2807*5f757f3fSDimitry Andric   std::vector<VarGrpTy> Groups;
2808*5f757f3fSDimitry Andric   // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
2809*5f757f3fSDimitry Andric   // variables belong to.  Group indexes refer to the elements in `Groups`.
2810*5f757f3fSDimitry Andric   // `VarGrpMap` is complete in that every variable that needs fix is in it.
2811*5f757f3fSDimitry Andric   std::map<const VarDecl *, unsigned> VarGrpMap;
2812*5f757f3fSDimitry Andric   // The union group over the ones in "Groups" that contain parameters of `D`:
2813*5f757f3fSDimitry Andric   llvm::SetVector<const VarDecl *>
2814*5f757f3fSDimitry Andric       GrpsUnionForParms; // these variables need to be fixed in one step
2815*5f757f3fSDimitry Andric 
281606c3fb27SDimitry Andric   // Group Connected Components for Unsafe Vars
281706c3fb27SDimitry Andric   // (Dependencies based on pointer assignments)
281806c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVars{};
281906c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
282006c3fb27SDimitry Andric     if (VisitedVars.find(Var) == VisitedVars.end()) {
2821*5f757f3fSDimitry Andric       VarGrpTy &VarGroup = Groups.emplace_back();
282206c3fb27SDimitry Andric       std::queue<const VarDecl*> Queue{};
2823*5f757f3fSDimitry Andric 
282406c3fb27SDimitry Andric       Queue.push(Var);
282506c3fb27SDimitry Andric       while(!Queue.empty()) {
282606c3fb27SDimitry Andric         const VarDecl* CurrentVar = Queue.front();
282706c3fb27SDimitry Andric         Queue.pop();
282806c3fb27SDimitry Andric         VisitedVars.insert(CurrentVar);
282906c3fb27SDimitry Andric         VarGroup.push_back(CurrentVar);
283006c3fb27SDimitry Andric         auto AdjacentNodes = DependenciesMap[CurrentVar];
283106c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
283206c3fb27SDimitry Andric           if (VisitedVars.find(Adj) == VisitedVars.end()) {
283306c3fb27SDimitry Andric             Queue.push(Adj);
283406c3fb27SDimitry Andric           }
283506c3fb27SDimitry Andric         }
283606c3fb27SDimitry Andric       }
2837*5f757f3fSDimitry Andric 
2838*5f757f3fSDimitry Andric       bool HasParm = false;
2839*5f757f3fSDimitry Andric       unsigned GrpIdx = Groups.size() - 1;
2840*5f757f3fSDimitry Andric 
284106c3fb27SDimitry Andric       for (const VarDecl *V : VarGroup) {
2842*5f757f3fSDimitry Andric         VarGrpMap[V] = GrpIdx;
2843*5f757f3fSDimitry Andric         if (!HasParm && isParameterOf(V, D))
2844*5f757f3fSDimitry Andric           HasParm = true;
284506c3fb27SDimitry Andric       }
2846*5f757f3fSDimitry Andric       if (HasParm)
2847*5f757f3fSDimitry Andric         GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
284806c3fb27SDimitry Andric     }
284906c3fb27SDimitry Andric   }
285006c3fb27SDimitry Andric 
2851*5f757f3fSDimitry Andric   // Remove a `FixableGadget` if the associated variable is not in the graph
2852*5f757f3fSDimitry Andric   // computed above.  We do not want to generate fix-its for such variables,
2853*5f757f3fSDimitry Andric   // since they are neither warned nor reachable from a warned one.
2854*5f757f3fSDimitry Andric   //
2855*5f757f3fSDimitry Andric   // Note a variable is not warned if it is not directly used in any unsafe
2856*5f757f3fSDimitry Andric   // operation. A variable `v` is NOT reachable from an unsafe variable, if it
2857*5f757f3fSDimitry Andric   // does not exist another variable `u` such that `u` is warned and fixing `u`
2858*5f757f3fSDimitry Andric   // (transitively) implicates fixing `v`.
2859*5f757f3fSDimitry Andric   //
2860*5f757f3fSDimitry Andric   // For example,
2861*5f757f3fSDimitry Andric   // ```
2862*5f757f3fSDimitry Andric   // void f(int * p) {
2863*5f757f3fSDimitry Andric   //   int * a = p; *p = 0;
2864*5f757f3fSDimitry Andric   // }
2865*5f757f3fSDimitry Andric   // ```
2866*5f757f3fSDimitry Andric   // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
2867*5f757f3fSDimitry Andric   // warned nor reachable from a warned one.  If we add `a[5] = 0` to the end of
2868*5f757f3fSDimitry Andric   // the function above, `p` becomes reachable from a warned variable.
2869*5f757f3fSDimitry Andric   for (auto I = FixablesForAllVars.byVar.begin();
2870*5f757f3fSDimitry Andric        I != FixablesForAllVars.byVar.end();) {
2871*5f757f3fSDimitry Andric     // Note `VisitedVars` contain all the variables in the graph:
2872*5f757f3fSDimitry Andric     if (!VisitedVars.count((*I).first)) {
2873*5f757f3fSDimitry Andric       // no such var in graph:
2874*5f757f3fSDimitry Andric       I = FixablesForAllVars.byVar.erase(I);
2875*5f757f3fSDimitry Andric     } else
2876*5f757f3fSDimitry Andric       ++I;
2877*5f757f3fSDimitry Andric   }
287806c3fb27SDimitry Andric 
2879*5f757f3fSDimitry Andric   // We assign strategies to variables that are 1) in the graph and 2) can be
2880*5f757f3fSDimitry Andric   // fixed. Other variables have the default "Won't fix" strategy.
2881*5f757f3fSDimitry Andric   Strategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
2882*5f757f3fSDimitry Andric       VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
2883*5f757f3fSDimitry Andric         // If a warned variable has no "Fixable", it is considered unfixable:
2884*5f757f3fSDimitry Andric         return FixablesForAllVars.byVar.count(V);
2885*5f757f3fSDimitry Andric       }));
2886*5f757f3fSDimitry Andric   VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
2887*5f757f3fSDimitry Andric 
2888*5f757f3fSDimitry Andric   if (isa<NamedDecl>(D))
2889*5f757f3fSDimitry Andric     // The only case where `D` is not a `NamedDecl` is when `D` is a
2890*5f757f3fSDimitry Andric     // `BlockDecl`. Let's not fix variables in blocks for now
289106c3fb27SDimitry Andric     FixItsForVariableGroup =
289206c3fb27SDimitry Andric         getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
2893*5f757f3fSDimitry Andric                   Tracker, Handler, VarGrpMgr);
2894bdd1243dSDimitry Andric 
2895bdd1243dSDimitry Andric   for (const auto &G : UnsafeOps.noVar) {
2896bdd1243dSDimitry Andric     Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false);
2897bdd1243dSDimitry Andric   }
2898bdd1243dSDimitry Andric 
2899bdd1243dSDimitry Andric   for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
290006c3fb27SDimitry Andric     auto FixItsIt = FixItsForVariableGroup.find(VD);
2901*5f757f3fSDimitry Andric     Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
290206c3fb27SDimitry Andric                                       FixItsIt != FixItsForVariableGroup.end()
2903bdd1243dSDimitry Andric                                       ? std::move(FixItsIt->second)
2904*5f757f3fSDimitry Andric                                       : FixItList{},
2905*5f757f3fSDimitry Andric                                       D);
2906bdd1243dSDimitry Andric     for (const auto &G : WarningGadgets) {
2907bdd1243dSDimitry Andric       Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true);
2908bdd1243dSDimitry Andric     }
2909bdd1243dSDimitry Andric   }
2910bdd1243dSDimitry Andric }
2911