xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/UnsafeBufferUsage.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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"
10*06c3fb27SDimitry Andric #include "clang/AST/Decl.h"
11bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
12bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
13*06c3fb27SDimitry Andric #include "clang/Lex/Lexer.h"
14*06c3fb27SDimitry Andric #include "clang/Lex/Preprocessor.h"
15bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h"
16bdd1243dSDimitry Andric #include <memory>
17bdd1243dSDimitry Andric #include <optional>
18*06c3fb27SDimitry Andric #include <sstream>
19*06c3fb27SDimitry Andric #include <queue>
20bdd1243dSDimitry Andric 
21bdd1243dSDimitry Andric using namespace llvm;
22bdd1243dSDimitry Andric using namespace clang;
23bdd1243dSDimitry Andric using namespace ast_matchers;
24bdd1243dSDimitry Andric 
25bdd1243dSDimitry Andric namespace clang::ast_matchers {
26bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
27bdd1243dSDimitry Andric // except for those belonging to a different callable of "n".
28bdd1243dSDimitry Andric class MatchDescendantVisitor
29bdd1243dSDimitry Andric     : public RecursiveASTVisitor<MatchDescendantVisitor> {
30bdd1243dSDimitry Andric public:
31bdd1243dSDimitry Andric   typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase;
32bdd1243dSDimitry Andric 
33bdd1243dSDimitry Andric   // Creates an AST visitor that matches `Matcher` on all
34bdd1243dSDimitry Andric   // descendants of a given node "n" except for the ones
35bdd1243dSDimitry Andric   // belonging to a different callable of "n".
36bdd1243dSDimitry Andric   MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
37bdd1243dSDimitry Andric                          internal::ASTMatchFinder *Finder,
38bdd1243dSDimitry Andric                          internal::BoundNodesTreeBuilder *Builder,
39*06c3fb27SDimitry Andric                          internal::ASTMatchFinder::BindKind Bind,
40*06c3fb27SDimitry Andric                          const bool ignoreUnevaluatedContext)
41bdd1243dSDimitry Andric       : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
42*06c3fb27SDimitry Andric         Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {}
43bdd1243dSDimitry Andric 
44bdd1243dSDimitry Andric   // Returns true if a match is found in a subtree of `DynNode`, which belongs
45bdd1243dSDimitry Andric   // to the same callable of `DynNode`.
46bdd1243dSDimitry Andric   bool findMatch(const DynTypedNode &DynNode) {
47bdd1243dSDimitry Andric     Matches = false;
48bdd1243dSDimitry Andric     if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
49bdd1243dSDimitry Andric       TraverseStmt(const_cast<Stmt *>(StmtNode));
50bdd1243dSDimitry Andric       *Builder = ResultBindings;
51bdd1243dSDimitry Andric       return Matches;
52bdd1243dSDimitry Andric     }
53bdd1243dSDimitry Andric     return false;
54bdd1243dSDimitry Andric   }
55bdd1243dSDimitry Andric 
56bdd1243dSDimitry Andric   // The following are overriding methods from the base visitor class.
57bdd1243dSDimitry Andric   // They are public only to allow CRTP to work. They are *not *part
58bdd1243dSDimitry Andric   // of the public API of this class.
59bdd1243dSDimitry Andric 
60bdd1243dSDimitry Andric   // For the matchers so far used in safe buffers, we only need to match
61bdd1243dSDimitry Andric   // `Stmt`s.  To override more as needed.
62bdd1243dSDimitry Andric 
63bdd1243dSDimitry Andric   bool TraverseDecl(Decl *Node) {
64bdd1243dSDimitry Andric     if (!Node)
65bdd1243dSDimitry Andric       return true;
66bdd1243dSDimitry Andric     if (!match(*Node))
67bdd1243dSDimitry Andric       return false;
68bdd1243dSDimitry Andric     // To skip callables:
69bdd1243dSDimitry Andric     if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
70bdd1243dSDimitry Andric       return true;
71bdd1243dSDimitry Andric     // Traverse descendants
72bdd1243dSDimitry Andric     return VisitorBase::TraverseDecl(Node);
73bdd1243dSDimitry Andric   }
74bdd1243dSDimitry Andric 
75*06c3fb27SDimitry Andric   bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) {
76*06c3fb27SDimitry Andric     // These are unevaluated, except the result expression.
77*06c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
78*06c3fb27SDimitry Andric       return TraverseStmt(Node->getResultExpr());
79*06c3fb27SDimitry Andric     return VisitorBase::TraverseGenericSelectionExpr(Node);
80*06c3fb27SDimitry Andric   }
81*06c3fb27SDimitry Andric 
82*06c3fb27SDimitry Andric   bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) {
83*06c3fb27SDimitry Andric     // Unevaluated context.
84*06c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
85*06c3fb27SDimitry Andric       return true;
86*06c3fb27SDimitry Andric     return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node);
87*06c3fb27SDimitry Andric   }
88*06c3fb27SDimitry Andric 
89*06c3fb27SDimitry Andric   bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) {
90*06c3fb27SDimitry Andric     // Unevaluated context.
91*06c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
92*06c3fb27SDimitry Andric       return true;
93*06c3fb27SDimitry Andric     return VisitorBase::TraverseTypeOfExprTypeLoc(Node);
94*06c3fb27SDimitry Andric   }
95*06c3fb27SDimitry Andric 
96*06c3fb27SDimitry Andric   bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) {
97*06c3fb27SDimitry Andric     // Unevaluated context.
98*06c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
99*06c3fb27SDimitry Andric       return true;
100*06c3fb27SDimitry Andric     return VisitorBase::TraverseDecltypeTypeLoc(Node);
101*06c3fb27SDimitry Andric   }
102*06c3fb27SDimitry Andric 
103*06c3fb27SDimitry Andric   bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) {
104*06c3fb27SDimitry Andric     // Unevaluated context.
105*06c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
106*06c3fb27SDimitry Andric       return true;
107*06c3fb27SDimitry Andric     return VisitorBase::TraverseCXXNoexceptExpr(Node);
108*06c3fb27SDimitry Andric   }
109*06c3fb27SDimitry Andric 
110*06c3fb27SDimitry Andric   bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) {
111*06c3fb27SDimitry Andric     // Unevaluated context.
112*06c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
113*06c3fb27SDimitry Andric       return true;
114*06c3fb27SDimitry Andric     return VisitorBase::TraverseCXXTypeidExpr(Node);
115*06c3fb27SDimitry Andric   }
116*06c3fb27SDimitry Andric 
117bdd1243dSDimitry Andric   bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
118bdd1243dSDimitry Andric     if (!Node)
119bdd1243dSDimitry Andric       return true;
120bdd1243dSDimitry Andric     if (!match(*Node))
121bdd1243dSDimitry Andric       return false;
122bdd1243dSDimitry Andric     return VisitorBase::TraverseStmt(Node);
123bdd1243dSDimitry Andric   }
124bdd1243dSDimitry Andric 
125bdd1243dSDimitry Andric   bool shouldVisitTemplateInstantiations() const { return true; }
126bdd1243dSDimitry Andric   bool shouldVisitImplicitCode() const {
127bdd1243dSDimitry Andric     // TODO: let's ignore implicit code for now
128bdd1243dSDimitry Andric     return false;
129bdd1243dSDimitry Andric   }
130bdd1243dSDimitry Andric 
131bdd1243dSDimitry Andric private:
132bdd1243dSDimitry Andric   // Sets 'Matched' to true if 'Matcher' matches 'Node'
133bdd1243dSDimitry Andric   //
134bdd1243dSDimitry Andric   // Returns 'true' if traversal should continue after this function
135bdd1243dSDimitry Andric   // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
136bdd1243dSDimitry Andric   template <typename T> bool match(const T &Node) {
137bdd1243dSDimitry Andric     internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
138bdd1243dSDimitry Andric 
139bdd1243dSDimitry Andric     if (Matcher->matches(DynTypedNode::create(Node), Finder,
140bdd1243dSDimitry Andric                          &RecursiveBuilder)) {
141bdd1243dSDimitry Andric       ResultBindings.addMatch(RecursiveBuilder);
142bdd1243dSDimitry Andric       Matches = true;
143bdd1243dSDimitry Andric       if (Bind != internal::ASTMatchFinder::BK_All)
144bdd1243dSDimitry Andric         return false; // Abort as soon as a match is found.
145bdd1243dSDimitry Andric     }
146bdd1243dSDimitry Andric     return true;
147bdd1243dSDimitry Andric   }
148bdd1243dSDimitry Andric 
149bdd1243dSDimitry Andric   const internal::DynTypedMatcher *const Matcher;
150bdd1243dSDimitry Andric   internal::ASTMatchFinder *const Finder;
151bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder *const Builder;
152bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder ResultBindings;
153bdd1243dSDimitry Andric   const internal::ASTMatchFinder::BindKind Bind;
154bdd1243dSDimitry Andric   bool Matches;
155*06c3fb27SDimitry Andric   bool ignoreUnevaluatedContext;
156bdd1243dSDimitry Andric };
157bdd1243dSDimitry Andric 
158*06c3fb27SDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that.
159*06c3fb27SDimitry Andric static auto hasPointerType() {
160*06c3fb27SDimitry Andric     return hasType(hasCanonicalType(pointerType()));
161*06c3fb27SDimitry Andric }
162*06c3fb27SDimitry Andric 
163*06c3fb27SDimitry Andric static auto hasArrayType() {
164*06c3fb27SDimitry Andric     return hasType(hasCanonicalType(arrayType()));
165*06c3fb27SDimitry Andric }
166*06c3fb27SDimitry Andric 
167*06c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>, innerMatcher) {
168bdd1243dSDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
169bdd1243dSDimitry Andric 
170*06c3fb27SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, true);
171bdd1243dSDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
172bdd1243dSDimitry Andric }
173*06c3fb27SDimitry Andric 
174*06c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>, innerMatcher) {
175*06c3fb27SDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
176*06c3fb27SDimitry Andric 
177*06c3fb27SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, false);
178*06c3fb27SDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
179*06c3fb27SDimitry Andric }
180*06c3fb27SDimitry Andric 
181*06c3fb27SDimitry Andric // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
182*06c3fb27SDimitry Andric AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *,
183*06c3fb27SDimitry Andric               Handler) {
184*06c3fb27SDimitry Andric   return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
185*06c3fb27SDimitry Andric }
186*06c3fb27SDimitry Andric 
187*06c3fb27SDimitry Andric AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
188*06c3fb27SDimitry Andric   return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
189*06c3fb27SDimitry Andric }
190*06c3fb27SDimitry Andric 
191*06c3fb27SDimitry Andric // Matches a `UnaryOperator` whose operator is pre-increment:
192*06c3fb27SDimitry Andric AST_MATCHER(UnaryOperator, isPreInc) {
193*06c3fb27SDimitry Andric   return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
194*06c3fb27SDimitry Andric }
195*06c3fb27SDimitry Andric 
196*06c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
197*06c3fb27SDimitry Andric // matches 'e' and 'e' is in an Unspecified Lvalue Context.
198*06c3fb27SDimitry Andric static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) {
199*06c3fb27SDimitry Andric   // clang-format off
200*06c3fb27SDimitry Andric   return
201*06c3fb27SDimitry Andric     expr(anyOf(
202*06c3fb27SDimitry Andric       implicitCastExpr(
203*06c3fb27SDimitry Andric         hasCastKind(CastKind::CK_LValueToRValue),
204*06c3fb27SDimitry Andric         castSubExpr(innerMatcher)),
205*06c3fb27SDimitry Andric       binaryOperator(
206*06c3fb27SDimitry Andric         hasAnyOperatorName("="),
207*06c3fb27SDimitry Andric         hasLHS(innerMatcher)
208*06c3fb27SDimitry Andric       )
209*06c3fb27SDimitry Andric     ));
210*06c3fb27SDimitry Andric // clang-format on
211*06c3fb27SDimitry Andric }
212*06c3fb27SDimitry Andric 
213*06c3fb27SDimitry Andric 
214*06c3fb27SDimitry Andric // Returns a matcher that matches any expression `e` such that `InnerMatcher`
215*06c3fb27SDimitry Andric // matches `e` and `e` is in an Unspecified Pointer Context (UPC).
216*06c3fb27SDimitry Andric static internal::Matcher<Stmt>
217*06c3fb27SDimitry Andric isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
218*06c3fb27SDimitry Andric   // A UPC can be
219*06c3fb27SDimitry Andric   // 1. an argument of a function call (except the callee has [[unsafe_...]]
220*06c3fb27SDimitry Andric   //    attribute), or
221*06c3fb27SDimitry Andric   // 2. the operand of a pointer-to-(integer or bool) cast operation; or
222*06c3fb27SDimitry Andric   // 3. the operand of a comparator operation; or
223*06c3fb27SDimitry Andric   // 4. the operand of a pointer subtraction operation
224*06c3fb27SDimitry Andric   //    (i.e., computing the distance between two pointers); or ...
225*06c3fb27SDimitry Andric 
226*06c3fb27SDimitry Andric   auto CallArgMatcher =
227*06c3fb27SDimitry Andric       callExpr(forEachArgumentWithParam(InnerMatcher,
228*06c3fb27SDimitry Andric                   hasPointerType() /* array also decays to pointer type*/),
229*06c3fb27SDimitry Andric           unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
230*06c3fb27SDimitry Andric 
231*06c3fb27SDimitry Andric   auto CastOperandMatcher =
232*06c3fb27SDimitry Andric       castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
233*06c3fb27SDimitry Andric 		     hasCastKind(CastKind::CK_PointerToBoolean)),
234*06c3fb27SDimitry Andric 	       castSubExpr(allOf(hasPointerType(), InnerMatcher)));
235*06c3fb27SDimitry Andric 
236*06c3fb27SDimitry Andric   auto CompOperandMatcher =
237*06c3fb27SDimitry Andric       binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
238*06c3fb27SDimitry Andric                      eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
239*06c3fb27SDimitry Andric                             hasRHS(allOf(hasPointerType(), InnerMatcher))));
240*06c3fb27SDimitry Andric 
241*06c3fb27SDimitry Andric   // A matcher that matches pointer subtractions:
242*06c3fb27SDimitry Andric   auto PtrSubtractionMatcher =
243*06c3fb27SDimitry Andric       binaryOperator(hasOperatorName("-"),
244*06c3fb27SDimitry Andric 		     // Note that here we need both LHS and RHS to be
245*06c3fb27SDimitry Andric 		     // pointer. Then the inner matcher can match any of
246*06c3fb27SDimitry Andric 		     // them:
247*06c3fb27SDimitry Andric 		     allOf(hasLHS(hasPointerType()),
248*06c3fb27SDimitry Andric 			   hasRHS(hasPointerType())),
249*06c3fb27SDimitry Andric 		     eachOf(hasLHS(InnerMatcher),
250*06c3fb27SDimitry Andric 			    hasRHS(InnerMatcher)));
251*06c3fb27SDimitry Andric 
252*06c3fb27SDimitry Andric   return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
253*06c3fb27SDimitry Andric 		    PtrSubtractionMatcher));
254*06c3fb27SDimitry Andric   // FIXME: any more cases? (UPC excludes the RHS of an assignment.  For now we
255*06c3fb27SDimitry Andric   // don't have to check that.)
256*06c3fb27SDimitry Andric }
257*06c3fb27SDimitry Andric 
258*06c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
259*06c3fb27SDimitry Andric // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression
260*06c3fb27SDimitry Andric // 'e' isn't evaluated to an RValue). For example, consider the following code:
261*06c3fb27SDimitry Andric //    int *p = new int[4];
262*06c3fb27SDimitry Andric //    int *q = new int[4];
263*06c3fb27SDimitry Andric //    if ((p = q)) {}
264*06c3fb27SDimitry Andric //    p = q;
265*06c3fb27SDimitry Andric // The expression `p = q` in the conditional of the `if` statement
266*06c3fb27SDimitry Andric // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
267*06c3fb27SDimitry Andric // in the assignment statement is in an untyped context.
268*06c3fb27SDimitry Andric static internal::Matcher<Stmt>
269*06c3fb27SDimitry Andric isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
270*06c3fb27SDimitry Andric   // An unspecified context can be
271*06c3fb27SDimitry Andric   // 1. A compound statement,
272*06c3fb27SDimitry Andric   // 2. The body of an if statement
273*06c3fb27SDimitry Andric   // 3. Body of a loop
274*06c3fb27SDimitry Andric   auto CompStmt = compoundStmt(forEach(InnerMatcher));
275*06c3fb27SDimitry Andric   auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
276*06c3fb27SDimitry Andric   auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
277*06c3fb27SDimitry Andric   // FIXME: Handle loop bodies.
278*06c3fb27SDimitry Andric   return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
279*06c3fb27SDimitry Andric }
280bdd1243dSDimitry Andric } // namespace clang::ast_matchers
281bdd1243dSDimitry Andric 
282bdd1243dSDimitry Andric namespace {
283bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to
284bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs).
285bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
286bdd1243dSDimitry Andric 
287bdd1243dSDimitry Andric // Convenience typedef.
288bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>;
289bdd1243dSDimitry Andric 
290bdd1243dSDimitry Andric // Defined below.
291bdd1243dSDimitry Andric class Strategy;
292bdd1243dSDimitry Andric } // namespace
293bdd1243dSDimitry Andric 
294bdd1243dSDimitry Andric namespace {
295bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to
296bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific
297bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object.
298bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand
299bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it.
300bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not
301bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify
302bdd1243dSDimitry Andric /// it warrants a fixit).
303bdd1243dSDimitry Andric class Gadget {
304bdd1243dSDimitry Andric public:
305bdd1243dSDimitry Andric   enum class Kind {
306bdd1243dSDimitry Andric #define GADGET(x) x,
307bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
308bdd1243dSDimitry Andric   };
309bdd1243dSDimitry Andric 
310bdd1243dSDimitry Andric   /// Common type of ASTMatchers used for discovering gadgets.
311bdd1243dSDimitry Andric   /// Useful for implementing the static matcher() methods
312bdd1243dSDimitry Andric   /// that are expected from all non-abstract subclasses.
313bdd1243dSDimitry Andric   using Matcher = decltype(stmt());
314bdd1243dSDimitry Andric 
315bdd1243dSDimitry Andric   Gadget(Kind K) : K(K) {}
316bdd1243dSDimitry Andric 
317bdd1243dSDimitry Andric   Kind getKind() const { return K; }
318bdd1243dSDimitry Andric 
319bdd1243dSDimitry Andric   virtual bool isWarningGadget() const = 0;
320bdd1243dSDimitry Andric   virtual const Stmt *getBaseStmt() const = 0;
321bdd1243dSDimitry Andric 
322bdd1243dSDimitry Andric   /// Returns the list of pointer-type variables on which this gadget performs
323bdd1243dSDimitry Andric   /// its operation. Typically, there's only one variable. This isn't a list
324bdd1243dSDimitry Andric   /// of all DeclRefExprs in the gadget's AST!
325bdd1243dSDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const = 0;
326bdd1243dSDimitry Andric 
327bdd1243dSDimitry Andric   virtual ~Gadget() = default;
328bdd1243dSDimitry Andric 
329bdd1243dSDimitry Andric private:
330bdd1243dSDimitry Andric   Kind K;
331bdd1243dSDimitry Andric };
332bdd1243dSDimitry Andric 
333bdd1243dSDimitry Andric 
334bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants
335bdd1243dSDimitry Andric /// an immediate warning.
336bdd1243dSDimitry Andric class WarningGadget : public Gadget {
337bdd1243dSDimitry Andric public:
338bdd1243dSDimitry Andric   WarningGadget(Kind K) : Gadget(K) {}
339bdd1243dSDimitry Andric 
340bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return G->isWarningGadget(); }
341bdd1243dSDimitry Andric   bool isWarningGadget() const final { return true; }
342bdd1243dSDimitry Andric };
343bdd1243dSDimitry Andric 
344bdd1243dSDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but need to be
345bdd1243dSDimitry Andric /// properly recognized in order to emit fixes. For example, if a raw pointer-type
346bdd1243dSDimitry Andric /// variable is replaced by a safe C++ container, every use of such variable must be
347bdd1243dSDimitry Andric /// carefully considered and possibly updated.
348bdd1243dSDimitry Andric class FixableGadget : public Gadget {
349bdd1243dSDimitry Andric public:
350bdd1243dSDimitry Andric   FixableGadget(Kind K) : Gadget(K) {}
351bdd1243dSDimitry Andric 
352bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
353bdd1243dSDimitry Andric   bool isWarningGadget() const final { return false; }
354bdd1243dSDimitry Andric 
355bdd1243dSDimitry Andric   /// Returns a fixit that would fix the current gadget according to
356*06c3fb27SDimitry Andric   /// the current strategy. Returns std::nullopt if the fix cannot be produced;
357bdd1243dSDimitry Andric   /// returns an empty list if no fixes are necessary.
358bdd1243dSDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &) const {
359bdd1243dSDimitry Andric     return std::nullopt;
360bdd1243dSDimitry Andric   }
361*06c3fb27SDimitry Andric 
362*06c3fb27SDimitry Andric   /// Returns a list of two elements where the first element is the LHS of a pointer assignment
363*06c3fb27SDimitry Andric   /// statement and the second element is the RHS. This two-element list represents the fact that
364*06c3fb27SDimitry Andric   /// the LHS buffer gets its bounds information from the RHS buffer. This information will be used
365*06c3fb27SDimitry Andric   /// later to group all those variables whose types must be modified together to prevent type
366*06c3fb27SDimitry Andric   /// mismatches.
367*06c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
368*06c3fb27SDimitry Andric   getStrategyImplications() const {
369*06c3fb27SDimitry Andric     return std::nullopt;
370*06c3fb27SDimitry Andric   }
371bdd1243dSDimitry Andric };
372bdd1243dSDimitry Andric 
373bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
374bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
375bdd1243dSDimitry Andric 
376bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer
377bdd1243dSDimitry Andric /// out of bounds.
378bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget {
379bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
380bdd1243dSDimitry Andric   const UnaryOperator *Op;
381bdd1243dSDimitry Andric 
382bdd1243dSDimitry Andric public:
383bdd1243dSDimitry Andric   IncrementGadget(const MatchFinder::MatchResult &Result)
384bdd1243dSDimitry Andric       : WarningGadget(Kind::Increment),
385bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
386bdd1243dSDimitry Andric 
387bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
388bdd1243dSDimitry Andric     return G->getKind() == Kind::Increment;
389bdd1243dSDimitry Andric   }
390bdd1243dSDimitry Andric 
391bdd1243dSDimitry Andric   static Matcher matcher() {
392bdd1243dSDimitry Andric     return stmt(unaryOperator(
393bdd1243dSDimitry Andric       hasOperatorName("++"),
394bdd1243dSDimitry Andric       hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
395bdd1243dSDimitry Andric     ).bind(OpTag));
396bdd1243dSDimitry Andric   }
397bdd1243dSDimitry Andric 
398bdd1243dSDimitry Andric   const UnaryOperator *getBaseStmt() const override { return Op; }
399bdd1243dSDimitry Andric 
400bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
401bdd1243dSDimitry Andric     SmallVector<const DeclRefExpr *, 2> Uses;
402bdd1243dSDimitry Andric     if (const auto *DRE =
403bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
404bdd1243dSDimitry Andric       Uses.push_back(DRE);
405bdd1243dSDimitry Andric     }
406bdd1243dSDimitry Andric 
407bdd1243dSDimitry Andric     return std::move(Uses);
408bdd1243dSDimitry Andric   }
409bdd1243dSDimitry Andric };
410bdd1243dSDimitry Andric 
411bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer
412bdd1243dSDimitry Andric /// out of bounds.
413bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget {
414bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
415bdd1243dSDimitry Andric   const UnaryOperator *Op;
416bdd1243dSDimitry Andric 
417bdd1243dSDimitry Andric public:
418bdd1243dSDimitry Andric   DecrementGadget(const MatchFinder::MatchResult &Result)
419bdd1243dSDimitry Andric       : WarningGadget(Kind::Decrement),
420bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
421bdd1243dSDimitry Andric 
422bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
423bdd1243dSDimitry Andric     return G->getKind() == Kind::Decrement;
424bdd1243dSDimitry Andric   }
425bdd1243dSDimitry Andric 
426bdd1243dSDimitry Andric   static Matcher matcher() {
427bdd1243dSDimitry Andric     return stmt(unaryOperator(
428bdd1243dSDimitry Andric       hasOperatorName("--"),
429bdd1243dSDimitry Andric       hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
430bdd1243dSDimitry Andric     ).bind(OpTag));
431bdd1243dSDimitry Andric   }
432bdd1243dSDimitry Andric 
433bdd1243dSDimitry Andric   const UnaryOperator *getBaseStmt() const override { return Op; }
434bdd1243dSDimitry Andric 
435bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
436bdd1243dSDimitry Andric     if (const auto *DRE =
437bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
438bdd1243dSDimitry Andric       return {DRE};
439bdd1243dSDimitry Andric     }
440bdd1243dSDimitry Andric 
441bdd1243dSDimitry Andric     return {};
442bdd1243dSDimitry Andric   }
443bdd1243dSDimitry Andric };
444bdd1243dSDimitry Andric 
445bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
446bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array.
447bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget {
448*06c3fb27SDimitry Andric   static constexpr const char *const ArraySubscrTag = "ArraySubscript";
449bdd1243dSDimitry Andric   const ArraySubscriptExpr *ASE;
450bdd1243dSDimitry Andric 
451bdd1243dSDimitry Andric public:
452bdd1243dSDimitry Andric   ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
453bdd1243dSDimitry Andric       : WarningGadget(Kind::ArraySubscript),
454bdd1243dSDimitry Andric         ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
455bdd1243dSDimitry Andric 
456bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
457bdd1243dSDimitry Andric     return G->getKind() == Kind::ArraySubscript;
458bdd1243dSDimitry Andric   }
459bdd1243dSDimitry Andric 
460bdd1243dSDimitry Andric   static Matcher matcher() {
461bdd1243dSDimitry Andric     // FIXME: What if the index is integer literal 0? Should this be
462bdd1243dSDimitry Andric     // a safe gadget in this case?
463bdd1243dSDimitry Andric       // clang-format off
464bdd1243dSDimitry Andric       return stmt(arraySubscriptExpr(
465bdd1243dSDimitry Andric             hasBase(ignoringParenImpCasts(
466bdd1243dSDimitry Andric               anyOf(hasPointerType(), hasArrayType()))),
467*06c3fb27SDimitry Andric             unless(hasIndex(
468*06c3fb27SDimitry Andric                 anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
469*06c3fb27SDimitry Andric              )))
470bdd1243dSDimitry Andric             .bind(ArraySubscrTag));
471bdd1243dSDimitry Andric     // clang-format on
472bdd1243dSDimitry Andric   }
473bdd1243dSDimitry Andric 
474bdd1243dSDimitry Andric   const ArraySubscriptExpr *getBaseStmt() const override { return ASE; }
475bdd1243dSDimitry Andric 
476bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
477bdd1243dSDimitry Andric     if (const auto *DRE =
478bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
479bdd1243dSDimitry Andric       return {DRE};
480bdd1243dSDimitry Andric     }
481bdd1243dSDimitry Andric 
482bdd1243dSDimitry Andric     return {};
483bdd1243dSDimitry Andric   }
484bdd1243dSDimitry Andric };
485bdd1243dSDimitry Andric 
486bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms:
487bdd1243dSDimitry Andric ///  \code
488bdd1243dSDimitry Andric ///  ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
489bdd1243dSDimitry Andric ///  \endcode
490bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget {
491bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticTag = "ptrAdd";
492bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
493bdd1243dSDimitry Andric   const BinaryOperator *PA; // pointer arithmetic expression
494bdd1243dSDimitry Andric   const Expr *Ptr;          // the pointer expression in `PA`
495bdd1243dSDimitry Andric 
496bdd1243dSDimitry Andric public:
497bdd1243dSDimitry Andric   PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
498bdd1243dSDimitry Andric       : WarningGadget(Kind::PointerArithmetic),
499bdd1243dSDimitry Andric         PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
500bdd1243dSDimitry Andric         Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
501bdd1243dSDimitry Andric 
502bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
503bdd1243dSDimitry Andric     return G->getKind() == Kind::PointerArithmetic;
504bdd1243dSDimitry Andric   }
505bdd1243dSDimitry Andric 
506bdd1243dSDimitry Andric   static Matcher matcher() {
507*06c3fb27SDimitry Andric     auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
508*06c3fb27SDimitry Andric     auto PtrAtRight =
509*06c3fb27SDimitry Andric         allOf(hasOperatorName("+"),
510bdd1243dSDimitry Andric               hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
511bdd1243dSDimitry Andric               hasLHS(HasIntegerType));
512*06c3fb27SDimitry Andric     auto PtrAtLeft =
513*06c3fb27SDimitry Andric         allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
514bdd1243dSDimitry Andric                     hasOperatorName("+="), hasOperatorName("-=")),
515bdd1243dSDimitry Andric               hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
516bdd1243dSDimitry Andric               hasRHS(HasIntegerType));
517bdd1243dSDimitry Andric 
518*06c3fb27SDimitry Andric     return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight))
519*06c3fb27SDimitry Andric                     .bind(PointerArithmeticTag));
520bdd1243dSDimitry Andric   }
521bdd1243dSDimitry Andric 
522bdd1243dSDimitry Andric   const Stmt *getBaseStmt() const override { return PA; }
523bdd1243dSDimitry Andric 
524bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
525*06c3fb27SDimitry Andric     if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
526bdd1243dSDimitry Andric       return {DRE};
527bdd1243dSDimitry Andric     }
528bdd1243dSDimitry Andric 
529bdd1243dSDimitry Andric     return {};
530bdd1243dSDimitry Andric   }
531bdd1243dSDimitry Andric   // FIXME: pointer adding zero should be fine
532bdd1243dSDimitry Andric   // FIXME: this gadge will need a fix-it
533bdd1243dSDimitry Andric };
534*06c3fb27SDimitry Andric 
535*06c3fb27SDimitry Andric /// A pointer initialization expression of the form:
536*06c3fb27SDimitry Andric ///  \code
537*06c3fb27SDimitry Andric ///  int *p = q;
538*06c3fb27SDimitry Andric ///  \endcode
539*06c3fb27SDimitry Andric class PointerInitGadget : public FixableGadget {
540*06c3fb27SDimitry Andric private:
541*06c3fb27SDimitry Andric   static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
542*06c3fb27SDimitry Andric   static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
543*06c3fb27SDimitry Andric   const VarDecl * PtrInitLHS;         // the LHS pointer expression in `PI`
544*06c3fb27SDimitry Andric   const DeclRefExpr * PtrInitRHS;         // the RHS pointer expression in `PI`
545*06c3fb27SDimitry Andric 
546*06c3fb27SDimitry Andric public:
547*06c3fb27SDimitry Andric   PointerInitGadget(const MatchFinder::MatchResult &Result)
548*06c3fb27SDimitry Andric       : FixableGadget(Kind::PointerInit),
549*06c3fb27SDimitry Andric     PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
550*06c3fb27SDimitry Andric     PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
551*06c3fb27SDimitry Andric 
552*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
553*06c3fb27SDimitry Andric     return G->getKind() == Kind::PointerInit;
554*06c3fb27SDimitry Andric   }
555*06c3fb27SDimitry Andric 
556*06c3fb27SDimitry Andric   static Matcher matcher() {
557*06c3fb27SDimitry Andric     auto PtrInitStmt = declStmt(hasSingleDecl(varDecl(
558*06c3fb27SDimitry Andric                                  hasInitializer(ignoringImpCasts(declRefExpr(
559*06c3fb27SDimitry Andric                                                   hasPointerType()).
560*06c3fb27SDimitry Andric                                                   bind(PointerInitRHSTag)))).
561*06c3fb27SDimitry Andric                                               bind(PointerInitLHSTag)));
562*06c3fb27SDimitry Andric 
563*06c3fb27SDimitry Andric     return stmt(PtrInitStmt);
564*06c3fb27SDimitry Andric   }
565*06c3fb27SDimitry Andric 
566*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
567*06c3fb27SDimitry Andric 
568*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return nullptr; }
569*06c3fb27SDimitry Andric 
570*06c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
571*06c3fb27SDimitry Andric     return DeclUseList{PtrInitRHS};
572*06c3fb27SDimitry Andric   }
573*06c3fb27SDimitry Andric 
574*06c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
575*06c3fb27SDimitry Andric   getStrategyImplications() const override {
576*06c3fb27SDimitry Andric       return std::make_pair(PtrInitLHS,
577*06c3fb27SDimitry Andric                             cast<VarDecl>(PtrInitRHS->getDecl()));
578*06c3fb27SDimitry Andric   }
579*06c3fb27SDimitry Andric };
580*06c3fb27SDimitry Andric 
581*06c3fb27SDimitry Andric /// A pointer assignment expression of the form:
582*06c3fb27SDimitry Andric ///  \code
583*06c3fb27SDimitry Andric ///  p = q;
584*06c3fb27SDimitry Andric ///  \endcode
585*06c3fb27SDimitry Andric class PointerAssignmentGadget : public FixableGadget {
586*06c3fb27SDimitry Andric private:
587*06c3fb27SDimitry Andric   static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
588*06c3fb27SDimitry Andric   static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
589*06c3fb27SDimitry Andric   const DeclRefExpr * PtrLHS;         // the LHS pointer expression in `PA`
590*06c3fb27SDimitry Andric   const DeclRefExpr * PtrRHS;         // the RHS pointer expression in `PA`
591*06c3fb27SDimitry Andric 
592*06c3fb27SDimitry Andric public:
593*06c3fb27SDimitry Andric   PointerAssignmentGadget(const MatchFinder::MatchResult &Result)
594*06c3fb27SDimitry Andric       : FixableGadget(Kind::PointerAssignment),
595*06c3fb27SDimitry Andric     PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
596*06c3fb27SDimitry Andric     PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
597*06c3fb27SDimitry Andric 
598*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
599*06c3fb27SDimitry Andric     return G->getKind() == Kind::PointerAssignment;
600*06c3fb27SDimitry Andric   }
601*06c3fb27SDimitry Andric 
602*06c3fb27SDimitry Andric   static Matcher matcher() {
603*06c3fb27SDimitry Andric     auto PtrAssignExpr = binaryOperator(allOf(hasOperatorName("="),
604*06c3fb27SDimitry Andric       hasRHS(ignoringParenImpCasts(declRefExpr(hasPointerType(),
605*06c3fb27SDimitry Andric                                                to(varDecl())).
606*06c3fb27SDimitry Andric                                    bind(PointerAssignRHSTag))),
607*06c3fb27SDimitry Andric                                    hasLHS(declRefExpr(hasPointerType(),
608*06c3fb27SDimitry Andric                                                       to(varDecl())).
609*06c3fb27SDimitry Andric                                           bind(PointerAssignLHSTag))));
610*06c3fb27SDimitry Andric 
611*06c3fb27SDimitry Andric     return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
612*06c3fb27SDimitry Andric   }
613*06c3fb27SDimitry Andric 
614*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
615*06c3fb27SDimitry Andric 
616*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return nullptr; }
617*06c3fb27SDimitry Andric 
618*06c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
619*06c3fb27SDimitry Andric     return DeclUseList{PtrLHS, PtrRHS};
620*06c3fb27SDimitry Andric   }
621*06c3fb27SDimitry Andric 
622*06c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
623*06c3fb27SDimitry Andric   getStrategyImplications() const override {
624*06c3fb27SDimitry Andric     return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
625*06c3fb27SDimitry Andric                           cast<VarDecl>(PtrRHS->getDecl()));
626*06c3fb27SDimitry Andric   }
627*06c3fb27SDimitry Andric };
628*06c3fb27SDimitry Andric 
629*06c3fb27SDimitry Andric /// A call of a function or method that performs unchecked buffer operations
630*06c3fb27SDimitry Andric /// over one of its pointer parameters.
631*06c3fb27SDimitry Andric class UnsafeBufferUsageAttrGadget : public WarningGadget {
632*06c3fb27SDimitry Andric   constexpr static const char *const OpTag = "call_expr";
633*06c3fb27SDimitry Andric   const CallExpr *Op;
634*06c3fb27SDimitry Andric 
635*06c3fb27SDimitry Andric public:
636*06c3fb27SDimitry Andric   UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
637*06c3fb27SDimitry Andric       : WarningGadget(Kind::UnsafeBufferUsageAttr),
638*06c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {}
639*06c3fb27SDimitry Andric 
640*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
641*06c3fb27SDimitry Andric     return G->getKind() == Kind::UnsafeBufferUsageAttr;
642*06c3fb27SDimitry Andric   }
643*06c3fb27SDimitry Andric 
644*06c3fb27SDimitry Andric   static Matcher matcher() {
645*06c3fb27SDimitry Andric     return stmt(callExpr(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))))
646*06c3fb27SDimitry Andric                     .bind(OpTag));
647*06c3fb27SDimitry Andric   }
648*06c3fb27SDimitry Andric   const Stmt *getBaseStmt() const override { return Op; }
649*06c3fb27SDimitry Andric 
650*06c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override { return {}; }
651*06c3fb27SDimitry Andric };
652*06c3fb27SDimitry Andric 
653*06c3fb27SDimitry Andric // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
654*06c3fb27SDimitry Andric // Context (see `isInUnspecifiedLvalueContext`).
655*06c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
656*06c3fb27SDimitry Andric class ULCArraySubscriptGadget : public FixableGadget {
657*06c3fb27SDimitry Andric private:
658*06c3fb27SDimitry Andric   static constexpr const char *const ULCArraySubscriptTag =
659*06c3fb27SDimitry Andric       "ArraySubscriptUnderULC";
660*06c3fb27SDimitry Andric   const ArraySubscriptExpr *Node;
661*06c3fb27SDimitry Andric 
662*06c3fb27SDimitry Andric public:
663*06c3fb27SDimitry Andric   ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
664*06c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
665*06c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
666*06c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
667*06c3fb27SDimitry Andric   }
668*06c3fb27SDimitry Andric 
669*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
670*06c3fb27SDimitry Andric     return G->getKind() == Kind::ULCArraySubscript;
671*06c3fb27SDimitry Andric   }
672*06c3fb27SDimitry Andric 
673*06c3fb27SDimitry Andric   static Matcher matcher() {
674*06c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
675*06c3fb27SDimitry Andric     auto BaseIsArrayOrPtrDRE =
676*06c3fb27SDimitry Andric         hasBase(ignoringParenImpCasts(declRefExpr(ArrayOrPtr)));
677*06c3fb27SDimitry Andric     auto Target =
678*06c3fb27SDimitry Andric         arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
679*06c3fb27SDimitry Andric 
680*06c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
681*06c3fb27SDimitry Andric   }
682*06c3fb27SDimitry Andric 
683*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
684*06c3fb27SDimitry Andric 
685*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
686*06c3fb27SDimitry Andric 
687*06c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
688*06c3fb27SDimitry Andric     if (const auto *DRE =
689*06c3fb27SDimitry Andric             dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
690*06c3fb27SDimitry Andric       return {DRE};
691*06c3fb27SDimitry Andric     }
692*06c3fb27SDimitry Andric     return {};
693*06c3fb27SDimitry Andric   }
694*06c3fb27SDimitry Andric };
695*06c3fb27SDimitry Andric 
696*06c3fb27SDimitry Andric // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
697*06c3fb27SDimitry Andric // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
698*06c3fb27SDimitry Andric // fixit of the form `UPC(DRE.data())`.
699*06c3fb27SDimitry Andric class UPCStandalonePointerGadget : public FixableGadget {
700*06c3fb27SDimitry Andric private:
701*06c3fb27SDimitry Andric   static constexpr const char *const DeclRefExprTag = "StandalonePointer";
702*06c3fb27SDimitry Andric   const DeclRefExpr *Node;
703*06c3fb27SDimitry Andric 
704*06c3fb27SDimitry Andric public:
705*06c3fb27SDimitry Andric   UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
706*06c3fb27SDimitry Andric       : FixableGadget(Kind::UPCStandalonePointer),
707*06c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
708*06c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
709*06c3fb27SDimitry Andric   }
710*06c3fb27SDimitry Andric 
711*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
712*06c3fb27SDimitry Andric     return G->getKind() == Kind::UPCStandalonePointer;
713*06c3fb27SDimitry Andric   }
714*06c3fb27SDimitry Andric 
715*06c3fb27SDimitry Andric   static Matcher matcher() {
716*06c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
717*06c3fb27SDimitry Andric     auto target = expr(
718*06c3fb27SDimitry Andric         ignoringParenImpCasts(declRefExpr(allOf(ArrayOrPtr, to(varDecl()))).bind(DeclRefExprTag)));
719*06c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(target));
720*06c3fb27SDimitry Andric   }
721*06c3fb27SDimitry Andric 
722*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
723*06c3fb27SDimitry Andric 
724*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
725*06c3fb27SDimitry Andric 
726*06c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
727*06c3fb27SDimitry Andric     return {Node};
728*06c3fb27SDimitry Andric   }
729*06c3fb27SDimitry Andric };
730*06c3fb27SDimitry Andric 
731*06c3fb27SDimitry Andric class PointerDereferenceGadget : public FixableGadget {
732*06c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
733*06c3fb27SDimitry Andric   static constexpr const char *const OperatorTag = "op";
734*06c3fb27SDimitry Andric 
735*06c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
736*06c3fb27SDimitry Andric   const UnaryOperator *Op = nullptr;
737*06c3fb27SDimitry Andric 
738*06c3fb27SDimitry Andric public:
739*06c3fb27SDimitry Andric   PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
740*06c3fb27SDimitry Andric       : FixableGadget(Kind::PointerDereference),
741*06c3fb27SDimitry Andric         BaseDeclRefExpr(
742*06c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
743*06c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
744*06c3fb27SDimitry Andric 
745*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
746*06c3fb27SDimitry Andric     return G->getKind() == Kind::PointerDereference;
747*06c3fb27SDimitry Andric   }
748*06c3fb27SDimitry Andric 
749*06c3fb27SDimitry Andric   static Matcher matcher() {
750*06c3fb27SDimitry Andric     auto Target =
751*06c3fb27SDimitry Andric         unaryOperator(
752*06c3fb27SDimitry Andric             hasOperatorName("*"),
753*06c3fb27SDimitry Andric             has(expr(ignoringParenImpCasts(
754*06c3fb27SDimitry Andric                 declRefExpr(to(varDecl())).bind(BaseDeclRefExprTag)))))
755*06c3fb27SDimitry Andric             .bind(OperatorTag);
756*06c3fb27SDimitry Andric 
757*06c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
758*06c3fb27SDimitry Andric   }
759*06c3fb27SDimitry Andric 
760*06c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
761*06c3fb27SDimitry Andric     return {BaseDeclRefExpr};
762*06c3fb27SDimitry Andric   }
763*06c3fb27SDimitry Andric 
764*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const final { return Op; }
765*06c3fb27SDimitry Andric 
766*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
767*06c3fb27SDimitry Andric };
768*06c3fb27SDimitry Andric 
769*06c3fb27SDimitry Andric // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
770*06c3fb27SDimitry Andric // Context (see `isInUnspecifiedPointerContext`).
771*06c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
772*06c3fb27SDimitry Andric class UPCAddressofArraySubscriptGadget : public FixableGadget {
773*06c3fb27SDimitry Andric private:
774*06c3fb27SDimitry Andric   static constexpr const char *const UPCAddressofArraySubscriptTag =
775*06c3fb27SDimitry Andric       "AddressofArraySubscriptUnderUPC";
776*06c3fb27SDimitry Andric   const UnaryOperator *Node; // the `&DRE[any]` node
777*06c3fb27SDimitry Andric 
778*06c3fb27SDimitry Andric public:
779*06c3fb27SDimitry Andric   UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
780*06c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
781*06c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<UnaryOperator>(
782*06c3fb27SDimitry Andric             UPCAddressofArraySubscriptTag)) {
783*06c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
784*06c3fb27SDimitry Andric   }
785*06c3fb27SDimitry Andric 
786*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
787*06c3fb27SDimitry Andric     return G->getKind() == Kind::UPCAddressofArraySubscript;
788*06c3fb27SDimitry Andric   }
789*06c3fb27SDimitry Andric 
790*06c3fb27SDimitry Andric   static Matcher matcher() {
791*06c3fb27SDimitry Andric     return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
792*06c3fb27SDimitry Andric         unaryOperator(hasOperatorName("&"),
793*06c3fb27SDimitry Andric                       hasUnaryOperand(arraySubscriptExpr(
794*06c3fb27SDimitry Andric                           hasBase(ignoringParenImpCasts(declRefExpr())))))
795*06c3fb27SDimitry Andric             .bind(UPCAddressofArraySubscriptTag)))));
796*06c3fb27SDimitry Andric   }
797*06c3fb27SDimitry Andric 
798*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &) const override;
799*06c3fb27SDimitry Andric 
800*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
801*06c3fb27SDimitry Andric 
802*06c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
803*06c3fb27SDimitry Andric     const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
804*06c3fb27SDimitry Andric     const auto *DRE =
805*06c3fb27SDimitry Andric         cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreImpCasts());
806*06c3fb27SDimitry Andric     return {DRE};
807*06c3fb27SDimitry Andric   }
808*06c3fb27SDimitry Andric };
809bdd1243dSDimitry Andric } // namespace
810bdd1243dSDimitry Andric 
811bdd1243dSDimitry Andric namespace {
812bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect
813*06c3fb27SDimitry Andric // declarations to its uses and make sure we've covered all uses with our
814*06c3fb27SDimitry Andric // analysis before we try to fix the declaration.
815bdd1243dSDimitry Andric class DeclUseTracker {
816bdd1243dSDimitry Andric   using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
817bdd1243dSDimitry Andric   using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
818bdd1243dSDimitry Andric 
819bdd1243dSDimitry Andric   // Allocate on the heap for easier move.
820bdd1243dSDimitry Andric   std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
821bdd1243dSDimitry Andric   DefMapTy Defs{};
822bdd1243dSDimitry Andric 
823bdd1243dSDimitry Andric public:
824bdd1243dSDimitry Andric   DeclUseTracker() = default;
825bdd1243dSDimitry Andric   DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
826*06c3fb27SDimitry Andric   DeclUseTracker &operator=(const DeclUseTracker &) = delete;
827bdd1243dSDimitry Andric   DeclUseTracker(DeclUseTracker &&) = default;
828bdd1243dSDimitry Andric   DeclUseTracker &operator=(DeclUseTracker &&) = default;
829bdd1243dSDimitry Andric 
830bdd1243dSDimitry Andric   // Start tracking a freshly discovered DRE.
831bdd1243dSDimitry Andric   void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
832bdd1243dSDimitry Andric 
833bdd1243dSDimitry Andric   // Stop tracking the DRE as it's been fully figured out.
834bdd1243dSDimitry Andric   void claimUse(const DeclRefExpr *DRE) {
835bdd1243dSDimitry Andric     assert(Uses->count(DRE) &&
836bdd1243dSDimitry Andric            "DRE not found or claimed by multiple matchers!");
837bdd1243dSDimitry Andric     Uses->erase(DRE);
838bdd1243dSDimitry Andric   }
839bdd1243dSDimitry Andric 
840bdd1243dSDimitry Andric   // A variable is unclaimed if at least one use is unclaimed.
841bdd1243dSDimitry Andric   bool hasUnclaimedUses(const VarDecl *VD) const {
842bdd1243dSDimitry Andric     // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
843bdd1243dSDimitry Andric     return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
844bdd1243dSDimitry Andric       return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
845bdd1243dSDimitry Andric     });
846bdd1243dSDimitry Andric   }
847bdd1243dSDimitry Andric 
848bdd1243dSDimitry Andric   void discoverDecl(const DeclStmt *DS) {
849bdd1243dSDimitry Andric     for (const Decl *D : DS->decls()) {
850bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(D)) {
851bdd1243dSDimitry Andric         // FIXME: Assertion temporarily disabled due to a bug in
852bdd1243dSDimitry Andric         // ASTMatcher internal behavior in presence of GNU
853bdd1243dSDimitry Andric         // statement-expressions. We need to properly investigate this
854bdd1243dSDimitry Andric         // because it can screw up our algorithm in other ways.
855bdd1243dSDimitry Andric         // assert(Defs.count(VD) == 0 && "Definition already discovered!");
856bdd1243dSDimitry Andric         Defs[VD] = DS;
857bdd1243dSDimitry Andric       }
858bdd1243dSDimitry Andric     }
859bdd1243dSDimitry Andric   }
860bdd1243dSDimitry Andric 
861bdd1243dSDimitry Andric   const DeclStmt *lookupDecl(const VarDecl *VD) const {
862bdd1243dSDimitry Andric     auto It = Defs.find(VD);
863bdd1243dSDimitry Andric     assert(It != Defs.end() && "Definition never discovered!");
864bdd1243dSDimitry Andric     return It->second;
865bdd1243dSDimitry Andric   }
866bdd1243dSDimitry Andric };
867bdd1243dSDimitry Andric } // namespace
868bdd1243dSDimitry Andric 
869bdd1243dSDimitry Andric namespace {
870bdd1243dSDimitry Andric // Strategy is a map from variables to the way we plan to emit fixes for
871bdd1243dSDimitry Andric // these variables. It is figured out gradually by trying different fixes
872bdd1243dSDimitry Andric // for different variables depending on gadgets in which these variables
873bdd1243dSDimitry Andric // participate.
874bdd1243dSDimitry Andric class Strategy {
875bdd1243dSDimitry Andric public:
876bdd1243dSDimitry Andric   enum class Kind {
877bdd1243dSDimitry Andric     Wontfix,  // We don't plan to emit a fixit for this variable.
878bdd1243dSDimitry Andric     Span,     // We recommend replacing the variable with std::span.
879bdd1243dSDimitry Andric     Iterator, // We recommend replacing the variable with std::span::iterator.
880bdd1243dSDimitry Andric     Array,    // We recommend replacing the variable with std::array.
881bdd1243dSDimitry Andric     Vector    // We recommend replacing the variable with std::vector.
882bdd1243dSDimitry Andric   };
883bdd1243dSDimitry Andric 
884bdd1243dSDimitry Andric private:
885bdd1243dSDimitry Andric   using MapTy = llvm::DenseMap<const VarDecl *, Kind>;
886bdd1243dSDimitry Andric 
887bdd1243dSDimitry Andric   MapTy Map;
888bdd1243dSDimitry Andric 
889bdd1243dSDimitry Andric public:
890bdd1243dSDimitry Andric   Strategy() = default;
891bdd1243dSDimitry Andric   Strategy(const Strategy &) = delete; // Let's avoid copies.
892*06c3fb27SDimitry Andric   Strategy &operator=(const Strategy &) = delete;
893bdd1243dSDimitry Andric   Strategy(Strategy &&) = default;
894*06c3fb27SDimitry Andric   Strategy &operator=(Strategy &&) = default;
895bdd1243dSDimitry Andric 
896*06c3fb27SDimitry Andric   void set(const VarDecl *VD, Kind K) { Map[VD] = K; }
897bdd1243dSDimitry Andric 
898bdd1243dSDimitry Andric   Kind lookup(const VarDecl *VD) const {
899bdd1243dSDimitry Andric     auto I = Map.find(VD);
900bdd1243dSDimitry Andric     if (I == Map.end())
901bdd1243dSDimitry Andric       return Kind::Wontfix;
902bdd1243dSDimitry Andric 
903bdd1243dSDimitry Andric     return I->second;
904bdd1243dSDimitry Andric   }
905bdd1243dSDimitry Andric };
906bdd1243dSDimitry Andric } // namespace
907bdd1243dSDimitry Andric 
908*06c3fb27SDimitry Andric 
909*06c3fb27SDimitry Andric // Representing a pointer type expression of the form `++Ptr` in an Unspecified
910*06c3fb27SDimitry Andric // Pointer Context (UPC):
911*06c3fb27SDimitry Andric class UPCPreIncrementGadget : public FixableGadget {
912*06c3fb27SDimitry Andric private:
913*06c3fb27SDimitry Andric   static constexpr const char *const UPCPreIncrementTag =
914*06c3fb27SDimitry Andric     "PointerPreIncrementUnderUPC";
915*06c3fb27SDimitry Andric   const UnaryOperator *Node; // the `++Ptr` node
916*06c3fb27SDimitry Andric 
917*06c3fb27SDimitry Andric public:
918*06c3fb27SDimitry Andric   UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
919*06c3fb27SDimitry Andric     : FixableGadget(Kind::UPCPreIncrement),
920*06c3fb27SDimitry Andric       Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
921*06c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
922*06c3fb27SDimitry Andric   }
923*06c3fb27SDimitry Andric 
924*06c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
925*06c3fb27SDimitry Andric     return G->getKind() == Kind::UPCPreIncrement;
926*06c3fb27SDimitry Andric   }
927*06c3fb27SDimitry Andric 
928*06c3fb27SDimitry Andric   static Matcher matcher() {
929*06c3fb27SDimitry Andric     // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
930*06c3fb27SDimitry Andric     // Although currently we can only provide fix-its when `Ptr` is a DRE, we
931*06c3fb27SDimitry Andric     // can have the matcher be general, so long as `getClaimedVarUseSites` does
932*06c3fb27SDimitry Andric     // things right.
933*06c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
934*06c3fb27SDimitry Andric 								    unaryOperator(isPreInc(),
935*06c3fb27SDimitry Andric 										  hasUnaryOperand(declRefExpr())
936*06c3fb27SDimitry Andric 										  ).bind(UPCPreIncrementTag)))));
937*06c3fb27SDimitry Andric   }
938*06c3fb27SDimitry Andric 
939*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
940*06c3fb27SDimitry Andric 
941*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
942*06c3fb27SDimitry Andric 
943*06c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
944*06c3fb27SDimitry Andric     return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
945*06c3fb27SDimitry Andric   }
946*06c3fb27SDimitry Andric };
947*06c3fb27SDimitry Andric 
948*06c3fb27SDimitry Andric // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
949*06c3fb27SDimitry Andric // ptr)`:
950*06c3fb27SDimitry Andric class DerefSimplePtrArithFixableGadget : public FixableGadget {
951*06c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
952*06c3fb27SDimitry Andric   static constexpr const char *const DerefOpTag = "DerefOp";
953*06c3fb27SDimitry Andric   static constexpr const char *const AddOpTag = "AddOp";
954*06c3fb27SDimitry Andric   static constexpr const char *const OffsetTag = "Offset";
955*06c3fb27SDimitry Andric 
956*06c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
957*06c3fb27SDimitry Andric   const UnaryOperator *DerefOp = nullptr;
958*06c3fb27SDimitry Andric   const BinaryOperator *AddOp = nullptr;
959*06c3fb27SDimitry Andric   const IntegerLiteral *Offset = nullptr;
960*06c3fb27SDimitry Andric 
961*06c3fb27SDimitry Andric public:
962*06c3fb27SDimitry Andric   DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)
963*06c3fb27SDimitry Andric       : FixableGadget(Kind::DerefSimplePtrArithFixable),
964*06c3fb27SDimitry Andric         BaseDeclRefExpr(
965*06c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
966*06c3fb27SDimitry Andric         DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
967*06c3fb27SDimitry Andric         AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
968*06c3fb27SDimitry Andric         Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
969*06c3fb27SDimitry Andric 
970*06c3fb27SDimitry Andric   static Matcher matcher() {
971*06c3fb27SDimitry Andric     // clang-format off
972*06c3fb27SDimitry Andric     auto ThePtr = expr(hasPointerType(),
973*06c3fb27SDimitry Andric                        ignoringImpCasts(declRefExpr(to(varDecl())).bind(BaseDeclRefExprTag)));
974*06c3fb27SDimitry Andric     auto PlusOverPtrAndInteger = expr(anyOf(
975*06c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
976*06c3fb27SDimitry Andric                          hasRHS(integerLiteral().bind(OffsetTag)))
977*06c3fb27SDimitry Andric                          .bind(AddOpTag),
978*06c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
979*06c3fb27SDimitry Andric                          hasLHS(integerLiteral().bind(OffsetTag)))
980*06c3fb27SDimitry Andric                          .bind(AddOpTag)));
981*06c3fb27SDimitry Andric     return isInUnspecifiedLvalueContext(unaryOperator(
982*06c3fb27SDimitry Andric         hasOperatorName("*"),
983*06c3fb27SDimitry Andric         hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
984*06c3fb27SDimitry Andric         .bind(DerefOpTag));
985*06c3fb27SDimitry Andric     // clang-format on
986*06c3fb27SDimitry Andric   }
987*06c3fb27SDimitry Andric 
988*06c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &s) const final;
989*06c3fb27SDimitry Andric 
990*06c3fb27SDimitry Andric   // TODO remove this method from FixableGadget interface
991*06c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const final { return nullptr; }
992*06c3fb27SDimitry Andric 
993*06c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const final {
994*06c3fb27SDimitry Andric     return {BaseDeclRefExpr};
995*06c3fb27SDimitry Andric   }
996*06c3fb27SDimitry Andric };
997*06c3fb27SDimitry Andric 
998bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits.
999*06c3fb27SDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
1000*06c3fb27SDimitry Andric findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
1001*06c3fb27SDimitry Andric             bool EmitSuggestions) {
1002bdd1243dSDimitry Andric 
1003bdd1243dSDimitry Andric   struct GadgetFinderCallback : MatchFinder::MatchCallback {
1004bdd1243dSDimitry Andric     FixableGadgetList FixableGadgets;
1005bdd1243dSDimitry Andric     WarningGadgetList WarningGadgets;
1006bdd1243dSDimitry Andric     DeclUseTracker Tracker;
1007bdd1243dSDimitry Andric 
1008bdd1243dSDimitry Andric     void run(const MatchFinder::MatchResult &Result) override {
1009bdd1243dSDimitry Andric       // In debug mode, assert that we've found exactly one gadget.
1010bdd1243dSDimitry Andric       // This helps us avoid conflicts in .bind() tags.
1011bdd1243dSDimitry Andric #if NDEBUG
1012bdd1243dSDimitry Andric #define NEXT return
1013bdd1243dSDimitry Andric #else
1014bdd1243dSDimitry Andric       [[maybe_unused]] int numFound = 0;
1015bdd1243dSDimitry Andric #define NEXT ++numFound
1016bdd1243dSDimitry Andric #endif
1017bdd1243dSDimitry Andric 
1018bdd1243dSDimitry Andric       if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
1019bdd1243dSDimitry Andric         Tracker.discoverUse(DRE);
1020bdd1243dSDimitry Andric         NEXT;
1021bdd1243dSDimitry Andric       }
1022bdd1243dSDimitry Andric 
1023bdd1243dSDimitry Andric       if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
1024bdd1243dSDimitry Andric         Tracker.discoverDecl(DS);
1025bdd1243dSDimitry Andric         NEXT;
1026bdd1243dSDimitry Andric       }
1027bdd1243dSDimitry Andric 
1028bdd1243dSDimitry Andric       // Figure out which matcher we've found, and call the appropriate
1029bdd1243dSDimitry Andric       // subclass constructor.
1030bdd1243dSDimitry Andric       // FIXME: Can we do this more logarithmically?
1031bdd1243dSDimitry Andric #define FIXABLE_GADGET(name)                                                   \
1032bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1033bdd1243dSDimitry Andric     FixableGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1034bdd1243dSDimitry Andric     NEXT;                                                                      \
1035bdd1243dSDimitry Andric   }
1036bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1037bdd1243dSDimitry Andric #define WARNING_GADGET(name)                                                   \
1038bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1039bdd1243dSDimitry Andric     WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1040bdd1243dSDimitry Andric     NEXT;                                                                      \
1041bdd1243dSDimitry Andric   }
1042bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1043bdd1243dSDimitry Andric 
1044bdd1243dSDimitry Andric       assert(numFound >= 1 && "Gadgets not found in match result!");
1045bdd1243dSDimitry Andric       assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
1046bdd1243dSDimitry Andric     }
1047bdd1243dSDimitry Andric   };
1048bdd1243dSDimitry Andric 
1049bdd1243dSDimitry Andric   MatchFinder M;
1050bdd1243dSDimitry Andric   GadgetFinderCallback CB;
1051bdd1243dSDimitry Andric 
1052bdd1243dSDimitry Andric   // clang-format off
1053bdd1243dSDimitry Andric   M.addMatcher(
1054*06c3fb27SDimitry Andric       stmt(
1055*06c3fb27SDimitry Andric         forEachDescendantEvaluatedStmt(stmt(anyOf(
1056bdd1243dSDimitry Andric           // Add Gadget::matcher() for every gadget in the registry.
1057*06c3fb27SDimitry Andric #define WARNING_GADGET(x)                                                      \
1058*06c3fb27SDimitry Andric           allOf(x ## Gadget::matcher().bind(#x),                               \
1059*06c3fb27SDimitry Andric                 notInSafeBufferOptOut(&Handler)),
1060*06c3fb27SDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1061*06c3fb27SDimitry Andric             // Avoid a hanging comma.
1062*06c3fb27SDimitry Andric             unless(stmt())
1063*06c3fb27SDimitry Andric         )))
1064*06c3fb27SDimitry Andric     ),
1065*06c3fb27SDimitry Andric     &CB
1066*06c3fb27SDimitry Andric   );
1067*06c3fb27SDimitry Andric   // clang-format on
1068*06c3fb27SDimitry Andric 
1069*06c3fb27SDimitry Andric   if (EmitSuggestions) {
1070*06c3fb27SDimitry Andric     // clang-format off
1071*06c3fb27SDimitry Andric     M.addMatcher(
1072*06c3fb27SDimitry Andric         stmt(
1073*06c3fb27SDimitry Andric           forEachDescendantStmt(stmt(eachOf(
1074*06c3fb27SDimitry Andric #define FIXABLE_GADGET(x)                                                      \
1075bdd1243dSDimitry Andric             x ## Gadget::matcher().bind(#x),
1076bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1077bdd1243dSDimitry Andric             // In parallel, match all DeclRefExprs so that to find out
1078bdd1243dSDimitry Andric             // whether there are any uncovered by gadgets.
1079bdd1243dSDimitry Andric             declRefExpr(anyOf(hasPointerType(), hasArrayType()),
1080bdd1243dSDimitry Andric                         to(varDecl())).bind("any_dre"),
1081bdd1243dSDimitry Andric             // Also match DeclStmts because we'll need them when fixing
1082bdd1243dSDimitry Andric             // their underlying VarDecls that otherwise don't have
1083bdd1243dSDimitry Andric             // any backreferences to DeclStmts.
1084bdd1243dSDimitry Andric             declStmt().bind("any_ds")
1085*06c3fb27SDimitry Andric           )))
1086*06c3fb27SDimitry Andric       ),
1087bdd1243dSDimitry Andric       &CB
1088bdd1243dSDimitry Andric     );
1089bdd1243dSDimitry Andric     // clang-format on
1090*06c3fb27SDimitry Andric   }
1091bdd1243dSDimitry Andric 
1092bdd1243dSDimitry Andric   M.match(*D->getBody(), D->getASTContext());
1093bdd1243dSDimitry Andric 
1094bdd1243dSDimitry Andric   // Gadgets "claim" variables they're responsible for. Once this loop finishes,
1095bdd1243dSDimitry Andric   // the tracker will only track DREs that weren't claimed by any gadgets,
1096bdd1243dSDimitry Andric   // i.e. not understood by the analysis.
1097bdd1243dSDimitry Andric   for (const auto &G : CB.FixableGadgets) {
1098bdd1243dSDimitry Andric     for (const auto *DRE : G->getClaimedVarUseSites()) {
1099bdd1243dSDimitry Andric       CB.Tracker.claimUse(DRE);
1100bdd1243dSDimitry Andric     }
1101bdd1243dSDimitry Andric   }
1102bdd1243dSDimitry Andric 
1103*06c3fb27SDimitry Andric   return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
1104*06c3fb27SDimitry Andric           std::move(CB.Tracker)};
1105bdd1243dSDimitry Andric }
1106bdd1243dSDimitry Andric 
1107*06c3fb27SDimitry Andric // Compares AST nodes by source locations.
1108*06c3fb27SDimitry Andric template <typename NodeTy> struct CompareNode {
1109*06c3fb27SDimitry Andric   bool operator()(const NodeTy *N1, const NodeTy *N2) const {
1110*06c3fb27SDimitry Andric     return N1->getBeginLoc().getRawEncoding() <
1111*06c3fb27SDimitry Andric            N2->getBeginLoc().getRawEncoding();
1112*06c3fb27SDimitry Andric   }
1113*06c3fb27SDimitry Andric };
1114*06c3fb27SDimitry Andric 
1115bdd1243dSDimitry Andric struct WarningGadgetSets {
1116*06c3fb27SDimitry Andric   std::map<const VarDecl *, std::set<const WarningGadget *>,
1117*06c3fb27SDimitry Andric            // To keep keys sorted by their locations in the map so that the
1118*06c3fb27SDimitry Andric            // order is deterministic:
1119*06c3fb27SDimitry Andric            CompareNode<VarDecl>>
1120*06c3fb27SDimitry Andric       byVar;
1121bdd1243dSDimitry Andric   // These Gadgets are not related to pointer variables (e. g. temporaries).
1122*06c3fb27SDimitry Andric   llvm::SmallVector<const WarningGadget *, 16> noVar;
1123bdd1243dSDimitry Andric };
1124bdd1243dSDimitry Andric 
1125bdd1243dSDimitry Andric static WarningGadgetSets
1126*06c3fb27SDimitry Andric groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
1127bdd1243dSDimitry Andric   WarningGadgetSets result;
1128bdd1243dSDimitry Andric   // If some gadgets cover more than one
1129bdd1243dSDimitry Andric   // variable, they'll appear more than once in the map.
1130bdd1243dSDimitry Andric   for (auto &G : AllUnsafeOperations) {
1131bdd1243dSDimitry Andric     DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
1132bdd1243dSDimitry Andric 
1133bdd1243dSDimitry Andric     bool AssociatedWithVarDecl = false;
1134bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
1135bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1136*06c3fb27SDimitry Andric         result.byVar[VD].insert(G.get());
1137bdd1243dSDimitry Andric         AssociatedWithVarDecl = true;
1138bdd1243dSDimitry Andric       }
1139bdd1243dSDimitry Andric     }
1140bdd1243dSDimitry Andric 
1141bdd1243dSDimitry Andric     if (!AssociatedWithVarDecl) {
1142*06c3fb27SDimitry Andric       result.noVar.push_back(G.get());
1143bdd1243dSDimitry Andric       continue;
1144bdd1243dSDimitry Andric     }
1145bdd1243dSDimitry Andric   }
1146bdd1243dSDimitry Andric   return result;
1147bdd1243dSDimitry Andric }
1148bdd1243dSDimitry Andric 
1149bdd1243dSDimitry Andric struct FixableGadgetSets {
1150*06c3fb27SDimitry Andric   std::map<const VarDecl *, std::set<const FixableGadget *>> byVar;
1151bdd1243dSDimitry Andric };
1152bdd1243dSDimitry Andric 
1153bdd1243dSDimitry Andric static FixableGadgetSets
1154bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
1155bdd1243dSDimitry Andric   FixableGadgetSets FixablesForUnsafeVars;
1156bdd1243dSDimitry Andric   for (auto &F : AllFixableOperations) {
1157bdd1243dSDimitry Andric     DeclUseList DREs = F->getClaimedVarUseSites();
1158bdd1243dSDimitry Andric 
1159bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : DREs) {
1160bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1161*06c3fb27SDimitry Andric         FixablesForUnsafeVars.byVar[VD].insert(F.get());
1162bdd1243dSDimitry Andric       }
1163bdd1243dSDimitry Andric     }
1164bdd1243dSDimitry Andric   }
1165bdd1243dSDimitry Andric   return FixablesForUnsafeVars;
1166bdd1243dSDimitry Andric }
1167bdd1243dSDimitry Andric 
1168*06c3fb27SDimitry Andric bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts,
1169*06c3fb27SDimitry Andric                                   const SourceManager &SM) {
1170*06c3fb27SDimitry Andric   // A simple interval overlap detection algorithm.  Sorts all ranges by their
1171*06c3fb27SDimitry Andric   // begin location then finds the first overlap in one pass.
1172*06c3fb27SDimitry Andric   std::vector<const FixItHint *> All; // a copy of `FixIts`
1173*06c3fb27SDimitry Andric 
1174*06c3fb27SDimitry Andric   for (const FixItHint &H : FixIts)
1175*06c3fb27SDimitry Andric     All.push_back(&H);
1176*06c3fb27SDimitry Andric   std::sort(All.begin(), All.end(),
1177*06c3fb27SDimitry Andric             [&SM](const FixItHint *H1, const FixItHint *H2) {
1178*06c3fb27SDimitry Andric               return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
1179*06c3fb27SDimitry Andric                                                   H2->RemoveRange.getBegin());
1180*06c3fb27SDimitry Andric             });
1181*06c3fb27SDimitry Andric 
1182*06c3fb27SDimitry Andric   const FixItHint *CurrHint = nullptr;
1183*06c3fb27SDimitry Andric 
1184*06c3fb27SDimitry Andric   for (const FixItHint *Hint : All) {
1185*06c3fb27SDimitry Andric     if (!CurrHint ||
1186*06c3fb27SDimitry Andric         SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
1187*06c3fb27SDimitry Andric                                      Hint->RemoveRange.getBegin())) {
1188*06c3fb27SDimitry Andric       // Either to initialize `CurrHint` or `CurrHint` does not
1189*06c3fb27SDimitry Andric       // overlap with `Hint`:
1190*06c3fb27SDimitry Andric       CurrHint = Hint;
1191*06c3fb27SDimitry Andric     } else
1192*06c3fb27SDimitry Andric       // In case `Hint` overlaps the `CurrHint`, we found at least one
1193*06c3fb27SDimitry Andric       // conflict:
1194*06c3fb27SDimitry Andric       return true;
1195*06c3fb27SDimitry Andric   }
1196*06c3fb27SDimitry Andric   return false;
1197*06c3fb27SDimitry Andric }
1198*06c3fb27SDimitry Andric 
1199*06c3fb27SDimitry Andric std::optional<FixItList>
1200*06c3fb27SDimitry Andric PointerAssignmentGadget::getFixits(const Strategy &S) const {
1201*06c3fb27SDimitry Andric   const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
1202*06c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
1203*06c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
1204*06c3fb27SDimitry Andric     case Strategy::Kind::Span:
1205*06c3fb27SDimitry Andric       if (S.lookup(RightVD) == Strategy::Kind::Span)
1206*06c3fb27SDimitry Andric         return FixItList{};
1207*06c3fb27SDimitry Andric       return std::nullopt;
1208*06c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
1209*06c3fb27SDimitry Andric       return std::nullopt;
1210*06c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
1211*06c3fb27SDimitry Andric     case Strategy::Kind::Array:
1212*06c3fb27SDimitry Andric     case Strategy::Kind::Vector:
1213*06c3fb27SDimitry Andric       llvm_unreachable("unsupported strategies for FixableGadgets");
1214*06c3fb27SDimitry Andric   }
1215*06c3fb27SDimitry Andric   return std::nullopt;
1216*06c3fb27SDimitry Andric }
1217*06c3fb27SDimitry Andric 
1218*06c3fb27SDimitry Andric std::optional<FixItList>
1219*06c3fb27SDimitry Andric PointerInitGadget::getFixits(const Strategy &S) const {
1220*06c3fb27SDimitry Andric   const auto *LeftVD = PtrInitLHS;
1221*06c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
1222*06c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
1223*06c3fb27SDimitry Andric     case Strategy::Kind::Span:
1224*06c3fb27SDimitry Andric       if (S.lookup(RightVD) == Strategy::Kind::Span)
1225*06c3fb27SDimitry Andric         return FixItList{};
1226*06c3fb27SDimitry Andric       return std::nullopt;
1227*06c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
1228*06c3fb27SDimitry Andric       return std::nullopt;
1229*06c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
1230*06c3fb27SDimitry Andric     case Strategy::Kind::Array:
1231*06c3fb27SDimitry Andric     case Strategy::Kind::Vector:
1232*06c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
1233*06c3fb27SDimitry Andric   }
1234*06c3fb27SDimitry Andric   return std::nullopt;
1235*06c3fb27SDimitry Andric }
1236*06c3fb27SDimitry Andric 
1237*06c3fb27SDimitry Andric std::optional<FixItList>
1238*06c3fb27SDimitry Andric ULCArraySubscriptGadget::getFixits(const Strategy &S) const {
1239*06c3fb27SDimitry Andric   if (const auto *DRE =
1240*06c3fb27SDimitry Andric           dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
1241*06c3fb27SDimitry Andric     if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1242*06c3fb27SDimitry Andric       switch (S.lookup(VD)) {
1243*06c3fb27SDimitry Andric       case Strategy::Kind::Span: {
1244*06c3fb27SDimitry Andric         // If the index has a negative constant value, we give up as no valid
1245*06c3fb27SDimitry Andric         // fix-it can be generated:
1246*06c3fb27SDimitry Andric         const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
1247*06c3fb27SDimitry Andric             VD->getASTContext();
1248*06c3fb27SDimitry Andric         if (auto ConstVal = Node->getIdx()->getIntegerConstantExpr(Ctx)) {
1249*06c3fb27SDimitry Andric           if (ConstVal->isNegative())
1250*06c3fb27SDimitry Andric             return std::nullopt;
1251*06c3fb27SDimitry Andric         } else if (!Node->getIdx()->getType()->isUnsignedIntegerType())
1252*06c3fb27SDimitry Andric           return std::nullopt;
1253*06c3fb27SDimitry Andric         // no-op is a good fix-it, otherwise
1254*06c3fb27SDimitry Andric         return FixItList{};
1255*06c3fb27SDimitry Andric       }
1256*06c3fb27SDimitry Andric       case Strategy::Kind::Wontfix:
1257*06c3fb27SDimitry Andric       case Strategy::Kind::Iterator:
1258*06c3fb27SDimitry Andric       case Strategy::Kind::Array:
1259*06c3fb27SDimitry Andric       case Strategy::Kind::Vector:
1260*06c3fb27SDimitry Andric         llvm_unreachable("unsupported strategies for FixableGadgets");
1261*06c3fb27SDimitry Andric       }
1262*06c3fb27SDimitry Andric     }
1263*06c3fb27SDimitry Andric   return std::nullopt;
1264*06c3fb27SDimitry Andric }
1265*06c3fb27SDimitry Andric 
1266*06c3fb27SDimitry Andric static std::optional<FixItList> // forward declaration
1267*06c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
1268*06c3fb27SDimitry Andric 
1269*06c3fb27SDimitry Andric std::optional<FixItList>
1270*06c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget::getFixits(const Strategy &S) const {
1271*06c3fb27SDimitry Andric   auto DREs = getClaimedVarUseSites();
1272*06c3fb27SDimitry Andric   const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
1273*06c3fb27SDimitry Andric 
1274*06c3fb27SDimitry Andric   switch (S.lookup(VD)) {
1275*06c3fb27SDimitry Andric   case Strategy::Kind::Span:
1276*06c3fb27SDimitry Andric     return fixUPCAddressofArraySubscriptWithSpan(Node);
1277*06c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
1278*06c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
1279*06c3fb27SDimitry Andric   case Strategy::Kind::Array:
1280*06c3fb27SDimitry Andric   case Strategy::Kind::Vector:
1281*06c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
1282*06c3fb27SDimitry Andric   }
1283*06c3fb27SDimitry Andric   return std::nullopt; // something went wrong, no fix-it
1284*06c3fb27SDimitry Andric }
1285*06c3fb27SDimitry Andric 
1286*06c3fb27SDimitry Andric // FIXME: this function should be customizable through format
1287*06c3fb27SDimitry Andric static StringRef getEndOfLine() {
1288*06c3fb27SDimitry Andric   static const char *const EOL = "\n";
1289*06c3fb27SDimitry Andric   return EOL;
1290*06c3fb27SDimitry Andric }
1291*06c3fb27SDimitry Andric 
1292*06c3fb27SDimitry Andric // Returns the text indicating that the user needs to provide input there:
1293*06c3fb27SDimitry Andric std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
1294*06c3fb27SDimitry Andric   std::string s = std::string("<# ");
1295*06c3fb27SDimitry Andric   s += HintTextToUser;
1296*06c3fb27SDimitry Andric   s += " #>";
1297*06c3fb27SDimitry Andric   return s;
1298*06c3fb27SDimitry Andric }
1299*06c3fb27SDimitry Andric 
1300*06c3fb27SDimitry Andric // Return the text representation of the given `APInt Val`:
1301*06c3fb27SDimitry Andric static std::string getAPIntText(APInt Val) {
1302*06c3fb27SDimitry Andric   SmallVector<char> Txt;
1303*06c3fb27SDimitry Andric   Val.toString(Txt, 10, true);
1304*06c3fb27SDimitry Andric   // APInt::toString does not add '\0' to the end of the string for us:
1305*06c3fb27SDimitry Andric   Txt.push_back('\0');
1306*06c3fb27SDimitry Andric   return Txt.data();
1307*06c3fb27SDimitry Andric }
1308*06c3fb27SDimitry Andric 
1309*06c3fb27SDimitry Andric // Return the source location of the last character of the AST `Node`.
1310*06c3fb27SDimitry Andric template <typename NodeTy>
1311*06c3fb27SDimitry Andric static std::optional<SourceLocation>
1312*06c3fb27SDimitry Andric getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
1313*06c3fb27SDimitry Andric               const LangOptions &LangOpts) {
1314*06c3fb27SDimitry Andric   unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
1315*06c3fb27SDimitry Andric   SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
1316*06c3fb27SDimitry Andric 
1317*06c3fb27SDimitry Andric   if (Loc.isValid())
1318*06c3fb27SDimitry Andric     return Loc;
1319*06c3fb27SDimitry Andric 
1320*06c3fb27SDimitry Andric   return std::nullopt;
1321*06c3fb27SDimitry Andric }
1322*06c3fb27SDimitry Andric 
1323*06c3fb27SDimitry Andric // Return the source location just past the last character of the AST `Node`.
1324*06c3fb27SDimitry Andric template <typename NodeTy>
1325*06c3fb27SDimitry Andric static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
1326*06c3fb27SDimitry Andric                                                 const SourceManager &SM,
1327*06c3fb27SDimitry Andric                                                 const LangOptions &LangOpts) {
1328*06c3fb27SDimitry Andric   SourceLocation Loc =
1329*06c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
1330*06c3fb27SDimitry Andric 
1331*06c3fb27SDimitry Andric   if (Loc.isValid())
1332*06c3fb27SDimitry Andric     return Loc;
1333*06c3fb27SDimitry Andric 
1334*06c3fb27SDimitry Andric   return std::nullopt;
1335*06c3fb27SDimitry Andric }
1336*06c3fb27SDimitry Andric 
1337*06c3fb27SDimitry Andric // Return text representation of an `Expr`.
1338*06c3fb27SDimitry Andric static std::optional<StringRef> getExprText(const Expr *E,
1339*06c3fb27SDimitry Andric                                             const SourceManager &SM,
1340*06c3fb27SDimitry Andric                                             const LangOptions &LangOpts) {
1341*06c3fb27SDimitry Andric   std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
1342*06c3fb27SDimitry Andric 
1343*06c3fb27SDimitry Andric   if (LastCharLoc)
1344*06c3fb27SDimitry Andric     return Lexer::getSourceText(
1345*06c3fb27SDimitry Andric         CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
1346*06c3fb27SDimitry Andric         LangOpts);
1347*06c3fb27SDimitry Andric 
1348*06c3fb27SDimitry Andric   return std::nullopt;
1349*06c3fb27SDimitry Andric }
1350*06c3fb27SDimitry Andric 
1351*06c3fb27SDimitry Andric // Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
1352*06c3fb27SDimitry Andric static std::optional<StringRef> getRangeText(SourceRange SR,
1353*06c3fb27SDimitry Andric                                              const SourceManager &SM,
1354*06c3fb27SDimitry Andric                                              const LangOptions &LangOpts) {
1355*06c3fb27SDimitry Andric   bool Invalid = false;
1356*06c3fb27SDimitry Andric   CharSourceRange CSR = CharSourceRange::getCharRange(SR.getBegin(), SR.getEnd());
1357*06c3fb27SDimitry Andric   StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
1358*06c3fb27SDimitry Andric 
1359*06c3fb27SDimitry Andric   if (!Invalid)
1360*06c3fb27SDimitry Andric     return Text;
1361*06c3fb27SDimitry Andric   return std::nullopt;
1362*06c3fb27SDimitry Andric }
1363*06c3fb27SDimitry Andric 
1364*06c3fb27SDimitry Andric // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
1365*06c3fb27SDimitry Andric // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does not
1366*06c3fb27SDimitry Andric // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
1367*06c3fb27SDimitry Andric // :( ), `Qualifiers` of the pointee type is returned separately through the
1368*06c3fb27SDimitry Andric // output parameter `QualifiersToAppend`.
1369*06c3fb27SDimitry Andric static std::optional<std::string>
1370*06c3fb27SDimitry Andric getPointeeTypeText(const ParmVarDecl *VD, const SourceManager &SM,
1371*06c3fb27SDimitry Andric                    const LangOptions &LangOpts,
1372*06c3fb27SDimitry Andric                    std::optional<Qualifiers> *QualifiersToAppend) {
1373*06c3fb27SDimitry Andric   QualType Ty = VD->getType();
1374*06c3fb27SDimitry Andric   QualType PteTy;
1375*06c3fb27SDimitry Andric 
1376*06c3fb27SDimitry Andric   assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
1377*06c3fb27SDimitry Andric          "Expecting a VarDecl of type of pointer to object type");
1378*06c3fb27SDimitry Andric   PteTy = Ty->getPointeeType();
1379*06c3fb27SDimitry Andric   if (PteTy->hasUnnamedOrLocalType())
1380*06c3fb27SDimitry Andric     // If the pointee type is unnamed, we can't refer to it
1381*06c3fb27SDimitry Andric     return std::nullopt;
1382*06c3fb27SDimitry Andric 
1383*06c3fb27SDimitry Andric   TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc();
1384*06c3fb27SDimitry Andric   TypeLoc PteTyLoc = TyLoc.getUnqualifiedLoc().getNextTypeLoc();
1385*06c3fb27SDimitry Andric   SourceLocation VDNameStartLoc = VD->getLocation();
1386*06c3fb27SDimitry Andric 
1387*06c3fb27SDimitry Andric   if (!(VDNameStartLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
1388*06c3fb27SDimitry Andric     // We are expecting these locations to be valid. But in some cases, they are
1389*06c3fb27SDimitry Andric     // not all valid. It is a Clang bug to me and we are not responsible for
1390*06c3fb27SDimitry Andric     // fixing it.  So we will just give up for now when it happens.
1391*06c3fb27SDimitry Andric     return std::nullopt;
1392*06c3fb27SDimitry Andric   }
1393*06c3fb27SDimitry Andric 
1394*06c3fb27SDimitry Andric   // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
1395*06c3fb27SDimitry Andric   SourceLocation PteEndOfTokenLoc =
1396*06c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
1397*06c3fb27SDimitry Andric 
1398*06c3fb27SDimitry Andric   if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, VDNameStartLoc)) {
1399*06c3fb27SDimitry Andric     // We only deal with the cases where the source text of the pointee type
1400*06c3fb27SDimitry Andric     // appears on the left-hand side of the variable identifier completely,
1401*06c3fb27SDimitry Andric     // including the following forms:
1402*06c3fb27SDimitry Andric     // `T ident`,
1403*06c3fb27SDimitry Andric     // `T ident[]`, where `T` is any type.
1404*06c3fb27SDimitry Andric     // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
1405*06c3fb27SDimitry Andric     return std::nullopt;
1406*06c3fb27SDimitry Andric   }
1407*06c3fb27SDimitry Andric   if (PteTy.hasQualifiers()) {
1408*06c3fb27SDimitry Andric     // TypeLoc does not provide source ranges for qualifiers (it says it's
1409*06c3fb27SDimitry Andric     // intentional but seems fishy to me), so we cannot get the full text
1410*06c3fb27SDimitry Andric     // `PteTy` via source ranges.
1411*06c3fb27SDimitry Andric     *QualifiersToAppend = PteTy.getQualifiers();
1412*06c3fb27SDimitry Andric   }
1413*06c3fb27SDimitry Andric   return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
1414*06c3fb27SDimitry Andric       ->str();
1415*06c3fb27SDimitry Andric }
1416*06c3fb27SDimitry Andric 
1417*06c3fb27SDimitry Andric // Returns the text of the name (with qualifiers) of a `FunctionDecl`.
1418*06c3fb27SDimitry Andric static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
1419*06c3fb27SDimitry Andric                                                const SourceManager &SM,
1420*06c3fb27SDimitry Andric                                                const LangOptions &LangOpts) {
1421*06c3fb27SDimitry Andric   SourceLocation BeginLoc = FD->getQualifier()
1422*06c3fb27SDimitry Andric                                 ? FD->getQualifierLoc().getBeginLoc()
1423*06c3fb27SDimitry Andric                                 : FD->getNameInfo().getBeginLoc();
1424*06c3fb27SDimitry Andric   // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
1425*06c3fb27SDimitry Andric   // last token:
1426*06c3fb27SDimitry Andric   SourceLocation EndLoc = Lexer::getLocForEndOfToken(
1427*06c3fb27SDimitry Andric       FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
1428*06c3fb27SDimitry Andric   SourceRange NameRange{BeginLoc, EndLoc};
1429*06c3fb27SDimitry Andric 
1430*06c3fb27SDimitry Andric   return getRangeText(NameRange, SM, LangOpts);
1431*06c3fb27SDimitry Andric }
1432*06c3fb27SDimitry Andric 
1433*06c3fb27SDimitry Andric std::optional<FixItList>
1434*06c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget::getFixits(const Strategy &s) const {
1435*06c3fb27SDimitry Andric   const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
1436*06c3fb27SDimitry Andric 
1437*06c3fb27SDimitry Andric   if (VD && s.lookup(VD) == Strategy::Kind::Span) {
1438*06c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
1439*06c3fb27SDimitry Andric     // std::span can't represent elements before its begin()
1440*06c3fb27SDimitry Andric     if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
1441*06c3fb27SDimitry Andric       if (ConstVal->isNegative())
1442*06c3fb27SDimitry Andric         return std::nullopt;
1443*06c3fb27SDimitry Andric 
1444*06c3fb27SDimitry Andric     // note that the expr may (oddly) has multiple layers of parens
1445*06c3fb27SDimitry Andric     // example:
1446*06c3fb27SDimitry Andric     //   *((..(pointer + 123)..))
1447*06c3fb27SDimitry Andric     // goal:
1448*06c3fb27SDimitry Andric     //   pointer[123]
1449*06c3fb27SDimitry Andric     // Fix-It:
1450*06c3fb27SDimitry Andric     //   remove '*('
1451*06c3fb27SDimitry Andric     //   replace ' + ' with '['
1452*06c3fb27SDimitry Andric     //   replace ')' with ']'
1453*06c3fb27SDimitry Andric 
1454*06c3fb27SDimitry Andric     // example:
1455*06c3fb27SDimitry Andric     //   *((..(123 + pointer)..))
1456*06c3fb27SDimitry Andric     // goal:
1457*06c3fb27SDimitry Andric     //   123[pointer]
1458*06c3fb27SDimitry Andric     // Fix-It:
1459*06c3fb27SDimitry Andric     //   remove '*('
1460*06c3fb27SDimitry Andric     //   replace ' + ' with '['
1461*06c3fb27SDimitry Andric     //   replace ')' with ']'
1462*06c3fb27SDimitry Andric 
1463*06c3fb27SDimitry Andric     const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
1464*06c3fb27SDimitry Andric     const SourceManager &SM = Ctx.getSourceManager();
1465*06c3fb27SDimitry Andric     const LangOptions &LangOpts = Ctx.getLangOpts();
1466*06c3fb27SDimitry Andric     CharSourceRange StarWithTrailWhitespace =
1467*06c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
1468*06c3fb27SDimitry Andric                                              LHS->getBeginLoc());
1469*06c3fb27SDimitry Andric 
1470*06c3fb27SDimitry Andric     std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
1471*06c3fb27SDimitry Andric     if (!LHSLocation)
1472*06c3fb27SDimitry Andric       return std::nullopt;
1473*06c3fb27SDimitry Andric 
1474*06c3fb27SDimitry Andric     CharSourceRange PlusWithSurroundingWhitespace =
1475*06c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
1476*06c3fb27SDimitry Andric 
1477*06c3fb27SDimitry Andric     std::optional<SourceLocation> AddOpLocation =
1478*06c3fb27SDimitry Andric         getPastLoc(AddOp, SM, LangOpts);
1479*06c3fb27SDimitry Andric     std::optional<SourceLocation> DerefOpLocation =
1480*06c3fb27SDimitry Andric         getPastLoc(DerefOp, SM, LangOpts);
1481*06c3fb27SDimitry Andric 
1482*06c3fb27SDimitry Andric     if (!AddOpLocation || !DerefOpLocation)
1483*06c3fb27SDimitry Andric       return std::nullopt;
1484*06c3fb27SDimitry Andric 
1485*06c3fb27SDimitry Andric     CharSourceRange ClosingParenWithPrecWhitespace =
1486*06c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
1487*06c3fb27SDimitry Andric 
1488*06c3fb27SDimitry Andric     return FixItList{
1489*06c3fb27SDimitry Andric         {FixItHint::CreateRemoval(StarWithTrailWhitespace),
1490*06c3fb27SDimitry Andric          FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
1491*06c3fb27SDimitry Andric          FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
1492*06c3fb27SDimitry Andric   }
1493*06c3fb27SDimitry Andric   return std::nullopt; // something wrong or unsupported, give up
1494*06c3fb27SDimitry Andric }
1495*06c3fb27SDimitry Andric 
1496*06c3fb27SDimitry Andric std::optional<FixItList>
1497*06c3fb27SDimitry Andric PointerDereferenceGadget::getFixits(const Strategy &S) const {
1498*06c3fb27SDimitry Andric   const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
1499*06c3fb27SDimitry Andric   switch (S.lookup(VD)) {
1500*06c3fb27SDimitry Andric   case Strategy::Kind::Span: {
1501*06c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
1502*06c3fb27SDimitry Andric     SourceManager &SM = Ctx.getSourceManager();
1503*06c3fb27SDimitry Andric     // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
1504*06c3fb27SDimitry Andric     // Deletes the *operand
1505*06c3fb27SDimitry Andric     CharSourceRange derefRange = clang::CharSourceRange::getCharRange(
1506*06c3fb27SDimitry Andric         Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
1507*06c3fb27SDimitry Andric     // Inserts the [0]
1508*06c3fb27SDimitry Andric     std::optional<SourceLocation> EndOfOperand =
1509*06c3fb27SDimitry Andric         getEndCharLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts());
1510*06c3fb27SDimitry Andric     if (EndOfOperand) {
1511*06c3fb27SDimitry Andric       return FixItList{{FixItHint::CreateRemoval(derefRange),
1512*06c3fb27SDimitry Andric                         FixItHint::CreateInsertion(
1513*06c3fb27SDimitry Andric                             (*EndOfOperand).getLocWithOffset(1), "[0]")}};
1514*06c3fb27SDimitry Andric     }
1515*06c3fb27SDimitry Andric   }
1516*06c3fb27SDimitry Andric     [[fallthrough]];
1517*06c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
1518*06c3fb27SDimitry Andric   case Strategy::Kind::Array:
1519*06c3fb27SDimitry Andric   case Strategy::Kind::Vector:
1520*06c3fb27SDimitry Andric     llvm_unreachable("Strategy not implemented yet!");
1521*06c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
1522*06c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
1523*06c3fb27SDimitry Andric   }
1524*06c3fb27SDimitry Andric 
1525*06c3fb27SDimitry Andric   return std::nullopt;
1526*06c3fb27SDimitry Andric }
1527*06c3fb27SDimitry Andric 
1528*06c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form UPC(DRE) with
1529*06c3fb27SDimitry Andric // `DRE.data()`
1530*06c3fb27SDimitry Andric std::optional<FixItList> UPCStandalonePointerGadget::getFixits(const Strategy &S)
1531*06c3fb27SDimitry Andric       const {
1532*06c3fb27SDimitry Andric   const auto VD = cast<VarDecl>(Node->getDecl());
1533*06c3fb27SDimitry Andric   switch (S.lookup(VD)) {
1534*06c3fb27SDimitry Andric     case Strategy::Kind::Span: {
1535*06c3fb27SDimitry Andric       ASTContext &Ctx = VD->getASTContext();
1536*06c3fb27SDimitry Andric       SourceManager &SM = Ctx.getSourceManager();
1537*06c3fb27SDimitry Andric       // Inserts the .data() after the DRE
1538*06c3fb27SDimitry Andric       std::optional<SourceLocation> EndOfOperand =
1539*06c3fb27SDimitry Andric           getPastLoc(Node, SM, Ctx.getLangOpts());
1540*06c3fb27SDimitry Andric 
1541*06c3fb27SDimitry Andric       if (EndOfOperand)
1542*06c3fb27SDimitry Andric         return FixItList{{FixItHint::CreateInsertion(
1543*06c3fb27SDimitry Andric             *EndOfOperand, ".data()")}};
1544*06c3fb27SDimitry Andric     }
1545*06c3fb27SDimitry Andric       [[fallthrough]];
1546*06c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
1547*06c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
1548*06c3fb27SDimitry Andric     case Strategy::Kind::Array:
1549*06c3fb27SDimitry Andric     case Strategy::Kind::Vector:
1550*06c3fb27SDimitry Andric       llvm_unreachable("unsupported strategies for FixableGadgets");
1551*06c3fb27SDimitry Andric   }
1552*06c3fb27SDimitry Andric 
1553*06c3fb27SDimitry Andric   return std::nullopt;
1554*06c3fb27SDimitry Andric }
1555*06c3fb27SDimitry Andric 
1556*06c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form `&DRE[e]` with
1557*06c3fb27SDimitry Andric // `&DRE.data()[e]`:
1558*06c3fb27SDimitry Andric static std::optional<FixItList>
1559*06c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) {
1560*06c3fb27SDimitry Andric   const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
1561*06c3fb27SDimitry Andric   const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
1562*06c3fb27SDimitry Andric   // FIXME: this `getASTContext` call is costly, we should pass the
1563*06c3fb27SDimitry Andric   // ASTContext in:
1564*06c3fb27SDimitry Andric   const ASTContext &Ctx = DRE->getDecl()->getASTContext();
1565*06c3fb27SDimitry Andric   const Expr *Idx = ArraySub->getIdx();
1566*06c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
1567*06c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
1568*06c3fb27SDimitry Andric   std::stringstream SS;
1569*06c3fb27SDimitry Andric   bool IdxIsLitZero = false;
1570*06c3fb27SDimitry Andric 
1571*06c3fb27SDimitry Andric   if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
1572*06c3fb27SDimitry Andric     if ((*ICE).isZero())
1573*06c3fb27SDimitry Andric       IdxIsLitZero = true;
1574*06c3fb27SDimitry Andric   std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
1575*06c3fb27SDimitry Andric   if (!DreString)
1576*06c3fb27SDimitry Andric     return std::nullopt;
1577*06c3fb27SDimitry Andric 
1578*06c3fb27SDimitry Andric   if (IdxIsLitZero) {
1579*06c3fb27SDimitry Andric     // If the index is literal zero, we produce the most concise fix-it:
1580*06c3fb27SDimitry Andric     SS << (*DreString).str() << ".data()";
1581*06c3fb27SDimitry Andric   } else {
1582*06c3fb27SDimitry Andric     std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
1583*06c3fb27SDimitry Andric     if (!IndexString)
1584*06c3fb27SDimitry Andric       return std::nullopt;
1585*06c3fb27SDimitry Andric 
1586*06c3fb27SDimitry Andric     SS << "&" << (*DreString).str() << ".data()"
1587*06c3fb27SDimitry Andric        << "[" << (*IndexString).str() << "]";
1588*06c3fb27SDimitry Andric   }
1589*06c3fb27SDimitry Andric   return FixItList{
1590*06c3fb27SDimitry Andric       FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
1591*06c3fb27SDimitry Andric }
1592*06c3fb27SDimitry Andric 
1593*06c3fb27SDimitry Andric 
1594*06c3fb27SDimitry Andric std::optional<FixItList> UPCPreIncrementGadget::getFixits(const Strategy &S) const {
1595*06c3fb27SDimitry Andric   DeclUseList DREs = getClaimedVarUseSites();
1596*06c3fb27SDimitry Andric 
1597*06c3fb27SDimitry Andric   if (DREs.size() != 1)
1598*06c3fb27SDimitry Andric     return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
1599*06c3fb27SDimitry Andric                          // give up
1600*06c3fb27SDimitry Andric   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
1601*06c3fb27SDimitry Andric     if (S.lookup(VD) == Strategy::Kind::Span) {
1602*06c3fb27SDimitry Andric       FixItList Fixes;
1603*06c3fb27SDimitry Andric       std::stringstream SS;
1604*06c3fb27SDimitry Andric       const Stmt *PreIncNode = getBaseStmt();
1605*06c3fb27SDimitry Andric       StringRef varName = VD->getName();
1606*06c3fb27SDimitry Andric       const ASTContext &Ctx = VD->getASTContext();
1607*06c3fb27SDimitry Andric 
1608*06c3fb27SDimitry Andric       // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
1609*06c3fb27SDimitry Andric       SS << "(" << varName.data() << " = " << varName.data()
1610*06c3fb27SDimitry Andric          << ".subspan(1)).data()";
1611*06c3fb27SDimitry Andric       std::optional<SourceLocation> PreIncLocation =
1612*06c3fb27SDimitry Andric           getEndCharLoc(PreIncNode, Ctx.getSourceManager(), Ctx.getLangOpts());
1613*06c3fb27SDimitry Andric       if (!PreIncLocation)
1614*06c3fb27SDimitry Andric         return std::nullopt;
1615*06c3fb27SDimitry Andric 
1616*06c3fb27SDimitry Andric       Fixes.push_back(FixItHint::CreateReplacement(
1617*06c3fb27SDimitry Andric           SourceRange(PreIncNode->getBeginLoc(), *PreIncLocation), SS.str()));
1618*06c3fb27SDimitry Andric       return Fixes;
1619*06c3fb27SDimitry Andric     }
1620*06c3fb27SDimitry Andric   }
1621*06c3fb27SDimitry Andric   return std::nullopt; // Not in the cases that we can handle for now, give up.
1622*06c3fb27SDimitry Andric }
1623*06c3fb27SDimitry Andric 
1624*06c3fb27SDimitry Andric 
1625*06c3fb27SDimitry Andric // For a non-null initializer `Init` of `T *` type, this function returns
1626*06c3fb27SDimitry Andric // `FixItHint`s producing a list initializer `{Init,  S}` as a part of a fix-it
1627*06c3fb27SDimitry Andric // to output stream.
1628*06c3fb27SDimitry Andric // In many cases, this function cannot figure out the actual extent `S`.  It
1629*06c3fb27SDimitry Andric // then will use a place holder to replace `S` to ask users to fill `S` in.  The
1630*06c3fb27SDimitry Andric // initializer shall be used to initialize a variable of type `std::span<T>`.
1631*06c3fb27SDimitry Andric //
1632*06c3fb27SDimitry Andric // FIXME: Support multi-level pointers
1633*06c3fb27SDimitry Andric //
1634*06c3fb27SDimitry Andric // Parameters:
1635*06c3fb27SDimitry Andric //   `Init` a pointer to the initializer expression
1636*06c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
1637*06c3fb27SDimitry Andric static FixItList
1638*06c3fb27SDimitry Andric populateInitializerFixItWithSpan(const Expr *Init, ASTContext &Ctx,
1639*06c3fb27SDimitry Andric                                  const StringRef UserFillPlaceHolder) {
1640*06c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
1641*06c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
1642*06c3fb27SDimitry Andric 
1643*06c3fb27SDimitry Andric   // If `Init` has a constant value that is (or equivalent to) a
1644*06c3fb27SDimitry Andric   // NULL pointer, we use the default constructor to initialize the span
1645*06c3fb27SDimitry Andric   // object, i.e., a `std:span` variable declaration with no initializer.
1646*06c3fb27SDimitry Andric   // So the fix-it is just to remove the initializer.
1647*06c3fb27SDimitry Andric   if (Init->isNullPointerConstant(Ctx,
1648*06c3fb27SDimitry Andric           // FIXME: Why does this function not ask for `const ASTContext
1649*06c3fb27SDimitry Andric           // &`? It should. Maybe worth an NFC patch later.
1650*06c3fb27SDimitry Andric           Expr::NullPointerConstantValueDependence::
1651*06c3fb27SDimitry Andric               NPC_ValueDependentIsNotNull)) {
1652*06c3fb27SDimitry Andric     std::optional<SourceLocation> InitLocation =
1653*06c3fb27SDimitry Andric         getEndCharLoc(Init, SM, LangOpts);
1654*06c3fb27SDimitry Andric     if (!InitLocation)
1655*06c3fb27SDimitry Andric       return {};
1656*06c3fb27SDimitry Andric 
1657*06c3fb27SDimitry Andric     SourceRange SR(Init->getBeginLoc(), *InitLocation);
1658*06c3fb27SDimitry Andric 
1659*06c3fb27SDimitry Andric     return {FixItHint::CreateRemoval(SR)};
1660*06c3fb27SDimitry Andric   }
1661*06c3fb27SDimitry Andric 
1662*06c3fb27SDimitry Andric   FixItList FixIts{};
1663*06c3fb27SDimitry Andric   std::string ExtentText = UserFillPlaceHolder.data();
1664*06c3fb27SDimitry Andric   StringRef One = "1";
1665*06c3fb27SDimitry Andric 
1666*06c3fb27SDimitry Andric   // Insert `{` before `Init`:
1667*06c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
1668*06c3fb27SDimitry Andric   // Try to get the data extent. Break into different cases:
1669*06c3fb27SDimitry Andric   if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
1670*06c3fb27SDimitry Andric     // In cases `Init` is `new T[n]` and there is no explicit cast over
1671*06c3fb27SDimitry Andric     // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
1672*06c3fb27SDimitry Andric     // of `T`. So the extent is `n` unless `n` has side effects.  Similar but
1673*06c3fb27SDimitry Andric     // simpler for the case where `Init` is `new T`.
1674*06c3fb27SDimitry Andric     if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
1675*06c3fb27SDimitry Andric       if (!Ext->HasSideEffects(Ctx)) {
1676*06c3fb27SDimitry Andric         std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
1677*06c3fb27SDimitry Andric         if (!ExtentString)
1678*06c3fb27SDimitry Andric           return {};
1679*06c3fb27SDimitry Andric         ExtentText = *ExtentString;
1680*06c3fb27SDimitry Andric       }
1681*06c3fb27SDimitry Andric     } else if (!CxxNew->isArray())
1682*06c3fb27SDimitry Andric       // Although the initializer is not allocating a buffer, the pointer
1683*06c3fb27SDimitry Andric       // variable could still be used in buffer access operations.
1684*06c3fb27SDimitry Andric       ExtentText = One;
1685*06c3fb27SDimitry Andric   } else if (const auto *CArrTy = Ctx.getAsConstantArrayType(
1686*06c3fb27SDimitry Andric                  Init->IgnoreImpCasts()->getType())) {
1687*06c3fb27SDimitry Andric     // In cases `Init` is of an array type after stripping off implicit casts,
1688*06c3fb27SDimitry Andric     // the extent is the array size.  Note that if the array size is not a
1689*06c3fb27SDimitry Andric     // constant, we cannot use it as the extent.
1690*06c3fb27SDimitry Andric     ExtentText = getAPIntText(CArrTy->getSize());
1691*06c3fb27SDimitry Andric   } else {
1692*06c3fb27SDimitry Andric     // In cases `Init` is of the form `&Var` after stripping of implicit
1693*06c3fb27SDimitry Andric     // casts, where `&` is the built-in operator, the extent is 1.
1694*06c3fb27SDimitry Andric     if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
1695*06c3fb27SDimitry Andric       if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
1696*06c3fb27SDimitry Andric           isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
1697*06c3fb27SDimitry Andric         ExtentText = One;
1698*06c3fb27SDimitry Andric     // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
1699*06c3fb27SDimitry Andric     // and explicit casting, etc. etc.
1700*06c3fb27SDimitry Andric   }
1701*06c3fb27SDimitry Andric 
1702*06c3fb27SDimitry Andric   SmallString<32> StrBuffer{};
1703*06c3fb27SDimitry Andric   std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
1704*06c3fb27SDimitry Andric 
1705*06c3fb27SDimitry Andric   if (!LocPassInit)
1706*06c3fb27SDimitry Andric     return {};
1707*06c3fb27SDimitry Andric 
1708*06c3fb27SDimitry Andric   StrBuffer.append(", ");
1709*06c3fb27SDimitry Andric   StrBuffer.append(ExtentText);
1710*06c3fb27SDimitry Andric   StrBuffer.append("}");
1711*06c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
1712*06c3fb27SDimitry Andric   return FixIts;
1713*06c3fb27SDimitry Andric }
1714*06c3fb27SDimitry Andric 
1715*06c3fb27SDimitry Andric // For a `VarDecl` of the form `T  * var (= Init)?`, this
1716*06c3fb27SDimitry Andric // function generates a fix-it for the declaration, which re-declares `var` to
1717*06c3fb27SDimitry Andric // be of `span<T>` type and transforms the initializer, if present, to a span
1718*06c3fb27SDimitry Andric // constructor---`span<T> var {Init, Extent}`, where `Extent` may need the user
1719*06c3fb27SDimitry Andric // to fill in.
1720*06c3fb27SDimitry Andric //
1721*06c3fb27SDimitry Andric // FIXME: support Multi-level pointers
1722*06c3fb27SDimitry Andric //
1723*06c3fb27SDimitry Andric // Parameters:
1724*06c3fb27SDimitry Andric //   `D` a pointer the variable declaration node
1725*06c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
1726*06c3fb27SDimitry Andric // Returns:
1727*06c3fb27SDimitry Andric //    the generated fix-it
1728*06c3fb27SDimitry Andric static FixItList fixVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
1729*06c3fb27SDimitry Andric                                     const StringRef UserFillPlaceHolder) {
1730*06c3fb27SDimitry Andric   const QualType &SpanEltT = D->getType()->getPointeeType();
1731*06c3fb27SDimitry Andric   assert(!SpanEltT.isNull() && "Trying to fix a non-pointer type variable!");
1732*06c3fb27SDimitry Andric 
1733*06c3fb27SDimitry Andric   FixItList FixIts{};
1734*06c3fb27SDimitry Andric   std::optional<SourceLocation>
1735*06c3fb27SDimitry Andric       ReplacementLastLoc; // the inclusive end location of the replacement
1736*06c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
1737*06c3fb27SDimitry Andric 
1738*06c3fb27SDimitry Andric   if (const Expr *Init = D->getInit()) {
1739*06c3fb27SDimitry Andric     FixItList InitFixIts =
1740*06c3fb27SDimitry Andric         populateInitializerFixItWithSpan(Init, Ctx, UserFillPlaceHolder);
1741*06c3fb27SDimitry Andric 
1742*06c3fb27SDimitry Andric     if (InitFixIts.empty())
1743*06c3fb27SDimitry Andric       return {};
1744*06c3fb27SDimitry Andric 
1745*06c3fb27SDimitry Andric     // The loc right before the initializer:
1746*06c3fb27SDimitry Andric     ReplacementLastLoc = Init->getBeginLoc().getLocWithOffset(-1);
1747*06c3fb27SDimitry Andric     FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts.begin()),
1748*06c3fb27SDimitry Andric                   std::make_move_iterator(InitFixIts.end()));
1749*06c3fb27SDimitry Andric   } else
1750*06c3fb27SDimitry Andric     ReplacementLastLoc = getEndCharLoc(D, SM, Ctx.getLangOpts());
1751*06c3fb27SDimitry Andric 
1752*06c3fb27SDimitry Andric   // Producing fix-it for variable declaration---make `D` to be of span type:
1753*06c3fb27SDimitry Andric   SmallString<32> Replacement;
1754*06c3fb27SDimitry Andric   raw_svector_ostream OS(Replacement);
1755*06c3fb27SDimitry Andric 
1756*06c3fb27SDimitry Andric   OS << "std::span<" << SpanEltT.getAsString() << "> " << D->getName();
1757*06c3fb27SDimitry Andric 
1758*06c3fb27SDimitry Andric   if (!ReplacementLastLoc)
1759*06c3fb27SDimitry Andric     return {};
1760*06c3fb27SDimitry Andric 
1761*06c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateReplacement(
1762*06c3fb27SDimitry Andric       SourceRange{D->getBeginLoc(), *ReplacementLastLoc}, OS.str()));
1763*06c3fb27SDimitry Andric   return FixIts;
1764*06c3fb27SDimitry Andric }
1765*06c3fb27SDimitry Andric 
1766*06c3fb27SDimitry Andric static bool hasConflictingOverload(const FunctionDecl *FD) {
1767*06c3fb27SDimitry Andric   return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
1768*06c3fb27SDimitry Andric }
1769*06c3fb27SDimitry Andric 
1770*06c3fb27SDimitry Andric // For a `FunDecl`, one of whose `ParmVarDecl`s is being changed to have a new
1771*06c3fb27SDimitry Andric // type, this function produces fix-its to make the change self-contained.  Let
1772*06c3fb27SDimitry Andric // 'F' be the entity defined by the original `FunDecl` and "NewF" be the entity
1773*06c3fb27SDimitry Andric // defined by the `FunDecl` after the change to the parameter.  Fix-its produced
1774*06c3fb27SDimitry Andric // by this function are
1775*06c3fb27SDimitry Andric //   1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
1776*06c3fb27SDimitry Andric //   of 'F';
1777*06c3fb27SDimitry Andric //   2. Create a declaration of "NewF" next to each declaration of `F`;
1778*06c3fb27SDimitry Andric //   3. Create a definition of "F" (as its' original definition is now belongs
1779*06c3fb27SDimitry Andric //      to "NewF") next to its original definition.  The body of the creating
1780*06c3fb27SDimitry Andric //      definition calls to "NewF".
1781*06c3fb27SDimitry Andric //
1782*06c3fb27SDimitry Andric // Example:
1783*06c3fb27SDimitry Andric //
1784*06c3fb27SDimitry Andric // void f(int *p);  // original declaration
1785*06c3fb27SDimitry Andric // void f(int *p) { // original definition
1786*06c3fb27SDimitry Andric //    p[5];
1787*06c3fb27SDimitry Andric // }
1788*06c3fb27SDimitry Andric //
1789*06c3fb27SDimitry Andric // To change the parameter `p` to be of `std::span<int>` type, we
1790*06c3fb27SDimitry Andric // also add overloads:
1791*06c3fb27SDimitry Andric //
1792*06c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
1793*06c3fb27SDimitry Andric // void f(std::span<int> p);                      // added overload decl
1794*06c3fb27SDimitry Andric // void f(std::span<int> p) {     // original def where param is changed
1795*06c3fb27SDimitry Andric //    p[5];
1796*06c3fb27SDimitry Andric // }
1797*06c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p) {  // added def
1798*06c3fb27SDimitry Andric //   return f(std::span(p, <# size #>));
1799*06c3fb27SDimitry Andric // }
1800*06c3fb27SDimitry Andric //
1801*06c3fb27SDimitry Andric // The actual fix-its may contain more details, e.g., the attribute may be guard
1802*06c3fb27SDimitry Andric // by a macro
1803*06c3fb27SDimitry Andric //   #if __has_cpp_attribute(clang::unsafe_buffer_usage)
1804*06c3fb27SDimitry Andric //   [[clang::unsafe_buffer_usage]]
1805*06c3fb27SDimitry Andric //   #endif
1806*06c3fb27SDimitry Andric //
1807*06c3fb27SDimitry Andric // `NewTypeText` is the string representation of the new type, to which the
1808*06c3fb27SDimitry Andric // parameter indexed by `ParmIdx` is being changed.
1809*06c3fb27SDimitry Andric static std::optional<FixItList>
1810*06c3fb27SDimitry Andric createOverloadsForFixedParams(unsigned ParmIdx, StringRef NewTyText,
1811*06c3fb27SDimitry Andric                               const FunctionDecl *FD, const ASTContext &Ctx,
1812*06c3fb27SDimitry Andric                               UnsafeBufferUsageHandler &Handler) {
1813*06c3fb27SDimitry Andric   // FIXME: need to make this conflict checking better:
1814*06c3fb27SDimitry Andric   if (hasConflictingOverload(FD))
1815*06c3fb27SDimitry Andric     return std::nullopt;
1816*06c3fb27SDimitry Andric 
1817*06c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
1818*06c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
1819*06c3fb27SDimitry Andric   // FIXME Respect indentation of the original code.
1820*06c3fb27SDimitry Andric 
1821*06c3fb27SDimitry Andric   // A lambda that creates the text representation of a function declaration
1822*06c3fb27SDimitry Andric   // with the new type signature:
1823*06c3fb27SDimitry Andric   const auto NewOverloadSignatureCreator =
1824*06c3fb27SDimitry Andric       [&SM, &LangOpts](const FunctionDecl *FD, unsigned ParmIdx,
1825*06c3fb27SDimitry Andric                        StringRef NewTypeText) -> std::optional<std::string> {
1826*06c3fb27SDimitry Andric     std::stringstream SS;
1827*06c3fb27SDimitry Andric 
1828*06c3fb27SDimitry Andric     SS << ";";
1829*06c3fb27SDimitry Andric     SS << getEndOfLine().str();
1830*06c3fb27SDimitry Andric     // Append: ret-type func-name "("
1831*06c3fb27SDimitry Andric     if (auto Prefix = getRangeText(
1832*06c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
1833*06c3fb27SDimitry Andric             SM, LangOpts))
1834*06c3fb27SDimitry Andric       SS << Prefix->str();
1835*06c3fb27SDimitry Andric     else
1836*06c3fb27SDimitry Andric       return std::nullopt; // give up
1837*06c3fb27SDimitry Andric     // Append: parameter-type-list
1838*06c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
1839*06c3fb27SDimitry Andric 
1840*06c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
1841*06c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
1842*06c3fb27SDimitry Andric 
1843*06c3fb27SDimitry Andric       if (Parm->isImplicit())
1844*06c3fb27SDimitry Andric         continue;
1845*06c3fb27SDimitry Andric       if (i == ParmIdx) {
1846*06c3fb27SDimitry Andric         SS << NewTypeText.str();
1847*06c3fb27SDimitry Andric         // print parameter name if provided:
1848*06c3fb27SDimitry Andric         if (IdentifierInfo * II = Parm->getIdentifier())
1849*06c3fb27SDimitry Andric           SS << " " << II->getName().str();
1850*06c3fb27SDimitry Andric       } else if (auto ParmTypeText =
1851*06c3fb27SDimitry Andric                      getRangeText(Parm->getSourceRange(), SM, LangOpts)) {
1852*06c3fb27SDimitry Andric         // print the whole `Parm` without modification:
1853*06c3fb27SDimitry Andric         SS << ParmTypeText->str();
1854*06c3fb27SDimitry Andric       } else
1855*06c3fb27SDimitry Andric         return std::nullopt; // something wrong, give up
1856*06c3fb27SDimitry Andric       if (i != NumParms - 1)
1857*06c3fb27SDimitry Andric         SS << ", ";
1858*06c3fb27SDimitry Andric     }
1859*06c3fb27SDimitry Andric     SS << ")";
1860*06c3fb27SDimitry Andric     return SS.str();
1861*06c3fb27SDimitry Andric   };
1862*06c3fb27SDimitry Andric 
1863*06c3fb27SDimitry Andric   // A lambda that creates the text representation of a function definition with
1864*06c3fb27SDimitry Andric   // the original signature:
1865*06c3fb27SDimitry Andric   const auto OldOverloadDefCreator =
1866*06c3fb27SDimitry Andric       [&SM, &Handler,
1867*06c3fb27SDimitry Andric        &LangOpts](const FunctionDecl *FD, unsigned ParmIdx,
1868*06c3fb27SDimitry Andric                   StringRef NewTypeText) -> std::optional<std::string> {
1869*06c3fb27SDimitry Andric     std::stringstream SS;
1870*06c3fb27SDimitry Andric 
1871*06c3fb27SDimitry Andric     SS << getEndOfLine().str();
1872*06c3fb27SDimitry Andric     // Append: attr-name ret-type func-name "(" param-list ")" "{"
1873*06c3fb27SDimitry Andric     if (auto FDPrefix = getRangeText(
1874*06c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
1875*06c3fb27SDimitry Andric             LangOpts))
1876*06c3fb27SDimitry Andric       SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
1877*06c3fb27SDimitry Andric          << FDPrefix->str() << "{";
1878*06c3fb27SDimitry Andric     else
1879*06c3fb27SDimitry Andric       return std::nullopt;
1880*06c3fb27SDimitry Andric     // Append: "return" func-name "("
1881*06c3fb27SDimitry Andric     if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
1882*06c3fb27SDimitry Andric       SS << "return " << FunQualName->str() << "(";
1883*06c3fb27SDimitry Andric     else
1884*06c3fb27SDimitry Andric       return std::nullopt;
1885*06c3fb27SDimitry Andric 
1886*06c3fb27SDimitry Andric     // Append: arg-list
1887*06c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
1888*06c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
1889*06c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
1890*06c3fb27SDimitry Andric 
1891*06c3fb27SDimitry Andric       if (Parm->isImplicit())
1892*06c3fb27SDimitry Andric         continue;
1893*06c3fb27SDimitry Andric       // FIXME: If a parameter has no name, it is unused in the
1894*06c3fb27SDimitry Andric       // definition. So we could just leave it as it is.
1895*06c3fb27SDimitry Andric       if (!Parm->getIdentifier())
1896*06c3fb27SDimitry Andric         // If a parameter of a function definition has no name:
1897*06c3fb27SDimitry Andric         return std::nullopt;
1898*06c3fb27SDimitry Andric       if (i == ParmIdx)
1899*06c3fb27SDimitry Andric         // This is our spanified paramter!
1900*06c3fb27SDimitry Andric         SS << NewTypeText.str() << "(" << Parm->getIdentifier()->getName().str() << ", "
1901*06c3fb27SDimitry Andric            << getUserFillPlaceHolder("size") << ")";
1902*06c3fb27SDimitry Andric       else
1903*06c3fb27SDimitry Andric         SS << Parm->getIdentifier()->getName().str();
1904*06c3fb27SDimitry Andric       if (i != NumParms - 1)
1905*06c3fb27SDimitry Andric         SS << ", ";
1906*06c3fb27SDimitry Andric     }
1907*06c3fb27SDimitry Andric     // finish call and the body
1908*06c3fb27SDimitry Andric     SS << ");}" << getEndOfLine().str();
1909*06c3fb27SDimitry Andric     // FIXME: 80-char line formatting?
1910*06c3fb27SDimitry Andric     return SS.str();
1911*06c3fb27SDimitry Andric   };
1912*06c3fb27SDimitry Andric 
1913*06c3fb27SDimitry Andric   FixItList FixIts{};
1914*06c3fb27SDimitry Andric   for (FunctionDecl *FReDecl : FD->redecls()) {
1915*06c3fb27SDimitry Andric     std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
1916*06c3fb27SDimitry Andric 
1917*06c3fb27SDimitry Andric     if (!Loc)
1918*06c3fb27SDimitry Andric       return {};
1919*06c3fb27SDimitry Andric     if (FReDecl->isThisDeclarationADefinition()) {
1920*06c3fb27SDimitry Andric       assert(FReDecl == FD && "inconsistent function definition");
1921*06c3fb27SDimitry Andric       // Inserts a definition with the old signature to the end of
1922*06c3fb27SDimitry Andric       // `FReDecl`:
1923*06c3fb27SDimitry Andric       if (auto OldOverloadDef =
1924*06c3fb27SDimitry Andric               OldOverloadDefCreator(FReDecl, ParmIdx, NewTyText))
1925*06c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
1926*06c3fb27SDimitry Andric       else
1927*06c3fb27SDimitry Andric         return {}; // give up
1928*06c3fb27SDimitry Andric     } else {
1929*06c3fb27SDimitry Andric       // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
1930*06c3fb27SDimitry Andric       if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
1931*06c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(
1932*06c3fb27SDimitry Andric             FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
1933*06c3fb27SDimitry Andric                                         FReDecl->getBeginLoc(), " ")));
1934*06c3fb27SDimitry Andric       }
1935*06c3fb27SDimitry Andric       // Inserts a declaration with the new signature to the end of `FReDecl`:
1936*06c3fb27SDimitry Andric       if (auto NewOverloadDecl =
1937*06c3fb27SDimitry Andric               NewOverloadSignatureCreator(FReDecl, ParmIdx, NewTyText))
1938*06c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
1939*06c3fb27SDimitry Andric       else
1940*06c3fb27SDimitry Andric         return {};
1941*06c3fb27SDimitry Andric     }
1942*06c3fb27SDimitry Andric   }
1943*06c3fb27SDimitry Andric   return FixIts;
1944*06c3fb27SDimitry Andric }
1945*06c3fb27SDimitry Andric 
1946*06c3fb27SDimitry Andric // To fix a `ParmVarDecl` to be of `std::span` type.  In addition, creating a
1947*06c3fb27SDimitry Andric // new overload of the function so that the change is self-contained (see
1948*06c3fb27SDimitry Andric // `createOverloadsForFixedParams`).
1949*06c3fb27SDimitry Andric static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
1950*06c3fb27SDimitry Andric                                   UnsafeBufferUsageHandler &Handler) {
1951*06c3fb27SDimitry Andric   if (PVD->hasDefaultArg())
1952*06c3fb27SDimitry Andric     // FIXME: generate fix-its for default values:
1953*06c3fb27SDimitry Andric     return {};
1954*06c3fb27SDimitry Andric   assert(PVD->getType()->isPointerType());
1955*06c3fb27SDimitry Andric   auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
1956*06c3fb27SDimitry Andric 
1957*06c3fb27SDimitry Andric   if (!FD)
1958*06c3fb27SDimitry Andric     return {};
1959*06c3fb27SDimitry Andric 
1960*06c3fb27SDimitry Andric   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
1961*06c3fb27SDimitry Andric   std::optional<std::string> PteTyText = getPointeeTypeText(
1962*06c3fb27SDimitry Andric       PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
1963*06c3fb27SDimitry Andric 
1964*06c3fb27SDimitry Andric   if (!PteTyText)
1965*06c3fb27SDimitry Andric     return {};
1966*06c3fb27SDimitry Andric 
1967*06c3fb27SDimitry Andric   std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
1968*06c3fb27SDimitry Andric 
1969*06c3fb27SDimitry Andric   if (!PVDNameText)
1970*06c3fb27SDimitry Andric     return {};
1971*06c3fb27SDimitry Andric 
1972*06c3fb27SDimitry Andric   std::string SpanOpen = "std::span<";
1973*06c3fb27SDimitry Andric   std::string SpanClose = ">";
1974*06c3fb27SDimitry Andric   std::string SpanTyText;
1975*06c3fb27SDimitry Andric   std::stringstream SS;
1976*06c3fb27SDimitry Andric 
1977*06c3fb27SDimitry Andric   SS << SpanOpen << *PteTyText;
1978*06c3fb27SDimitry Andric   // Append qualifiers to span element type:
1979*06c3fb27SDimitry Andric   if (PteTyQualifiers)
1980*06c3fb27SDimitry Andric     SS << " " << PteTyQualifiers->getAsString();
1981*06c3fb27SDimitry Andric   SS << SpanClose;
1982*06c3fb27SDimitry Andric   // Save the Span type text:
1983*06c3fb27SDimitry Andric   SpanTyText = SS.str();
1984*06c3fb27SDimitry Andric   // Append qualifiers to the type of the parameter:
1985*06c3fb27SDimitry Andric   if (PVD->getType().hasQualifiers())
1986*06c3fb27SDimitry Andric     SS << " " << PVD->getType().getQualifiers().getAsString();
1987*06c3fb27SDimitry Andric   // Append parameter's name:
1988*06c3fb27SDimitry Andric   SS << " " << PVDNameText->str();
1989*06c3fb27SDimitry Andric 
1990*06c3fb27SDimitry Andric   FixItList Fixes;
1991*06c3fb27SDimitry Andric   unsigned ParmIdx = 0;
1992*06c3fb27SDimitry Andric 
1993*06c3fb27SDimitry Andric   Fixes.push_back(
1994*06c3fb27SDimitry Andric       FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str()));
1995*06c3fb27SDimitry Andric   for (auto *ParmIter : FD->parameters()) {
1996*06c3fb27SDimitry Andric     if (PVD == ParmIter)
1997*06c3fb27SDimitry Andric       break;
1998*06c3fb27SDimitry Andric     ParmIdx++;
1999*06c3fb27SDimitry Andric   }
2000*06c3fb27SDimitry Andric   if (ParmIdx < FD->getNumParams())
2001*06c3fb27SDimitry Andric     if (auto OverloadFix = createOverloadsForFixedParams(ParmIdx, SpanTyText,
2002*06c3fb27SDimitry Andric                                                          FD, Ctx, Handler)) {
2003*06c3fb27SDimitry Andric       Fixes.append(*OverloadFix);
2004*06c3fb27SDimitry Andric       return Fixes;
2005*06c3fb27SDimitry Andric     }
2006*06c3fb27SDimitry Andric   return {};
2007*06c3fb27SDimitry Andric }
2008*06c3fb27SDimitry Andric 
2009*06c3fb27SDimitry Andric static FixItList fixVariableWithSpan(const VarDecl *VD,
2010*06c3fb27SDimitry Andric                                      const DeclUseTracker &Tracker,
2011*06c3fb27SDimitry Andric                                      ASTContext &Ctx,
2012*06c3fb27SDimitry Andric                                      UnsafeBufferUsageHandler &Handler) {
2013*06c3fb27SDimitry Andric   const DeclStmt *DS = Tracker.lookupDecl(VD);
2014*06c3fb27SDimitry Andric   assert(DS && "Fixing non-local variables not implemented yet!");
2015*06c3fb27SDimitry Andric   if (!DS->isSingleDecl()) {
2016*06c3fb27SDimitry Andric     // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2017*06c3fb27SDimitry Andric     return {};
2018*06c3fb27SDimitry Andric   }
2019*06c3fb27SDimitry Andric   // Currently DS is an unused variable but we'll need it when
2020*06c3fb27SDimitry Andric   // non-single decls are implemented, where the pointee type name
2021*06c3fb27SDimitry Andric   // and the '*' are spread around the place.
2022*06c3fb27SDimitry Andric   (void)DS;
2023*06c3fb27SDimitry Andric 
2024*06c3fb27SDimitry Andric   // FIXME: handle cases where DS has multiple declarations
2025*06c3fb27SDimitry Andric   return fixVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder());
2026*06c3fb27SDimitry Andric }
2027*06c3fb27SDimitry Andric 
2028*06c3fb27SDimitry Andric // TODO: we should be consistent to use `std::nullopt` to represent no-fix due
2029*06c3fb27SDimitry Andric // to any unexpected problem.
2030*06c3fb27SDimitry Andric static FixItList
2031*06c3fb27SDimitry Andric fixVariable(const VarDecl *VD, Strategy::Kind K,
2032*06c3fb27SDimitry Andric             /* The function decl under analysis */ const Decl *D,
2033*06c3fb27SDimitry Andric             const DeclUseTracker &Tracker, ASTContext &Ctx,
2034*06c3fb27SDimitry Andric             UnsafeBufferUsageHandler &Handler) {
2035*06c3fb27SDimitry Andric   if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
2036*06c3fb27SDimitry Andric     auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
2037*06c3fb27SDimitry Andric     if (!FD || FD != D)
2038*06c3fb27SDimitry Andric       // `FD != D` means that `PVD` belongs to a function that is not being
2039*06c3fb27SDimitry Andric       // analyzed currently.  Thus `FD` may not be complete.
2040*06c3fb27SDimitry Andric       return {};
2041*06c3fb27SDimitry Andric 
2042*06c3fb27SDimitry Andric     // TODO If function has a try block we can't change params unless we check
2043*06c3fb27SDimitry Andric     // also its catch block for their use.
2044*06c3fb27SDimitry Andric     // FIXME We might support static class methods, some select methods,
2045*06c3fb27SDimitry Andric     // operators and possibly lamdas.
2046*06c3fb27SDimitry Andric     if (FD->isMain() || FD->isConstexpr() ||
2047*06c3fb27SDimitry Andric         FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
2048*06c3fb27SDimitry Andric         FD->isVariadic() ||
2049*06c3fb27SDimitry Andric         // also covers call-operator of lamdas
2050*06c3fb27SDimitry Andric         isa<CXXMethodDecl>(FD) ||
2051*06c3fb27SDimitry Andric         // skip when the function body is a try-block
2052*06c3fb27SDimitry Andric         (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
2053*06c3fb27SDimitry Andric         FD->isOverloadedOperator())
2054*06c3fb27SDimitry Andric       return {}; // TODO test all these cases
2055*06c3fb27SDimitry Andric   }
2056*06c3fb27SDimitry Andric 
2057*06c3fb27SDimitry Andric   switch (K) {
2058*06c3fb27SDimitry Andric   case Strategy::Kind::Span: {
2059*06c3fb27SDimitry Andric     if (VD->getType()->isPointerType()) {
2060*06c3fb27SDimitry Andric       if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
2061*06c3fb27SDimitry Andric         return fixParamWithSpan(PVD, Ctx, Handler);
2062*06c3fb27SDimitry Andric 
2063*06c3fb27SDimitry Andric       if (VD->isLocalVarDecl())
2064*06c3fb27SDimitry Andric         return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
2065*06c3fb27SDimitry Andric     }
2066*06c3fb27SDimitry Andric     return {};
2067*06c3fb27SDimitry Andric   }
2068*06c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
2069*06c3fb27SDimitry Andric   case Strategy::Kind::Array:
2070*06c3fb27SDimitry Andric   case Strategy::Kind::Vector:
2071*06c3fb27SDimitry Andric     llvm_unreachable("Strategy not implemented yet!");
2072*06c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
2073*06c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
2074*06c3fb27SDimitry Andric   }
2075*06c3fb27SDimitry Andric   llvm_unreachable("Unknown strategy!");
2076*06c3fb27SDimitry Andric }
2077*06c3fb27SDimitry Andric 
2078*06c3fb27SDimitry Andric // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
2079*06c3fb27SDimitry Andric // `RemoveRange` of 'h' overlaps with a macro use.
2080*06c3fb27SDimitry Andric static bool overlapWithMacro(const FixItList &FixIts) {
2081*06c3fb27SDimitry Andric   // FIXME: For now we only check if the range (or the first token) is (part of)
2082*06c3fb27SDimitry Andric   // a macro expansion.  Ideally, we want to check for all tokens in the range.
2083*06c3fb27SDimitry Andric   return llvm::any_of(FixIts, [](const FixItHint &Hint) {
2084*06c3fb27SDimitry Andric     auto Range = Hint.RemoveRange;
2085*06c3fb27SDimitry Andric     if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
2086*06c3fb27SDimitry Andric       // If the range (or the first token) is (part of) a macro expansion:
2087*06c3fb27SDimitry Andric       return true;
2088*06c3fb27SDimitry Andric     return false;
2089*06c3fb27SDimitry Andric   });
2090*06c3fb27SDimitry Andric }
2091*06c3fb27SDimitry Andric 
2092*06c3fb27SDimitry Andric static bool impossibleToFixForVar(const FixableGadgetSets &FixablesForAllVars,
2093*06c3fb27SDimitry Andric                                   const Strategy &S,
2094*06c3fb27SDimitry Andric                                   const VarDecl * Var) {
2095*06c3fb27SDimitry Andric   for (const auto &F : FixablesForAllVars.byVar.find(Var)->second) {
2096*06c3fb27SDimitry Andric     std::optional<FixItList> Fixits = F->getFixits(S);
2097*06c3fb27SDimitry Andric     if (!Fixits) {
2098*06c3fb27SDimitry Andric       return true;
2099*06c3fb27SDimitry Andric     }
2100*06c3fb27SDimitry Andric   }
2101*06c3fb27SDimitry Andric   return false;
2102*06c3fb27SDimitry Andric }
2103*06c3fb27SDimitry Andric 
2104bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList>
2105*06c3fb27SDimitry Andric getFixIts(FixableGadgetSets &FixablesForAllVars, const Strategy &S,
2106*06c3fb27SDimitry Andric 	  ASTContext &Ctx,
2107*06c3fb27SDimitry Andric           /* The function decl under analysis */ const Decl *D,
2108*06c3fb27SDimitry Andric           const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
2109*06c3fb27SDimitry Andric           const DefMapTy &VarGrpMap) {
2110bdd1243dSDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariable;
2111*06c3fb27SDimitry Andric   for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
2112*06c3fb27SDimitry Andric     FixItsForVariable[VD] =
2113*06c3fb27SDimitry Andric         fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
2114*06c3fb27SDimitry Andric     // If we fail to produce Fix-It for the declaration we have to skip the
2115*06c3fb27SDimitry Andric     // variable entirely.
2116*06c3fb27SDimitry Andric     if (FixItsForVariable[VD].empty()) {
2117*06c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
2118*06c3fb27SDimitry Andric       continue;
2119*06c3fb27SDimitry Andric     }
2120bdd1243dSDimitry Andric     bool ImpossibleToFix = false;
2121bdd1243dSDimitry Andric     llvm::SmallVector<FixItHint, 16> FixItsForVD;
2122bdd1243dSDimitry Andric     for (const auto &F : Fixables) {
2123*06c3fb27SDimitry Andric       std::optional<FixItList> Fixits = F->getFixits(S);
2124bdd1243dSDimitry Andric       if (!Fixits) {
2125bdd1243dSDimitry Andric         ImpossibleToFix = true;
2126bdd1243dSDimitry Andric         break;
2127bdd1243dSDimitry Andric       } else {
2128bdd1243dSDimitry Andric         const FixItList CorrectFixes = Fixits.value();
2129bdd1243dSDimitry Andric         FixItsForVD.insert(FixItsForVD.end(), CorrectFixes.begin(),
2130bdd1243dSDimitry Andric                            CorrectFixes.end());
2131bdd1243dSDimitry Andric       }
2132bdd1243dSDimitry Andric     }
2133*06c3fb27SDimitry Andric 
2134*06c3fb27SDimitry Andric     if (ImpossibleToFix) {
2135bdd1243dSDimitry Andric       FixItsForVariable.erase(VD);
2136*06c3fb27SDimitry Andric       continue;
2137*06c3fb27SDimitry Andric     }
2138*06c3fb27SDimitry Andric 
2139*06c3fb27SDimitry Andric     const auto VarGroupForVD = VarGrpMap.find(VD);
2140*06c3fb27SDimitry Andric     if (VarGroupForVD != VarGrpMap.end()) {
2141*06c3fb27SDimitry Andric       for (const VarDecl * V : VarGroupForVD->second) {
2142*06c3fb27SDimitry Andric         if (V == VD) {
2143*06c3fb27SDimitry Andric           continue;
2144*06c3fb27SDimitry Andric         }
2145*06c3fb27SDimitry Andric         if (impossibleToFixForVar(FixablesForAllVars, S, V)) {
2146*06c3fb27SDimitry Andric           ImpossibleToFix = true;
2147*06c3fb27SDimitry Andric           break;
2148*06c3fb27SDimitry Andric         }
2149*06c3fb27SDimitry Andric       }
2150*06c3fb27SDimitry Andric 
2151*06c3fb27SDimitry Andric       if (ImpossibleToFix) {
2152*06c3fb27SDimitry Andric         FixItsForVariable.erase(VD);
2153*06c3fb27SDimitry Andric         for (const VarDecl * V : VarGroupForVD->second) {
2154*06c3fb27SDimitry Andric           FixItsForVariable.erase(V);
2155*06c3fb27SDimitry Andric         }
2156*06c3fb27SDimitry Andric         continue;
2157*06c3fb27SDimitry Andric       }
2158*06c3fb27SDimitry Andric     }
2159bdd1243dSDimitry Andric     FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
2160bdd1243dSDimitry Andric                                  FixItsForVD.begin(), FixItsForVD.end());
2161*06c3fb27SDimitry Andric 
2162*06c3fb27SDimitry Andric     // Fix-it shall not overlap with macros or/and templates:
2163*06c3fb27SDimitry Andric     if (overlapWithMacro(FixItsForVariable[VD]) ||
2164*06c3fb27SDimitry Andric         clang::internal::anyConflict(FixItsForVariable[VD],
2165*06c3fb27SDimitry Andric                                      Ctx.getSourceManager())) {
2166*06c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
2167*06c3fb27SDimitry Andric       continue;
2168*06c3fb27SDimitry Andric     }
2169*06c3fb27SDimitry Andric   }
2170*06c3fb27SDimitry Andric 
2171*06c3fb27SDimitry Andric   for (auto VD : FixItsForVariable) {
2172*06c3fb27SDimitry Andric     const auto VarGroupForVD = VarGrpMap.find(VD.first);
2173*06c3fb27SDimitry Andric     const Strategy::Kind ReplacementTypeForVD = S.lookup(VD.first);
2174*06c3fb27SDimitry Andric     if (VarGroupForVD != VarGrpMap.end()) {
2175*06c3fb27SDimitry Andric       for (const VarDecl * Var : VarGroupForVD->second) {
2176*06c3fb27SDimitry Andric         if (Var == VD.first) {
2177*06c3fb27SDimitry Andric           continue;
2178*06c3fb27SDimitry Andric         }
2179*06c3fb27SDimitry Andric 
2180*06c3fb27SDimitry Andric         FixItList GroupFix;
2181*06c3fb27SDimitry Andric         if (FixItsForVariable.find(Var) == FixItsForVariable.end()) {
2182*06c3fb27SDimitry Andric           GroupFix = fixVariable(Var, ReplacementTypeForVD, D, Tracker,
2183*06c3fb27SDimitry Andric                                  Var->getASTContext(), Handler);
2184*06c3fb27SDimitry Andric         } else {
2185*06c3fb27SDimitry Andric           GroupFix = FixItsForVariable[Var];
2186*06c3fb27SDimitry Andric         }
2187*06c3fb27SDimitry Andric 
2188*06c3fb27SDimitry Andric         for (auto Fix : GroupFix) {
2189*06c3fb27SDimitry Andric           FixItsForVariable[VD.first].push_back(Fix);
2190*06c3fb27SDimitry Andric         }
2191*06c3fb27SDimitry Andric       }
2192*06c3fb27SDimitry Andric     }
2193bdd1243dSDimitry Andric   }
2194bdd1243dSDimitry Andric   return FixItsForVariable;
2195bdd1243dSDimitry Andric }
2196bdd1243dSDimitry Andric 
2197*06c3fb27SDimitry Andric 
2198bdd1243dSDimitry Andric static Strategy
2199bdd1243dSDimitry Andric getNaiveStrategy(const llvm::SmallVectorImpl<const VarDecl *> &UnsafeVars) {
2200bdd1243dSDimitry Andric   Strategy S;
2201bdd1243dSDimitry Andric   for (const VarDecl *VD : UnsafeVars) {
2202bdd1243dSDimitry Andric     S.set(VD, Strategy::Kind::Span);
2203bdd1243dSDimitry Andric   }
2204bdd1243dSDimitry Andric   return S;
2205bdd1243dSDimitry Andric }
2206bdd1243dSDimitry Andric 
2207bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D,
2208*06c3fb27SDimitry Andric                                    UnsafeBufferUsageHandler &Handler,
2209*06c3fb27SDimitry Andric                                    bool EmitSuggestions) {
2210bdd1243dSDimitry Andric   assert(D && D->getBody());
2211bdd1243dSDimitry Andric 
2212*06c3fb27SDimitry Andric   // We do not want to visit a Lambda expression defined inside a method independently.
2213*06c3fb27SDimitry Andric   // Instead, it should be visited along with the outer method.
2214*06c3fb27SDimitry Andric   if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
2215*06c3fb27SDimitry Andric     if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
2216*06c3fb27SDimitry Andric       return;
2217bdd1243dSDimitry Andric   }
2218bdd1243dSDimitry Andric 
2219*06c3fb27SDimitry Andric   // Do not emit fixit suggestions for functions declared in an
2220*06c3fb27SDimitry Andric   // extern "C" block.
2221*06c3fb27SDimitry Andric   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
2222*06c3fb27SDimitry Andric       for (FunctionDecl *FReDecl : FD->redecls()) {
2223*06c3fb27SDimitry Andric       if (FReDecl->isExternC()) {
2224*06c3fb27SDimitry Andric         EmitSuggestions = false;
2225*06c3fb27SDimitry Andric         break;
2226*06c3fb27SDimitry Andric       }
2227*06c3fb27SDimitry Andric     }
2228*06c3fb27SDimitry Andric   }
2229*06c3fb27SDimitry Andric 
2230*06c3fb27SDimitry Andric   WarningGadgetSets UnsafeOps;
2231*06c3fb27SDimitry Andric   FixableGadgetSets FixablesForAllVars;
2232*06c3fb27SDimitry Andric 
2233*06c3fb27SDimitry Andric   auto [FixableGadgets, WarningGadgets, Tracker] =
2234*06c3fb27SDimitry Andric     findGadgets(D, Handler, EmitSuggestions);
2235*06c3fb27SDimitry Andric 
2236*06c3fb27SDimitry Andric   if (!EmitSuggestions) {
2237*06c3fb27SDimitry Andric     // Our job is very easy without suggestions. Just warn about
2238*06c3fb27SDimitry Andric     // every problematic operation and consider it done. No need to deal
2239*06c3fb27SDimitry Andric     // with fixable gadgets, no need to group operations by variable.
2240*06c3fb27SDimitry Andric     for (const auto &G : WarningGadgets) {
2241*06c3fb27SDimitry Andric       Handler.handleUnsafeOperation(G->getBaseStmt(),
2242*06c3fb27SDimitry Andric                                     /*IsRelatedToDecl=*/false);
2243*06c3fb27SDimitry Andric     }
2244*06c3fb27SDimitry Andric 
2245*06c3fb27SDimitry Andric     // This return guarantees that most of the machine doesn't run when
2246*06c3fb27SDimitry Andric     // suggestions aren't requested.
2247*06c3fb27SDimitry Andric     assert(FixableGadgets.size() == 0 &&
2248*06c3fb27SDimitry Andric            "Fixable gadgets found but suggestions not requested!");
2249*06c3fb27SDimitry Andric     return;
2250*06c3fb27SDimitry Andric   }
2251*06c3fb27SDimitry Andric 
2252*06c3fb27SDimitry Andric   UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
2253*06c3fb27SDimitry Andric   FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
2254*06c3fb27SDimitry Andric 
2255*06c3fb27SDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
2256*06c3fb27SDimitry Andric   DefMapTy VariableGroupsMap{};
2257*06c3fb27SDimitry Andric 
2258bdd1243dSDimitry Andric   // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
2259*06c3fb27SDimitry Andric   for (auto it = FixablesForAllVars.byVar.cbegin();
2260*06c3fb27SDimitry Andric        it != FixablesForAllVars.byVar.cend();) {
2261*06c3fb27SDimitry Andric       // FIXME: need to deal with global variables later
2262*06c3fb27SDimitry Andric       if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first)) ||
2263*06c3fb27SDimitry Andric           Tracker.hasUnclaimedUses(it->first) || it->first->isInitCapture()) {
2264*06c3fb27SDimitry Andric       it = FixablesForAllVars.byVar.erase(it);
2265bdd1243dSDimitry Andric     } else {
2266bdd1243dSDimitry Andric       ++it;
2267bdd1243dSDimitry Andric     }
2268bdd1243dSDimitry Andric   }
2269bdd1243dSDimitry Andric 
2270bdd1243dSDimitry Andric   llvm::SmallVector<const VarDecl *, 16> UnsafeVars;
2271*06c3fb27SDimitry Andric   for (const auto &[VD, ignore] : FixablesForAllVars.byVar)
2272bdd1243dSDimitry Andric     UnsafeVars.push_back(VD);
2273bdd1243dSDimitry Andric 
2274*06c3fb27SDimitry Andric   // Fixpoint iteration for pointer assignments
2275*06c3fb27SDimitry Andric   using DepMapTy = DenseMap<const VarDecl *, std::set<const VarDecl *>>;
2276*06c3fb27SDimitry Andric   DepMapTy DependenciesMap{};
2277*06c3fb27SDimitry Andric   DepMapTy PtrAssignmentGraph{};
2278*06c3fb27SDimitry Andric 
2279*06c3fb27SDimitry Andric   for (auto it : FixablesForAllVars.byVar) {
2280*06c3fb27SDimitry Andric     for (const FixableGadget *fixable : it.second) {
2281*06c3fb27SDimitry Andric       std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
2282*06c3fb27SDimitry Andric                                   fixable->getStrategyImplications();
2283*06c3fb27SDimitry Andric       if (ImplPair) {
2284*06c3fb27SDimitry Andric         std::pair<const VarDecl *, const VarDecl *> Impl = ImplPair.value();
2285*06c3fb27SDimitry Andric         PtrAssignmentGraph[Impl.first].insert(Impl.second);
2286*06c3fb27SDimitry Andric       }
2287*06c3fb27SDimitry Andric     }
2288*06c3fb27SDimitry Andric   }
2289*06c3fb27SDimitry Andric 
2290*06c3fb27SDimitry Andric   /*
2291*06c3fb27SDimitry Andric    The following code does a BFS traversal of the `PtrAssignmentGraph`
2292*06c3fb27SDimitry Andric    considering all unsafe vars as starting nodes and constructs an undirected
2293*06c3fb27SDimitry Andric    graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
2294*06c3fb27SDimitry Andric    elimiates all variables that are unreachable from any unsafe var. In other
2295*06c3fb27SDimitry Andric    words, this removes all dependencies that don't include any unsafe variable
2296*06c3fb27SDimitry Andric    and consequently don't need any fixit generation.
2297*06c3fb27SDimitry Andric    Note: A careful reader would observe that the code traverses
2298*06c3fb27SDimitry Andric    `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
2299*06c3fb27SDimitry Andric    `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
2300*06c3fb27SDimitry Andric    achieve the same result but the one used here dramatically cuts the
2301*06c3fb27SDimitry Andric    amount of hoops the second part of the algorithm needs to jump, given that
2302*06c3fb27SDimitry Andric    a lot of these connections become "direct". The reader is advised not to
2303*06c3fb27SDimitry Andric    imagine how the graph is transformed because of using `Var` instead of
2304*06c3fb27SDimitry Andric    `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
2305*06c3fb27SDimitry Andric    and think about why it's equivalent later.
2306*06c3fb27SDimitry Andric    */
2307*06c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVarsDirected{};
2308*06c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
2309*06c3fb27SDimitry Andric     if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
2310*06c3fb27SDimitry Andric 
2311*06c3fb27SDimitry Andric       std::queue<const VarDecl*> QueueDirected{};
2312*06c3fb27SDimitry Andric       QueueDirected.push(Var);
2313*06c3fb27SDimitry Andric       while(!QueueDirected.empty()) {
2314*06c3fb27SDimitry Andric         const VarDecl* CurrentVar = QueueDirected.front();
2315*06c3fb27SDimitry Andric         QueueDirected.pop();
2316*06c3fb27SDimitry Andric         VisitedVarsDirected.insert(CurrentVar);
2317*06c3fb27SDimitry Andric         auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
2318*06c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
2319*06c3fb27SDimitry Andric           if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
2320*06c3fb27SDimitry Andric             QueueDirected.push(Adj);
2321*06c3fb27SDimitry Andric           }
2322*06c3fb27SDimitry Andric           DependenciesMap[Var].insert(Adj);
2323*06c3fb27SDimitry Andric           DependenciesMap[Adj].insert(Var);
2324*06c3fb27SDimitry Andric         }
2325*06c3fb27SDimitry Andric       }
2326*06c3fb27SDimitry Andric     }
2327*06c3fb27SDimitry Andric   }
2328*06c3fb27SDimitry Andric 
2329*06c3fb27SDimitry Andric   // Group Connected Components for Unsafe Vars
2330*06c3fb27SDimitry Andric   // (Dependencies based on pointer assignments)
2331*06c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVars{};
2332*06c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
2333*06c3fb27SDimitry Andric     if (VisitedVars.find(Var) == VisitedVars.end()) {
2334*06c3fb27SDimitry Andric       std::vector<const VarDecl *> VarGroup{};
2335*06c3fb27SDimitry Andric 
2336*06c3fb27SDimitry Andric       std::queue<const VarDecl*> Queue{};
2337*06c3fb27SDimitry Andric       Queue.push(Var);
2338*06c3fb27SDimitry Andric       while(!Queue.empty()) {
2339*06c3fb27SDimitry Andric         const VarDecl* CurrentVar = Queue.front();
2340*06c3fb27SDimitry Andric         Queue.pop();
2341*06c3fb27SDimitry Andric         VisitedVars.insert(CurrentVar);
2342*06c3fb27SDimitry Andric         VarGroup.push_back(CurrentVar);
2343*06c3fb27SDimitry Andric         auto AdjacentNodes = DependenciesMap[CurrentVar];
2344*06c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
2345*06c3fb27SDimitry Andric           if (VisitedVars.find(Adj) == VisitedVars.end()) {
2346*06c3fb27SDimitry Andric             Queue.push(Adj);
2347*06c3fb27SDimitry Andric           }
2348*06c3fb27SDimitry Andric         }
2349*06c3fb27SDimitry Andric       }
2350*06c3fb27SDimitry Andric       for (const VarDecl * V : VarGroup) {
2351*06c3fb27SDimitry Andric         if (UnsafeOps.byVar.find(V) != UnsafeOps.byVar.end()) {
2352*06c3fb27SDimitry Andric           VariableGroupsMap[V] = VarGroup;
2353*06c3fb27SDimitry Andric         }
2354*06c3fb27SDimitry Andric       }
2355*06c3fb27SDimitry Andric     }
2356*06c3fb27SDimitry Andric   }
2357*06c3fb27SDimitry Andric 
2358bdd1243dSDimitry Andric   Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars);
2359*06c3fb27SDimitry Andric 
2360*06c3fb27SDimitry Andric   FixItsForVariableGroup =
2361*06c3fb27SDimitry Andric       getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
2362*06c3fb27SDimitry Andric                 Tracker, Handler, VariableGroupsMap);
2363bdd1243dSDimitry Andric 
2364bdd1243dSDimitry Andric   // FIXME Detect overlapping FixIts.
2365bdd1243dSDimitry Andric 
2366bdd1243dSDimitry Andric   for (const auto &G : UnsafeOps.noVar) {
2367bdd1243dSDimitry Andric     Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false);
2368bdd1243dSDimitry Andric   }
2369bdd1243dSDimitry Andric 
2370bdd1243dSDimitry Andric   for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
2371*06c3fb27SDimitry Andric     auto FixItsIt = FixItsForVariableGroup.find(VD);
2372*06c3fb27SDimitry Andric     Handler.handleUnsafeVariableGroup(VD, VariableGroupsMap,
2373*06c3fb27SDimitry Andric                                       FixItsIt != FixItsForVariableGroup.end()
2374bdd1243dSDimitry Andric                                       ? std::move(FixItsIt->second)
2375bdd1243dSDimitry Andric                                       : FixItList{});
2376bdd1243dSDimitry Andric     for (const auto &G : WarningGadgets) {
2377bdd1243dSDimitry Andric       Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true);
2378bdd1243dSDimitry Andric     }
2379bdd1243dSDimitry Andric   }
2380bdd1243dSDimitry Andric }
2381