xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/UnsafeBufferUsage.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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*0fca6ea1SDimitry Andric #include "clang/AST/ASTContext.h"
1106c3fb27SDimitry Andric #include "clang/AST/Decl.h"
125f757f3fSDimitry Andric #include "clang/AST/Expr.h"
13bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
14*0fca6ea1SDimitry Andric #include "clang/AST/Stmt.h"
155f757f3fSDimitry Andric #include "clang/AST/StmtVisitor.h"
16bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
17*0fca6ea1SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
18*0fca6ea1SDimitry Andric #include "clang/Basic/CharInfo.h"
19*0fca6ea1SDimitry Andric #include "clang/Basic/SourceLocation.h"
2006c3fb27SDimitry Andric #include "clang/Lex/Lexer.h"
2106c3fb27SDimitry Andric #include "clang/Lex/Preprocessor.h"
22*0fca6ea1SDimitry Andric #include "llvm/ADT/APSInt.h"
23bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h"
24*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h"
25*0fca6ea1SDimitry Andric #include "llvm/Support/Casting.h"
26bdd1243dSDimitry Andric #include <memory>
27bdd1243dSDimitry Andric #include <optional>
2806c3fb27SDimitry Andric #include <queue>
29*0fca6ea1SDimitry Andric #include <sstream>
30bdd1243dSDimitry Andric 
31bdd1243dSDimitry Andric using namespace llvm;
32bdd1243dSDimitry Andric using namespace clang;
33bdd1243dSDimitry Andric using namespace ast_matchers;
34bdd1243dSDimitry Andric 
355f757f3fSDimitry Andric #ifndef NDEBUG
365f757f3fSDimitry Andric namespace {
375f757f3fSDimitry Andric class StmtDebugPrinter
385f757f3fSDimitry Andric     : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
395f757f3fSDimitry Andric public:
405f757f3fSDimitry Andric   std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
415f757f3fSDimitry Andric 
425f757f3fSDimitry Andric   std::string VisitBinaryOperator(const BinaryOperator *BO) {
435f757f3fSDimitry Andric     return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
445f757f3fSDimitry Andric   }
455f757f3fSDimitry Andric 
465f757f3fSDimitry Andric   std::string VisitUnaryOperator(const UnaryOperator *UO) {
475f757f3fSDimitry Andric     return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
485f757f3fSDimitry Andric   }
495f757f3fSDimitry Andric 
505f757f3fSDimitry Andric   std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
515f757f3fSDimitry Andric     return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
525f757f3fSDimitry Andric   }
535f757f3fSDimitry Andric };
545f757f3fSDimitry Andric 
555f757f3fSDimitry Andric // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
565f757f3fSDimitry Andric // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
575f757f3fSDimitry Andric static std::string getDREAncestorString(const DeclRefExpr *DRE,
585f757f3fSDimitry Andric                                         ASTContext &Ctx) {
595f757f3fSDimitry Andric   std::stringstream SS;
605f757f3fSDimitry Andric   const Stmt *St = DRE;
615f757f3fSDimitry Andric   StmtDebugPrinter StmtPriner;
625f757f3fSDimitry Andric 
635f757f3fSDimitry Andric   do {
645f757f3fSDimitry Andric     SS << StmtPriner.Visit(St);
655f757f3fSDimitry Andric 
665f757f3fSDimitry Andric     DynTypedNodeList StParents = Ctx.getParents(*St);
675f757f3fSDimitry Andric 
685f757f3fSDimitry Andric     if (StParents.size() > 1)
695f757f3fSDimitry Andric       return "unavailable due to multiple parents";
705f757f3fSDimitry Andric     if (StParents.size() == 0)
715f757f3fSDimitry Andric       break;
725f757f3fSDimitry Andric     St = StParents.begin()->get<Stmt>();
735f757f3fSDimitry Andric     if (St)
745f757f3fSDimitry Andric       SS << " ==> ";
755f757f3fSDimitry Andric   } while (St);
765f757f3fSDimitry Andric   return SS.str();
775f757f3fSDimitry Andric }
785f757f3fSDimitry Andric } // namespace
795f757f3fSDimitry Andric #endif /* NDEBUG */
805f757f3fSDimitry Andric 
81bdd1243dSDimitry Andric namespace clang::ast_matchers {
82bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
83bdd1243dSDimitry Andric // except for those belonging to a different callable of "n".
84bdd1243dSDimitry Andric class MatchDescendantVisitor
85bdd1243dSDimitry Andric     : public RecursiveASTVisitor<MatchDescendantVisitor> {
86bdd1243dSDimitry Andric public:
87bdd1243dSDimitry Andric   typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase;
88bdd1243dSDimitry Andric 
89bdd1243dSDimitry Andric   // Creates an AST visitor that matches `Matcher` on all
90bdd1243dSDimitry Andric   // descendants of a given node "n" except for the ones
91bdd1243dSDimitry Andric   // belonging to a different callable of "n".
92bdd1243dSDimitry Andric   MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
93bdd1243dSDimitry Andric                          internal::ASTMatchFinder *Finder,
94bdd1243dSDimitry Andric                          internal::BoundNodesTreeBuilder *Builder,
9506c3fb27SDimitry Andric                          internal::ASTMatchFinder::BindKind Bind,
9606c3fb27SDimitry Andric                          const bool ignoreUnevaluatedContext)
97bdd1243dSDimitry Andric       : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
9806c3fb27SDimitry Andric         Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {}
99bdd1243dSDimitry Andric 
100bdd1243dSDimitry Andric   // Returns true if a match is found in a subtree of `DynNode`, which belongs
101bdd1243dSDimitry Andric   // to the same callable of `DynNode`.
102bdd1243dSDimitry Andric   bool findMatch(const DynTypedNode &DynNode) {
103bdd1243dSDimitry Andric     Matches = false;
104bdd1243dSDimitry Andric     if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
105bdd1243dSDimitry Andric       TraverseStmt(const_cast<Stmt *>(StmtNode));
106bdd1243dSDimitry Andric       *Builder = ResultBindings;
107bdd1243dSDimitry Andric       return Matches;
108bdd1243dSDimitry Andric     }
109bdd1243dSDimitry Andric     return false;
110bdd1243dSDimitry Andric   }
111bdd1243dSDimitry Andric 
112bdd1243dSDimitry Andric   // The following are overriding methods from the base visitor class.
113bdd1243dSDimitry Andric   // They are public only to allow CRTP to work. They are *not *part
114bdd1243dSDimitry Andric   // of the public API of this class.
115bdd1243dSDimitry Andric 
116bdd1243dSDimitry Andric   // For the matchers so far used in safe buffers, we only need to match
117bdd1243dSDimitry Andric   // `Stmt`s.  To override more as needed.
118bdd1243dSDimitry Andric 
119bdd1243dSDimitry Andric   bool TraverseDecl(Decl *Node) {
120bdd1243dSDimitry Andric     if (!Node)
121bdd1243dSDimitry Andric       return true;
122bdd1243dSDimitry Andric     if (!match(*Node))
123bdd1243dSDimitry Andric       return false;
124bdd1243dSDimitry Andric     // To skip callables:
125bdd1243dSDimitry Andric     if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
126bdd1243dSDimitry Andric       return true;
127bdd1243dSDimitry Andric     // Traverse descendants
128bdd1243dSDimitry Andric     return VisitorBase::TraverseDecl(Node);
129bdd1243dSDimitry Andric   }
130bdd1243dSDimitry Andric 
13106c3fb27SDimitry Andric   bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) {
13206c3fb27SDimitry Andric     // These are unevaluated, except the result expression.
13306c3fb27SDimitry Andric     if (ignoreUnevaluatedContext)
13406c3fb27SDimitry Andric       return TraverseStmt(Node->getResultExpr());
13506c3fb27SDimitry Andric     return VisitorBase::TraverseGenericSelectionExpr(Node);
13606c3fb27SDimitry Andric   }
13706c3fb27SDimitry Andric 
13806c3fb27SDimitry Andric   bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) {
13906c3fb27SDimitry Andric     // Unevaluated context.
14006c3fb27SDimitry Andric     if (ignoreUnevaluatedContext)
14106c3fb27SDimitry Andric       return true;
14206c3fb27SDimitry Andric     return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node);
14306c3fb27SDimitry Andric   }
14406c3fb27SDimitry Andric 
14506c3fb27SDimitry Andric   bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) {
14606c3fb27SDimitry Andric     // Unevaluated context.
14706c3fb27SDimitry Andric     if (ignoreUnevaluatedContext)
14806c3fb27SDimitry Andric       return true;
14906c3fb27SDimitry Andric     return VisitorBase::TraverseTypeOfExprTypeLoc(Node);
15006c3fb27SDimitry Andric   }
15106c3fb27SDimitry Andric 
15206c3fb27SDimitry Andric   bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) {
15306c3fb27SDimitry Andric     // Unevaluated context.
15406c3fb27SDimitry Andric     if (ignoreUnevaluatedContext)
15506c3fb27SDimitry Andric       return true;
15606c3fb27SDimitry Andric     return VisitorBase::TraverseDecltypeTypeLoc(Node);
15706c3fb27SDimitry Andric   }
15806c3fb27SDimitry Andric 
15906c3fb27SDimitry Andric   bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) {
16006c3fb27SDimitry Andric     // Unevaluated context.
16106c3fb27SDimitry Andric     if (ignoreUnevaluatedContext)
16206c3fb27SDimitry Andric       return true;
16306c3fb27SDimitry Andric     return VisitorBase::TraverseCXXNoexceptExpr(Node);
16406c3fb27SDimitry Andric   }
16506c3fb27SDimitry Andric 
16606c3fb27SDimitry Andric   bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) {
16706c3fb27SDimitry Andric     // Unevaluated context.
16806c3fb27SDimitry Andric     if (ignoreUnevaluatedContext)
16906c3fb27SDimitry Andric       return true;
17006c3fb27SDimitry Andric     return VisitorBase::TraverseCXXTypeidExpr(Node);
17106c3fb27SDimitry Andric   }
17206c3fb27SDimitry Andric 
173bdd1243dSDimitry Andric   bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
174bdd1243dSDimitry Andric     if (!Node)
175bdd1243dSDimitry Andric       return true;
176bdd1243dSDimitry Andric     if (!match(*Node))
177bdd1243dSDimitry Andric       return false;
178bdd1243dSDimitry Andric     return VisitorBase::TraverseStmt(Node);
179bdd1243dSDimitry Andric   }
180bdd1243dSDimitry Andric 
181bdd1243dSDimitry Andric   bool shouldVisitTemplateInstantiations() const { return true; }
182bdd1243dSDimitry Andric   bool shouldVisitImplicitCode() const {
183bdd1243dSDimitry Andric     // TODO: let's ignore implicit code for now
184bdd1243dSDimitry Andric     return false;
185bdd1243dSDimitry Andric   }
186bdd1243dSDimitry Andric 
187bdd1243dSDimitry Andric private:
188bdd1243dSDimitry Andric   // Sets 'Matched' to true if 'Matcher' matches 'Node'
189bdd1243dSDimitry Andric   //
190bdd1243dSDimitry Andric   // Returns 'true' if traversal should continue after this function
191bdd1243dSDimitry Andric   // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
192bdd1243dSDimitry Andric   template <typename T> bool match(const T &Node) {
193bdd1243dSDimitry Andric     internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
194bdd1243dSDimitry Andric 
195bdd1243dSDimitry Andric     if (Matcher->matches(DynTypedNode::create(Node), Finder,
196bdd1243dSDimitry Andric                          &RecursiveBuilder)) {
197bdd1243dSDimitry Andric       ResultBindings.addMatch(RecursiveBuilder);
198bdd1243dSDimitry Andric       Matches = true;
199bdd1243dSDimitry Andric       if (Bind != internal::ASTMatchFinder::BK_All)
200bdd1243dSDimitry Andric         return false; // Abort as soon as a match is found.
201bdd1243dSDimitry Andric     }
202bdd1243dSDimitry Andric     return true;
203bdd1243dSDimitry Andric   }
204bdd1243dSDimitry Andric 
205bdd1243dSDimitry Andric   const internal::DynTypedMatcher *const Matcher;
206bdd1243dSDimitry Andric   internal::ASTMatchFinder *const Finder;
207bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder *const Builder;
208bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder ResultBindings;
209bdd1243dSDimitry Andric   const internal::ASTMatchFinder::BindKind Bind;
210bdd1243dSDimitry Andric   bool Matches;
21106c3fb27SDimitry Andric   bool ignoreUnevaluatedContext;
212bdd1243dSDimitry Andric };
213bdd1243dSDimitry Andric 
21406c3fb27SDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that.
21506c3fb27SDimitry Andric static auto hasPointerType() {
21606c3fb27SDimitry Andric   return hasType(hasCanonicalType(pointerType()));
21706c3fb27SDimitry Andric }
21806c3fb27SDimitry Andric 
219*0fca6ea1SDimitry Andric static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); }
22006c3fb27SDimitry Andric 
221*0fca6ea1SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>,
222*0fca6ea1SDimitry Andric               innerMatcher) {
223bdd1243dSDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
224bdd1243dSDimitry Andric 
225*0fca6ea1SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
226*0fca6ea1SDimitry Andric                                  true);
227bdd1243dSDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
228bdd1243dSDimitry Andric }
22906c3fb27SDimitry Andric 
230*0fca6ea1SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>,
231*0fca6ea1SDimitry Andric               innerMatcher) {
23206c3fb27SDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
23306c3fb27SDimitry Andric 
234*0fca6ea1SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
235*0fca6ea1SDimitry Andric                                  false);
23606c3fb27SDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
23706c3fb27SDimitry Andric }
23806c3fb27SDimitry Andric 
23906c3fb27SDimitry Andric // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
24006c3fb27SDimitry Andric AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *,
24106c3fb27SDimitry Andric               Handler) {
24206c3fb27SDimitry Andric   return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
24306c3fb27SDimitry Andric }
24406c3fb27SDimitry Andric 
245*0fca6ea1SDimitry Andric AST_MATCHER_P(Stmt, ignoreUnsafeBufferInContainer,
246*0fca6ea1SDimitry Andric               const UnsafeBufferUsageHandler *, Handler) {
247*0fca6ea1SDimitry Andric   return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());
248*0fca6ea1SDimitry Andric }
249*0fca6ea1SDimitry Andric 
25006c3fb27SDimitry Andric AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
25106c3fb27SDimitry Andric   return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
25206c3fb27SDimitry Andric }
25306c3fb27SDimitry Andric 
25406c3fb27SDimitry Andric // Matches a `UnaryOperator` whose operator is pre-increment:
25506c3fb27SDimitry Andric AST_MATCHER(UnaryOperator, isPreInc) {
25606c3fb27SDimitry Andric   return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
25706c3fb27SDimitry Andric }
25806c3fb27SDimitry Andric 
25906c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
26006c3fb27SDimitry Andric // matches 'e' and 'e' is in an Unspecified Lvalue Context.
26106c3fb27SDimitry Andric static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) {
26206c3fb27SDimitry Andric   // clang-format off
26306c3fb27SDimitry Andric   return
26406c3fb27SDimitry Andric     expr(anyOf(
26506c3fb27SDimitry Andric       implicitCastExpr(
26606c3fb27SDimitry Andric         hasCastKind(CastKind::CK_LValueToRValue),
26706c3fb27SDimitry Andric         castSubExpr(innerMatcher)),
26806c3fb27SDimitry Andric       binaryOperator(
26906c3fb27SDimitry Andric         hasAnyOperatorName("="),
27006c3fb27SDimitry Andric         hasLHS(innerMatcher)
27106c3fb27SDimitry Andric       )
27206c3fb27SDimitry Andric     ));
27306c3fb27SDimitry Andric   // clang-format on
27406c3fb27SDimitry Andric }
27506c3fb27SDimitry Andric 
27606c3fb27SDimitry Andric // Returns a matcher that matches any expression `e` such that `InnerMatcher`
27706c3fb27SDimitry Andric // matches `e` and `e` is in an Unspecified Pointer Context (UPC).
27806c3fb27SDimitry Andric static internal::Matcher<Stmt>
27906c3fb27SDimitry Andric isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
28006c3fb27SDimitry Andric   // A UPC can be
28106c3fb27SDimitry Andric   // 1. an argument of a function call (except the callee has [[unsafe_...]]
28206c3fb27SDimitry Andric   //    attribute), or
28306c3fb27SDimitry Andric   // 2. the operand of a pointer-to-(integer or bool) cast operation; or
28406c3fb27SDimitry Andric   // 3. the operand of a comparator operation; or
28506c3fb27SDimitry Andric   // 4. the operand of a pointer subtraction operation
28606c3fb27SDimitry Andric   //    (i.e., computing the distance between two pointers); or ...
28706c3fb27SDimitry Andric 
288*0fca6ea1SDimitry Andric   // clang-format off
289*0fca6ea1SDimitry Andric   auto CallArgMatcher = callExpr(
290*0fca6ea1SDimitry Andric     forEachArgumentWithParamType(
291*0fca6ea1SDimitry Andric       InnerMatcher,
292*0fca6ea1SDimitry Andric       isAnyPointer() /* array also decays to pointer type*/),
293*0fca6ea1SDimitry Andric     unless(callee(
294*0fca6ea1SDimitry Andric       functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
29506c3fb27SDimitry Andric 
29606c3fb27SDimitry Andric   auto CastOperandMatcher =
29706c3fb27SDimitry Andric       castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
29806c3fb27SDimitry Andric 		     hasCastKind(CastKind::CK_PointerToBoolean)),
29906c3fb27SDimitry Andric 	       castSubExpr(allOf(hasPointerType(), InnerMatcher)));
30006c3fb27SDimitry Andric 
30106c3fb27SDimitry Andric   auto CompOperandMatcher =
30206c3fb27SDimitry Andric       binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
30306c3fb27SDimitry Andric                      eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
30406c3fb27SDimitry Andric                             hasRHS(allOf(hasPointerType(), InnerMatcher))));
30506c3fb27SDimitry Andric 
30606c3fb27SDimitry Andric   // A matcher that matches pointer subtractions:
30706c3fb27SDimitry Andric   auto PtrSubtractionMatcher =
30806c3fb27SDimitry Andric       binaryOperator(hasOperatorName("-"),
30906c3fb27SDimitry Andric 		     // Note that here we need both LHS and RHS to be
31006c3fb27SDimitry Andric 		     // pointer. Then the inner matcher can match any of
31106c3fb27SDimitry Andric 		     // them:
31206c3fb27SDimitry Andric 		     allOf(hasLHS(hasPointerType()),
31306c3fb27SDimitry Andric 			   hasRHS(hasPointerType())),
31406c3fb27SDimitry Andric 		     eachOf(hasLHS(InnerMatcher),
31506c3fb27SDimitry Andric 			    hasRHS(InnerMatcher)));
316*0fca6ea1SDimitry Andric   // clang-format on
31706c3fb27SDimitry Andric 
31806c3fb27SDimitry Andric   return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
31906c3fb27SDimitry Andric                     PtrSubtractionMatcher));
32006c3fb27SDimitry Andric   // FIXME: any more cases? (UPC excludes the RHS of an assignment.  For now we
32106c3fb27SDimitry Andric   // don't have to check that.)
32206c3fb27SDimitry Andric }
32306c3fb27SDimitry Andric 
32406c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
32506c3fb27SDimitry Andric // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression
32606c3fb27SDimitry Andric // 'e' isn't evaluated to an RValue). For example, consider the following code:
32706c3fb27SDimitry Andric //    int *p = new int[4];
32806c3fb27SDimitry Andric //    int *q = new int[4];
32906c3fb27SDimitry Andric //    if ((p = q)) {}
33006c3fb27SDimitry Andric //    p = q;
33106c3fb27SDimitry Andric // The expression `p = q` in the conditional of the `if` statement
33206c3fb27SDimitry Andric // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
33306c3fb27SDimitry Andric // in the assignment statement is in an untyped context.
33406c3fb27SDimitry Andric static internal::Matcher<Stmt>
33506c3fb27SDimitry Andric isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
33606c3fb27SDimitry Andric   // An unspecified context can be
33706c3fb27SDimitry Andric   // 1. A compound statement,
33806c3fb27SDimitry Andric   // 2. The body of an if statement
33906c3fb27SDimitry Andric   // 3. Body of a loop
34006c3fb27SDimitry Andric   auto CompStmt = compoundStmt(forEach(InnerMatcher));
34106c3fb27SDimitry Andric   auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
34206c3fb27SDimitry Andric   auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
34306c3fb27SDimitry Andric   // FIXME: Handle loop bodies.
34406c3fb27SDimitry Andric   return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
34506c3fb27SDimitry Andric }
346*0fca6ea1SDimitry Andric 
347*0fca6ea1SDimitry Andric // Given a two-param std::span construct call, matches iff the call has the
348*0fca6ea1SDimitry Andric // following forms:
349*0fca6ea1SDimitry Andric //   1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
350*0fca6ea1SDimitry Andric //   2. `std::span<T>{new T, 1}`
351*0fca6ea1SDimitry Andric //   3. `std::span<T>{&var, 1}`
352*0fca6ea1SDimitry Andric //   4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size
353*0fca6ea1SDimitry Andric //   `n`
354*0fca6ea1SDimitry Andric //   5. `std::span<T>{any, 0}`
355*0fca6ea1SDimitry Andric AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
356*0fca6ea1SDimitry Andric   assert(Node.getNumArgs() == 2 &&
357*0fca6ea1SDimitry Andric          "expecting a two-parameter std::span constructor");
358*0fca6ea1SDimitry Andric   const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit();
359*0fca6ea1SDimitry Andric   const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit();
360*0fca6ea1SDimitry Andric   auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) {
361*0fca6ea1SDimitry Andric     if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext()))
362*0fca6ea1SDimitry Andric       if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) {
363*0fca6ea1SDimitry Andric         return APSInt::compareValues(*E0CV, *E1CV) == 0;
364*0fca6ea1SDimitry Andric       }
365*0fca6ea1SDimitry Andric     return false;
366*0fca6ea1SDimitry Andric   };
367*0fca6ea1SDimitry Andric   auto AreSameDRE = [](const Expr *E0, const Expr *E1) {
368*0fca6ea1SDimitry Andric     if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
369*0fca6ea1SDimitry Andric       if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
370*0fca6ea1SDimitry Andric         return DRE0->getDecl() == DRE1->getDecl();
371*0fca6ea1SDimitry Andric       }
372*0fca6ea1SDimitry Andric     return false;
373*0fca6ea1SDimitry Andric   };
374*0fca6ea1SDimitry Andric   std::optional<APSInt> Arg1CV =
375*0fca6ea1SDimitry Andric       Arg1->getIntegerConstantExpr(Finder->getASTContext());
376*0fca6ea1SDimitry Andric 
377*0fca6ea1SDimitry Andric   if (Arg1CV && Arg1CV->isZero())
378*0fca6ea1SDimitry Andric     // Check form 5:
379*0fca6ea1SDimitry Andric     return true;
380*0fca6ea1SDimitry Andric   switch (Arg0->IgnoreImplicit()->getStmtClass()) {
381*0fca6ea1SDimitry Andric   case Stmt::CXXNewExprClass:
382*0fca6ea1SDimitry Andric     if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) {
383*0fca6ea1SDimitry Andric       // Check form 1:
384*0fca6ea1SDimitry Andric       return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
385*0fca6ea1SDimitry Andric              HaveEqualConstantValues(*Size, Arg1);
386*0fca6ea1SDimitry Andric     }
387*0fca6ea1SDimitry Andric     // TODO: what's placeholder type? avoid it for now.
388*0fca6ea1SDimitry Andric     if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) {
389*0fca6ea1SDimitry Andric       // Check form 2:
390*0fca6ea1SDimitry Andric       return Arg1CV && Arg1CV->isOne();
391*0fca6ea1SDimitry Andric     }
392*0fca6ea1SDimitry Andric     break;
393*0fca6ea1SDimitry Andric   case Stmt::UnaryOperatorClass:
394*0fca6ea1SDimitry Andric     if (cast<UnaryOperator>(Arg0)->getOpcode() ==
395*0fca6ea1SDimitry Andric         UnaryOperator::Opcode::UO_AddrOf)
396*0fca6ea1SDimitry Andric       // Check form 3:
397*0fca6ea1SDimitry Andric       return Arg1CV && Arg1CV->isOne();
398*0fca6ea1SDimitry Andric     break;
399*0fca6ea1SDimitry Andric   default:
400*0fca6ea1SDimitry Andric     break;
401*0fca6ea1SDimitry Andric   }
402*0fca6ea1SDimitry Andric 
403*0fca6ea1SDimitry Andric   QualType Arg0Ty = Arg0->IgnoreImplicit()->getType();
404*0fca6ea1SDimitry Andric 
405*0fca6ea1SDimitry Andric   if (Arg0Ty->isConstantArrayType()) {
406*0fca6ea1SDimitry Andric     const APSInt ConstArrSize =
407*0fca6ea1SDimitry Andric         APSInt(cast<ConstantArrayType>(Arg0Ty)->getSize());
408*0fca6ea1SDimitry Andric 
409*0fca6ea1SDimitry Andric     // Check form 4:
410*0fca6ea1SDimitry Andric     return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;
411*0fca6ea1SDimitry Andric   }
412*0fca6ea1SDimitry Andric   return false;
413*0fca6ea1SDimitry Andric }
414*0fca6ea1SDimitry Andric 
415*0fca6ea1SDimitry Andric AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
416*0fca6ea1SDimitry Andric   // FIXME: Proper solution:
417*0fca6ea1SDimitry Andric   //  - refactor Sema::CheckArrayAccess
418*0fca6ea1SDimitry Andric   //    - split safe/OOB/unknown decision logic from diagnostics emitting code
419*0fca6ea1SDimitry Andric   //    -  e. g. "Try harder to find a NamedDecl to point at in the note."
420*0fca6ea1SDimitry Andric   //    already duplicated
421*0fca6ea1SDimitry Andric   //  - call both from Sema and from here
422*0fca6ea1SDimitry Andric 
423*0fca6ea1SDimitry Andric   const auto *BaseDRE =
424*0fca6ea1SDimitry Andric       dyn_cast<DeclRefExpr>(Node.getBase()->IgnoreParenImpCasts());
425*0fca6ea1SDimitry Andric   if (!BaseDRE)
426*0fca6ea1SDimitry Andric     return false;
427*0fca6ea1SDimitry Andric   if (!BaseDRE->getDecl())
428*0fca6ea1SDimitry Andric     return false;
429*0fca6ea1SDimitry Andric   const auto *CATy = Finder->getASTContext().getAsConstantArrayType(
430*0fca6ea1SDimitry Andric       BaseDRE->getDecl()->getType());
431*0fca6ea1SDimitry Andric   if (!CATy)
432*0fca6ea1SDimitry Andric     return false;
433*0fca6ea1SDimitry Andric 
434*0fca6ea1SDimitry Andric   if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) {
435*0fca6ea1SDimitry Andric     const APInt ArrIdx = IdxLit->getValue();
436*0fca6ea1SDimitry Andric     // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
437*0fca6ea1SDimitry Andric     // bug
438*0fca6ea1SDimitry Andric     if (ArrIdx.isNonNegative() &&
439*0fca6ea1SDimitry Andric         ArrIdx.getLimitedValue() < CATy->getLimitedSize())
440*0fca6ea1SDimitry Andric       return true;
441*0fca6ea1SDimitry Andric   }
442*0fca6ea1SDimitry Andric 
443*0fca6ea1SDimitry Andric   return false;
444*0fca6ea1SDimitry Andric }
445*0fca6ea1SDimitry Andric 
446bdd1243dSDimitry Andric } // namespace clang::ast_matchers
447bdd1243dSDimitry Andric 
448bdd1243dSDimitry Andric namespace {
449bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to
450bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs).
451bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
452bdd1243dSDimitry Andric 
453bdd1243dSDimitry Andric // Convenience typedef.
454bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>;
455bdd1243dSDimitry Andric } // namespace
456bdd1243dSDimitry Andric 
457bdd1243dSDimitry Andric namespace {
458bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to
459bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific
460bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object.
461bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand
462bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it.
463bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not
464bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify
465bdd1243dSDimitry Andric /// it warrants a fixit).
466bdd1243dSDimitry Andric class Gadget {
467bdd1243dSDimitry Andric public:
468bdd1243dSDimitry Andric   enum class Kind {
469bdd1243dSDimitry Andric #define GADGET(x) x,
470bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
471bdd1243dSDimitry Andric   };
472bdd1243dSDimitry Andric 
473bdd1243dSDimitry Andric   /// Common type of ASTMatchers used for discovering gadgets.
474bdd1243dSDimitry Andric   /// Useful for implementing the static matcher() methods
475bdd1243dSDimitry Andric   /// that are expected from all non-abstract subclasses.
476bdd1243dSDimitry Andric   using Matcher = decltype(stmt());
477bdd1243dSDimitry Andric 
478bdd1243dSDimitry Andric   Gadget(Kind K) : K(K) {}
479bdd1243dSDimitry Andric 
480bdd1243dSDimitry Andric   Kind getKind() const { return K; }
481bdd1243dSDimitry Andric 
4825f757f3fSDimitry Andric #ifndef NDEBUG
4835f757f3fSDimitry Andric   StringRef getDebugName() const {
4845f757f3fSDimitry Andric     switch (K) {
485*0fca6ea1SDimitry Andric #define GADGET(x)                                                              \
486*0fca6ea1SDimitry Andric   case Kind::x:                                                                \
487*0fca6ea1SDimitry Andric     return #x;
4885f757f3fSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
4895f757f3fSDimitry Andric     }
4905f757f3fSDimitry Andric     llvm_unreachable("Unhandled Gadget::Kind enum");
4915f757f3fSDimitry Andric   }
4925f757f3fSDimitry Andric #endif
4935f757f3fSDimitry Andric 
494bdd1243dSDimitry Andric   virtual bool isWarningGadget() const = 0;
495*0fca6ea1SDimitry Andric   // TODO remove this method from WarningGadget interface. It's only used for
496*0fca6ea1SDimitry Andric   // debug prints in FixableGadget.
497*0fca6ea1SDimitry Andric   virtual SourceLocation getSourceLoc() const = 0;
498bdd1243dSDimitry Andric 
499bdd1243dSDimitry Andric   /// Returns the list of pointer-type variables on which this gadget performs
500bdd1243dSDimitry Andric   /// its operation. Typically, there's only one variable. This isn't a list
501bdd1243dSDimitry Andric   /// of all DeclRefExprs in the gadget's AST!
502bdd1243dSDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const = 0;
503bdd1243dSDimitry Andric 
504bdd1243dSDimitry Andric   virtual ~Gadget() = default;
505bdd1243dSDimitry Andric 
506bdd1243dSDimitry Andric private:
507bdd1243dSDimitry Andric   Kind K;
508bdd1243dSDimitry Andric };
509bdd1243dSDimitry Andric 
510bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants
511bdd1243dSDimitry Andric /// an immediate warning.
512bdd1243dSDimitry Andric class WarningGadget : public Gadget {
513bdd1243dSDimitry Andric public:
514bdd1243dSDimitry Andric   WarningGadget(Kind K) : Gadget(K) {}
515bdd1243dSDimitry Andric 
516bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return G->isWarningGadget(); }
517bdd1243dSDimitry Andric   bool isWarningGadget() const final { return true; }
518*0fca6ea1SDimitry Andric 
519*0fca6ea1SDimitry Andric   virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
520*0fca6ea1SDimitry Andric                                      bool IsRelatedToDecl,
521*0fca6ea1SDimitry Andric                                      ASTContext &Ctx) const = 0;
522bdd1243dSDimitry Andric };
523bdd1243dSDimitry Andric 
524*0fca6ea1SDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but
525*0fca6ea1SDimitry Andric /// need to be properly recognized in order to emit fixes. For example, if a raw
526*0fca6ea1SDimitry Andric /// pointer-type variable is replaced by a safe C++ container, every use of such
527*0fca6ea1SDimitry Andric /// variable must be carefully considered and possibly updated.
528bdd1243dSDimitry Andric class FixableGadget : public Gadget {
529bdd1243dSDimitry Andric public:
530bdd1243dSDimitry Andric   FixableGadget(Kind K) : Gadget(K) {}
531bdd1243dSDimitry Andric 
532bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
533bdd1243dSDimitry Andric   bool isWarningGadget() const final { return false; }
534bdd1243dSDimitry Andric 
535bdd1243dSDimitry Andric   /// Returns a fixit that would fix the current gadget according to
53606c3fb27SDimitry Andric   /// the current strategy. Returns std::nullopt if the fix cannot be produced;
537bdd1243dSDimitry Andric   /// returns an empty list if no fixes are necessary.
538*0fca6ea1SDimitry Andric   virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
539bdd1243dSDimitry Andric     return std::nullopt;
540bdd1243dSDimitry Andric   }
54106c3fb27SDimitry Andric 
542*0fca6ea1SDimitry Andric   /// Returns a list of two elements where the first element is the LHS of a
543*0fca6ea1SDimitry Andric   /// pointer assignment statement and the second element is the RHS. This
544*0fca6ea1SDimitry Andric   /// two-element list represents the fact that the LHS buffer gets its bounds
545*0fca6ea1SDimitry Andric   /// information from the RHS buffer. This information will be used later to
546*0fca6ea1SDimitry Andric   /// group all those variables whose types must be modified together to prevent
547*0fca6ea1SDimitry Andric   /// type mismatches.
54806c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
54906c3fb27SDimitry Andric   getStrategyImplications() const {
55006c3fb27SDimitry Andric     return std::nullopt;
55106c3fb27SDimitry Andric   }
552bdd1243dSDimitry Andric };
553bdd1243dSDimitry Andric 
554*0fca6ea1SDimitry Andric static auto toSupportedVariable() { return to(varDecl()); }
5555f757f3fSDimitry Andric 
556bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
557bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
558bdd1243dSDimitry Andric 
559bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer
560bdd1243dSDimitry Andric /// out of bounds.
561bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget {
562bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
563bdd1243dSDimitry Andric   const UnaryOperator *Op;
564bdd1243dSDimitry Andric 
565bdd1243dSDimitry Andric public:
566bdd1243dSDimitry Andric   IncrementGadget(const MatchFinder::MatchResult &Result)
567bdd1243dSDimitry Andric       : WarningGadget(Kind::Increment),
568bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
569bdd1243dSDimitry Andric 
570bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
571bdd1243dSDimitry Andric     return G->getKind() == Kind::Increment;
572bdd1243dSDimitry Andric   }
573bdd1243dSDimitry Andric 
574bdd1243dSDimitry Andric   static Matcher matcher() {
575*0fca6ea1SDimitry Andric     return stmt(
576*0fca6ea1SDimitry Andric         unaryOperator(hasOperatorName("++"),
577*0fca6ea1SDimitry Andric                       hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
578*0fca6ea1SDimitry Andric             .bind(OpTag));
579bdd1243dSDimitry Andric   }
580bdd1243dSDimitry Andric 
581*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
582*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
583*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
584*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
585*0fca6ea1SDimitry Andric   }
586*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
587bdd1243dSDimitry Andric 
588bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
589bdd1243dSDimitry Andric     SmallVector<const DeclRefExpr *, 2> Uses;
590bdd1243dSDimitry Andric     if (const auto *DRE =
591bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
592bdd1243dSDimitry Andric       Uses.push_back(DRE);
593bdd1243dSDimitry Andric     }
594bdd1243dSDimitry Andric 
595bdd1243dSDimitry Andric     return std::move(Uses);
596bdd1243dSDimitry Andric   }
597bdd1243dSDimitry Andric };
598bdd1243dSDimitry Andric 
599bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer
600bdd1243dSDimitry Andric /// out of bounds.
601bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget {
602bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
603bdd1243dSDimitry Andric   const UnaryOperator *Op;
604bdd1243dSDimitry Andric 
605bdd1243dSDimitry Andric public:
606bdd1243dSDimitry Andric   DecrementGadget(const MatchFinder::MatchResult &Result)
607bdd1243dSDimitry Andric       : WarningGadget(Kind::Decrement),
608bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
609bdd1243dSDimitry Andric 
610bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
611bdd1243dSDimitry Andric     return G->getKind() == Kind::Decrement;
612bdd1243dSDimitry Andric   }
613bdd1243dSDimitry Andric 
614bdd1243dSDimitry Andric   static Matcher matcher() {
615*0fca6ea1SDimitry Andric     return stmt(
616*0fca6ea1SDimitry Andric         unaryOperator(hasOperatorName("--"),
617*0fca6ea1SDimitry Andric                       hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
618*0fca6ea1SDimitry Andric             .bind(OpTag));
619bdd1243dSDimitry Andric   }
620bdd1243dSDimitry Andric 
621*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
622*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
623*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
624*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
625*0fca6ea1SDimitry Andric   }
626*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
627bdd1243dSDimitry Andric 
628bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
629bdd1243dSDimitry Andric     if (const auto *DRE =
630bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
631bdd1243dSDimitry Andric       return {DRE};
632bdd1243dSDimitry Andric     }
633bdd1243dSDimitry Andric 
634bdd1243dSDimitry Andric     return {};
635bdd1243dSDimitry Andric   }
636bdd1243dSDimitry Andric };
637bdd1243dSDimitry Andric 
638bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
639bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array.
640bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget {
64106c3fb27SDimitry Andric   static constexpr const char *const ArraySubscrTag = "ArraySubscript";
642bdd1243dSDimitry Andric   const ArraySubscriptExpr *ASE;
643bdd1243dSDimitry Andric 
644bdd1243dSDimitry Andric public:
645bdd1243dSDimitry Andric   ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
646bdd1243dSDimitry Andric       : WarningGadget(Kind::ArraySubscript),
647bdd1243dSDimitry Andric         ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
648bdd1243dSDimitry Andric 
649bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
650bdd1243dSDimitry Andric     return G->getKind() == Kind::ArraySubscript;
651bdd1243dSDimitry Andric   }
652bdd1243dSDimitry Andric 
653bdd1243dSDimitry Andric   static Matcher matcher() {
654bdd1243dSDimitry Andric     // clang-format off
655bdd1243dSDimitry Andric       return stmt(arraySubscriptExpr(
656bdd1243dSDimitry Andric             hasBase(ignoringParenImpCasts(
657bdd1243dSDimitry Andric               anyOf(hasPointerType(), hasArrayType()))),
658*0fca6ea1SDimitry Andric             unless(anyOf(
659*0fca6ea1SDimitry Andric               isSafeArraySubscript(),
660*0fca6ea1SDimitry Andric               hasIndex(
66106c3fb27SDimitry Andric                   anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
662*0fca6ea1SDimitry Andric               )
663*0fca6ea1SDimitry Andric             ))).bind(ArraySubscrTag));
664bdd1243dSDimitry Andric     // clang-format on
665bdd1243dSDimitry Andric   }
666bdd1243dSDimitry Andric 
667*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
668*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
669*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
670*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
671*0fca6ea1SDimitry Andric   }
672*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
673bdd1243dSDimitry Andric 
674bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
675bdd1243dSDimitry Andric     if (const auto *DRE =
676bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
677bdd1243dSDimitry Andric       return {DRE};
678bdd1243dSDimitry Andric     }
679bdd1243dSDimitry Andric 
680bdd1243dSDimitry Andric     return {};
681bdd1243dSDimitry Andric   }
682bdd1243dSDimitry Andric };
683bdd1243dSDimitry Andric 
684bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms:
685bdd1243dSDimitry Andric ///  \code
686bdd1243dSDimitry Andric ///  ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
687bdd1243dSDimitry Andric ///  \endcode
688bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget {
689bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticTag = "ptrAdd";
690bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
691bdd1243dSDimitry Andric   const BinaryOperator *PA; // pointer arithmetic expression
692bdd1243dSDimitry Andric   const Expr *Ptr;          // the pointer expression in `PA`
693bdd1243dSDimitry Andric 
694bdd1243dSDimitry Andric public:
695bdd1243dSDimitry Andric   PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
696bdd1243dSDimitry Andric       : WarningGadget(Kind::PointerArithmetic),
697bdd1243dSDimitry Andric         PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
698bdd1243dSDimitry Andric         Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
699bdd1243dSDimitry Andric 
700bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
701bdd1243dSDimitry Andric     return G->getKind() == Kind::PointerArithmetic;
702bdd1243dSDimitry Andric   }
703bdd1243dSDimitry Andric 
704bdd1243dSDimitry Andric   static Matcher matcher() {
70506c3fb27SDimitry Andric     auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
70606c3fb27SDimitry Andric     auto PtrAtRight =
70706c3fb27SDimitry Andric         allOf(hasOperatorName("+"),
708bdd1243dSDimitry Andric               hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
709bdd1243dSDimitry Andric               hasLHS(HasIntegerType));
71006c3fb27SDimitry Andric     auto PtrAtLeft =
71106c3fb27SDimitry Andric         allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
712bdd1243dSDimitry Andric                     hasOperatorName("+="), hasOperatorName("-=")),
713bdd1243dSDimitry Andric               hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
714bdd1243dSDimitry Andric               hasRHS(HasIntegerType));
715bdd1243dSDimitry Andric 
71606c3fb27SDimitry Andric     return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight))
71706c3fb27SDimitry Andric                     .bind(PointerArithmeticTag));
718bdd1243dSDimitry Andric   }
719bdd1243dSDimitry Andric 
720*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
721*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
722*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
723*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
724*0fca6ea1SDimitry Andric   }
725*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
726bdd1243dSDimitry Andric 
727bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
72806c3fb27SDimitry Andric     if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
729bdd1243dSDimitry Andric       return {DRE};
730bdd1243dSDimitry Andric     }
731bdd1243dSDimitry Andric 
732bdd1243dSDimitry Andric     return {};
733bdd1243dSDimitry Andric   }
734bdd1243dSDimitry Andric   // FIXME: pointer adding zero should be fine
735bdd1243dSDimitry Andric   // FIXME: this gadge will need a fix-it
736bdd1243dSDimitry Andric };
73706c3fb27SDimitry Andric 
738*0fca6ea1SDimitry Andric class SpanTwoParamConstructorGadget : public WarningGadget {
739*0fca6ea1SDimitry Andric   static constexpr const char *const SpanTwoParamConstructorTag =
740*0fca6ea1SDimitry Andric       "spanTwoParamConstructor";
741*0fca6ea1SDimitry Andric   const CXXConstructExpr *Ctor; // the span constructor expression
742*0fca6ea1SDimitry Andric 
743*0fca6ea1SDimitry Andric public:
744*0fca6ea1SDimitry Andric   SpanTwoParamConstructorGadget(const MatchFinder::MatchResult &Result)
745*0fca6ea1SDimitry Andric       : WarningGadget(Kind::SpanTwoParamConstructor),
746*0fca6ea1SDimitry Andric         Ctor(Result.Nodes.getNodeAs<CXXConstructExpr>(
747*0fca6ea1SDimitry Andric             SpanTwoParamConstructorTag)) {}
748*0fca6ea1SDimitry Andric 
749*0fca6ea1SDimitry Andric   static bool classof(const Gadget *G) {
750*0fca6ea1SDimitry Andric     return G->getKind() == Kind::SpanTwoParamConstructor;
751*0fca6ea1SDimitry Andric   }
752*0fca6ea1SDimitry Andric 
753*0fca6ea1SDimitry Andric   static Matcher matcher() {
754*0fca6ea1SDimitry Andric     auto HasTwoParamSpanCtorDecl = hasDeclaration(
755*0fca6ea1SDimitry Andric         cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"),
756*0fca6ea1SDimitry Andric                            parameterCountIs(2)));
757*0fca6ea1SDimitry Andric 
758*0fca6ea1SDimitry Andric     return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl,
759*0fca6ea1SDimitry Andric                                  unless(isSafeSpanTwoParamConstruct()))
760*0fca6ea1SDimitry Andric                     .bind(SpanTwoParamConstructorTag));
761*0fca6ea1SDimitry Andric   }
762*0fca6ea1SDimitry Andric 
763*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
764*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
765*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
766*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
767*0fca6ea1SDimitry Andric   }
768*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
769*0fca6ea1SDimitry Andric 
770*0fca6ea1SDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
771*0fca6ea1SDimitry Andric     // If the constructor call is of the form `std::span{var, n}`, `var` is
772*0fca6ea1SDimitry Andric     // considered an unsafe variable.
773*0fca6ea1SDimitry Andric     if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
774*0fca6ea1SDimitry Andric       if (isa<VarDecl>(DRE->getDecl()))
775*0fca6ea1SDimitry Andric         return {DRE};
776*0fca6ea1SDimitry Andric     }
777*0fca6ea1SDimitry Andric     return {};
778*0fca6ea1SDimitry Andric   }
779*0fca6ea1SDimitry Andric };
780*0fca6ea1SDimitry Andric 
78106c3fb27SDimitry Andric /// A pointer initialization expression of the form:
78206c3fb27SDimitry Andric ///  \code
78306c3fb27SDimitry Andric ///  int *p = q;
78406c3fb27SDimitry Andric ///  \endcode
78506c3fb27SDimitry Andric class PointerInitGadget : public FixableGadget {
78606c3fb27SDimitry Andric private:
78706c3fb27SDimitry Andric   static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
78806c3fb27SDimitry Andric   static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
78906c3fb27SDimitry Andric   const VarDecl *PtrInitLHS;     // the LHS pointer expression in `PI`
79006c3fb27SDimitry Andric   const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
79106c3fb27SDimitry Andric 
79206c3fb27SDimitry Andric public:
79306c3fb27SDimitry Andric   PointerInitGadget(const MatchFinder::MatchResult &Result)
79406c3fb27SDimitry Andric       : FixableGadget(Kind::PointerInit),
79506c3fb27SDimitry Andric         PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
79606c3fb27SDimitry Andric         PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
79706c3fb27SDimitry Andric 
79806c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
79906c3fb27SDimitry Andric     return G->getKind() == Kind::PointerInit;
80006c3fb27SDimitry Andric   }
80106c3fb27SDimitry Andric 
80206c3fb27SDimitry Andric   static Matcher matcher() {
803*0fca6ea1SDimitry Andric     auto PtrInitStmt = declStmt(hasSingleDecl(
804*0fca6ea1SDimitry Andric         varDecl(hasInitializer(ignoringImpCasts(
805*0fca6ea1SDimitry Andric                     declRefExpr(hasPointerType(), toSupportedVariable())
806*0fca6ea1SDimitry Andric                         .bind(PointerInitRHSTag))))
807*0fca6ea1SDimitry Andric             .bind(PointerInitLHSTag)));
80806c3fb27SDimitry Andric 
80906c3fb27SDimitry Andric     return stmt(PtrInitStmt);
81006c3fb27SDimitry Andric   }
81106c3fb27SDimitry Andric 
812*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
813*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
814*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override {
815*0fca6ea1SDimitry Andric     return PtrInitRHS->getBeginLoc();
8165f757f3fSDimitry Andric   }
81706c3fb27SDimitry Andric 
81806c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
81906c3fb27SDimitry Andric     return DeclUseList{PtrInitRHS};
82006c3fb27SDimitry Andric   }
82106c3fb27SDimitry Andric 
82206c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
82306c3fb27SDimitry Andric   getStrategyImplications() const override {
824*0fca6ea1SDimitry Andric     return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
82506c3fb27SDimitry Andric   }
82606c3fb27SDimitry Andric };
82706c3fb27SDimitry Andric 
82806c3fb27SDimitry Andric /// A pointer assignment expression of the form:
82906c3fb27SDimitry Andric ///  \code
83006c3fb27SDimitry Andric ///  p = q;
83106c3fb27SDimitry Andric ///  \endcode
832*0fca6ea1SDimitry Andric /// where both `p` and `q` are pointers.
833*0fca6ea1SDimitry Andric class PtrToPtrAssignmentGadget : public FixableGadget {
83406c3fb27SDimitry Andric private:
83506c3fb27SDimitry Andric   static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
83606c3fb27SDimitry Andric   static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
83706c3fb27SDimitry Andric   const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
83806c3fb27SDimitry Andric   const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
83906c3fb27SDimitry Andric 
84006c3fb27SDimitry Andric public:
841*0fca6ea1SDimitry Andric   PtrToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
842*0fca6ea1SDimitry Andric       : FixableGadget(Kind::PtrToPtrAssignment),
84306c3fb27SDimitry Andric         PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
84406c3fb27SDimitry Andric         PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
84506c3fb27SDimitry Andric 
84606c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
847*0fca6ea1SDimitry Andric     return G->getKind() == Kind::PtrToPtrAssignment;
84806c3fb27SDimitry Andric   }
84906c3fb27SDimitry Andric 
85006c3fb27SDimitry Andric   static Matcher matcher() {
851*0fca6ea1SDimitry Andric     auto PtrAssignExpr = binaryOperator(
852*0fca6ea1SDimitry Andric         allOf(hasOperatorName("="),
853*0fca6ea1SDimitry Andric               hasRHS(ignoringParenImpCasts(
854*0fca6ea1SDimitry Andric                   declRefExpr(hasPointerType(), toSupportedVariable())
855*0fca6ea1SDimitry Andric                       .bind(PointerAssignRHSTag))),
856*0fca6ea1SDimitry Andric               hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
857*0fca6ea1SDimitry Andric                          .bind(PointerAssignLHSTag))));
85806c3fb27SDimitry Andric 
85906c3fb27SDimitry Andric     return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
86006c3fb27SDimitry Andric   }
86106c3fb27SDimitry Andric 
862*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
863*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
864*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
86506c3fb27SDimitry Andric 
86606c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
86706c3fb27SDimitry Andric     return DeclUseList{PtrLHS, PtrRHS};
86806c3fb27SDimitry Andric   }
86906c3fb27SDimitry Andric 
87006c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
87106c3fb27SDimitry Andric   getStrategyImplications() const override {
87206c3fb27SDimitry Andric     return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
87306c3fb27SDimitry Andric                           cast<VarDecl>(PtrRHS->getDecl()));
87406c3fb27SDimitry Andric   }
87506c3fb27SDimitry Andric };
87606c3fb27SDimitry Andric 
877*0fca6ea1SDimitry Andric /// An assignment expression of the form:
878*0fca6ea1SDimitry Andric ///  \code
879*0fca6ea1SDimitry Andric ///  ptr = array;
880*0fca6ea1SDimitry Andric ///  \endcode
881*0fca6ea1SDimitry Andric /// where `p` is a pointer and `array` is a constant size array.
882*0fca6ea1SDimitry Andric class CArrayToPtrAssignmentGadget : public FixableGadget {
883*0fca6ea1SDimitry Andric private:
884*0fca6ea1SDimitry Andric   static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
885*0fca6ea1SDimitry Andric   static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
886*0fca6ea1SDimitry Andric   const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
887*0fca6ea1SDimitry Andric   const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
888*0fca6ea1SDimitry Andric 
889*0fca6ea1SDimitry Andric public:
890*0fca6ea1SDimitry Andric   CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
891*0fca6ea1SDimitry Andric       : FixableGadget(Kind::CArrayToPtrAssignment),
892*0fca6ea1SDimitry Andric         PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
893*0fca6ea1SDimitry Andric         PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
894*0fca6ea1SDimitry Andric 
895*0fca6ea1SDimitry Andric   static bool classof(const Gadget *G) {
896*0fca6ea1SDimitry Andric     return G->getKind() == Kind::CArrayToPtrAssignment;
897*0fca6ea1SDimitry Andric   }
898*0fca6ea1SDimitry Andric 
899*0fca6ea1SDimitry Andric   static Matcher matcher() {
900*0fca6ea1SDimitry Andric     auto PtrAssignExpr = binaryOperator(
901*0fca6ea1SDimitry Andric         allOf(hasOperatorName("="),
902*0fca6ea1SDimitry Andric               hasRHS(ignoringParenImpCasts(
903*0fca6ea1SDimitry Andric                   declRefExpr(hasType(hasCanonicalType(constantArrayType())),
904*0fca6ea1SDimitry Andric                               toSupportedVariable())
905*0fca6ea1SDimitry Andric                       .bind(PointerAssignRHSTag))),
906*0fca6ea1SDimitry Andric               hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
907*0fca6ea1SDimitry Andric                          .bind(PointerAssignLHSTag))));
908*0fca6ea1SDimitry Andric 
909*0fca6ea1SDimitry Andric     return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
910*0fca6ea1SDimitry Andric   }
911*0fca6ea1SDimitry Andric 
912*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
913*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
914*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
915*0fca6ea1SDimitry Andric 
916*0fca6ea1SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
917*0fca6ea1SDimitry Andric     return DeclUseList{PtrLHS, PtrRHS};
918*0fca6ea1SDimitry Andric   }
919*0fca6ea1SDimitry Andric 
920*0fca6ea1SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
921*0fca6ea1SDimitry Andric   getStrategyImplications() const override {
922*0fca6ea1SDimitry Andric     return {};
923*0fca6ea1SDimitry Andric   }
924*0fca6ea1SDimitry Andric };
925*0fca6ea1SDimitry Andric 
92606c3fb27SDimitry Andric /// A call of a function or method that performs unchecked buffer operations
92706c3fb27SDimitry Andric /// over one of its pointer parameters.
92806c3fb27SDimitry Andric class UnsafeBufferUsageAttrGadget : public WarningGadget {
92906c3fb27SDimitry Andric   constexpr static const char *const OpTag = "call_expr";
93006c3fb27SDimitry Andric   const CallExpr *Op;
93106c3fb27SDimitry Andric 
93206c3fb27SDimitry Andric public:
93306c3fb27SDimitry Andric   UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
93406c3fb27SDimitry Andric       : WarningGadget(Kind::UnsafeBufferUsageAttr),
93506c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {}
93606c3fb27SDimitry Andric 
93706c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
93806c3fb27SDimitry Andric     return G->getKind() == Kind::UnsafeBufferUsageAttr;
93906c3fb27SDimitry Andric   }
94006c3fb27SDimitry Andric 
94106c3fb27SDimitry Andric   static Matcher matcher() {
942*0fca6ea1SDimitry Andric     auto HasUnsafeFnDecl =
943*0fca6ea1SDimitry Andric         callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)));
944*0fca6ea1SDimitry Andric     return stmt(callExpr(HasUnsafeFnDecl).bind(OpTag));
945*0fca6ea1SDimitry Andric   }
946*0fca6ea1SDimitry Andric 
947*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
948*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
949*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
950*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
951*0fca6ea1SDimitry Andric   }
952*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
953*0fca6ea1SDimitry Andric 
954*0fca6ea1SDimitry Andric   DeclUseList getClaimedVarUseSites() const override { return {}; }
955*0fca6ea1SDimitry Andric };
956*0fca6ea1SDimitry Andric 
957*0fca6ea1SDimitry Andric /// A call of a constructor that performs unchecked buffer operations
958*0fca6ea1SDimitry Andric /// over one of its pointer parameters, or constructs a class object that will
959*0fca6ea1SDimitry Andric /// perform buffer operations that depend on the correctness of the parameters.
960*0fca6ea1SDimitry Andric class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
961*0fca6ea1SDimitry Andric   constexpr static const char *const OpTag = "cxx_construct_expr";
962*0fca6ea1SDimitry Andric   const CXXConstructExpr *Op;
963*0fca6ea1SDimitry Andric 
964*0fca6ea1SDimitry Andric public:
965*0fca6ea1SDimitry Andric   UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult &Result)
966*0fca6ea1SDimitry Andric       : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
967*0fca6ea1SDimitry Andric         Op(Result.Nodes.getNodeAs<CXXConstructExpr>(OpTag)) {}
968*0fca6ea1SDimitry Andric 
969*0fca6ea1SDimitry Andric   static bool classof(const Gadget *G) {
970*0fca6ea1SDimitry Andric     return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
971*0fca6ea1SDimitry Andric   }
972*0fca6ea1SDimitry Andric 
973*0fca6ea1SDimitry Andric   static Matcher matcher() {
974*0fca6ea1SDimitry Andric     auto HasUnsafeCtorDecl =
975*0fca6ea1SDimitry Andric         hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage)));
976*0fca6ea1SDimitry Andric     // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
977*0fca6ea1SDimitry Andric     auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();
978*0fca6ea1SDimitry Andric     return stmt(
979*0fca6ea1SDimitry Andric         cxxConstructExpr(HasUnsafeCtorDecl, unless(HasTwoParamSpanCtorDecl))
98006c3fb27SDimitry Andric             .bind(OpTag));
98106c3fb27SDimitry Andric   }
982*0fca6ea1SDimitry Andric 
983*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
984*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
985*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
986*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
987*0fca6ea1SDimitry Andric   }
988*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
98906c3fb27SDimitry Andric 
99006c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override { return {}; }
99106c3fb27SDimitry Andric };
99206c3fb27SDimitry Andric 
9931db9f3b2SDimitry Andric // Warning gadget for unsafe invocation of span::data method.
9941db9f3b2SDimitry Andric // Triggers when the pointer returned by the invocation is immediately
9951db9f3b2SDimitry Andric // cast to a larger type.
9961db9f3b2SDimitry Andric 
9971db9f3b2SDimitry Andric class DataInvocationGadget : public WarningGadget {
9981db9f3b2SDimitry Andric   constexpr static const char *const OpTag = "data_invocation_expr";
9991db9f3b2SDimitry Andric   const ExplicitCastExpr *Op;
10001db9f3b2SDimitry Andric 
10011db9f3b2SDimitry Andric public:
10021db9f3b2SDimitry Andric   DataInvocationGadget(const MatchFinder::MatchResult &Result)
10031db9f3b2SDimitry Andric       : WarningGadget(Kind::DataInvocation),
10041db9f3b2SDimitry Andric         Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {}
10051db9f3b2SDimitry Andric 
10061db9f3b2SDimitry Andric   static bool classof(const Gadget *G) {
10071db9f3b2SDimitry Andric     return G->getKind() == Kind::DataInvocation;
10081db9f3b2SDimitry Andric   }
10091db9f3b2SDimitry Andric 
10101db9f3b2SDimitry Andric   static Matcher matcher() {
10117a6dacacSDimitry Andric     Matcher callExpr = cxxMemberCallExpr(
10127a6dacacSDimitry Andric         callee(cxxMethodDecl(hasName("data"), ofClass(hasName("std::span")))));
10131db9f3b2SDimitry Andric     return stmt(
10147a6dacacSDimitry Andric         explicitCastExpr(anyOf(has(callExpr), has(parenExpr(has(callExpr)))))
10151db9f3b2SDimitry Andric             .bind(OpTag));
10161db9f3b2SDimitry Andric   }
1017*0fca6ea1SDimitry Andric 
1018*0fca6ea1SDimitry Andric   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1019*0fca6ea1SDimitry Andric                              bool IsRelatedToDecl,
1020*0fca6ea1SDimitry Andric                              ASTContext &Ctx) const override {
1021*0fca6ea1SDimitry Andric     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1022*0fca6ea1SDimitry Andric   }
1023*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
10241db9f3b2SDimitry Andric 
10251db9f3b2SDimitry Andric   DeclUseList getClaimedVarUseSites() const override { return {}; }
10261db9f3b2SDimitry Andric };
10271db9f3b2SDimitry Andric 
102806c3fb27SDimitry Andric // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
102906c3fb27SDimitry Andric // Context (see `isInUnspecifiedLvalueContext`).
103006c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
103106c3fb27SDimitry Andric class ULCArraySubscriptGadget : public FixableGadget {
103206c3fb27SDimitry Andric private:
103306c3fb27SDimitry Andric   static constexpr const char *const ULCArraySubscriptTag =
103406c3fb27SDimitry Andric       "ArraySubscriptUnderULC";
103506c3fb27SDimitry Andric   const ArraySubscriptExpr *Node;
103606c3fb27SDimitry Andric 
103706c3fb27SDimitry Andric public:
103806c3fb27SDimitry Andric   ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
103906c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
104006c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
104106c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
104206c3fb27SDimitry Andric   }
104306c3fb27SDimitry Andric 
104406c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
104506c3fb27SDimitry Andric     return G->getKind() == Kind::ULCArraySubscript;
104606c3fb27SDimitry Andric   }
104706c3fb27SDimitry Andric 
104806c3fb27SDimitry Andric   static Matcher matcher() {
104906c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1050*0fca6ea1SDimitry Andric     auto BaseIsArrayOrPtrDRE = hasBase(
1051*0fca6ea1SDimitry Andric         ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));
105206c3fb27SDimitry Andric     auto Target =
105306c3fb27SDimitry Andric         arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
105406c3fb27SDimitry Andric 
105506c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
105606c3fb27SDimitry Andric   }
105706c3fb27SDimitry Andric 
1058*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
1059*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
1060*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
106106c3fb27SDimitry Andric 
106206c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
106306c3fb27SDimitry Andric     if (const auto *DRE =
106406c3fb27SDimitry Andric             dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
106506c3fb27SDimitry Andric       return {DRE};
106606c3fb27SDimitry Andric     }
106706c3fb27SDimitry Andric     return {};
106806c3fb27SDimitry Andric   }
106906c3fb27SDimitry Andric };
107006c3fb27SDimitry Andric 
107106c3fb27SDimitry Andric // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
107206c3fb27SDimitry Andric // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
107306c3fb27SDimitry Andric // fixit of the form `UPC(DRE.data())`.
107406c3fb27SDimitry Andric class UPCStandalonePointerGadget : public FixableGadget {
107506c3fb27SDimitry Andric private:
107606c3fb27SDimitry Andric   static constexpr const char *const DeclRefExprTag = "StandalonePointer";
107706c3fb27SDimitry Andric   const DeclRefExpr *Node;
107806c3fb27SDimitry Andric 
107906c3fb27SDimitry Andric public:
108006c3fb27SDimitry Andric   UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
108106c3fb27SDimitry Andric       : FixableGadget(Kind::UPCStandalonePointer),
108206c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
108306c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
108406c3fb27SDimitry Andric   }
108506c3fb27SDimitry Andric 
108606c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
108706c3fb27SDimitry Andric     return G->getKind() == Kind::UPCStandalonePointer;
108806c3fb27SDimitry Andric   }
108906c3fb27SDimitry Andric 
109006c3fb27SDimitry Andric   static Matcher matcher() {
109106c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1092*0fca6ea1SDimitry Andric     auto target = expr(ignoringParenImpCasts(
1093*0fca6ea1SDimitry Andric         declRefExpr(allOf(ArrayOrPtr, toSupportedVariable()))
1094*0fca6ea1SDimitry Andric             .bind(DeclRefExprTag)));
109506c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(target));
109606c3fb27SDimitry Andric   }
109706c3fb27SDimitry Andric 
1098*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
1099*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
1100*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
110106c3fb27SDimitry Andric 
1102*0fca6ea1SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
110306c3fb27SDimitry Andric };
110406c3fb27SDimitry Andric 
110506c3fb27SDimitry Andric class PointerDereferenceGadget : public FixableGadget {
110606c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
110706c3fb27SDimitry Andric   static constexpr const char *const OperatorTag = "op";
110806c3fb27SDimitry Andric 
110906c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
111006c3fb27SDimitry Andric   const UnaryOperator *Op = nullptr;
111106c3fb27SDimitry Andric 
111206c3fb27SDimitry Andric public:
111306c3fb27SDimitry Andric   PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
111406c3fb27SDimitry Andric       : FixableGadget(Kind::PointerDereference),
111506c3fb27SDimitry Andric         BaseDeclRefExpr(
111606c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
111706c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
111806c3fb27SDimitry Andric 
111906c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
112006c3fb27SDimitry Andric     return G->getKind() == Kind::PointerDereference;
112106c3fb27SDimitry Andric   }
112206c3fb27SDimitry Andric 
112306c3fb27SDimitry Andric   static Matcher matcher() {
112406c3fb27SDimitry Andric     auto Target =
112506c3fb27SDimitry Andric         unaryOperator(
112606c3fb27SDimitry Andric             hasOperatorName("*"),
112706c3fb27SDimitry Andric             has(expr(ignoringParenImpCasts(
11285f757f3fSDimitry Andric                 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
112906c3fb27SDimitry Andric             .bind(OperatorTag);
113006c3fb27SDimitry Andric 
113106c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
113206c3fb27SDimitry Andric   }
113306c3fb27SDimitry Andric 
113406c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
113506c3fb27SDimitry Andric     return {BaseDeclRefExpr};
113606c3fb27SDimitry Andric   }
113706c3fb27SDimitry Andric 
1138*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
1139*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
1140*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
114106c3fb27SDimitry Andric };
114206c3fb27SDimitry Andric 
114306c3fb27SDimitry Andric // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
114406c3fb27SDimitry Andric // Context (see `isInUnspecifiedPointerContext`).
114506c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
114606c3fb27SDimitry Andric class UPCAddressofArraySubscriptGadget : public FixableGadget {
114706c3fb27SDimitry Andric private:
114806c3fb27SDimitry Andric   static constexpr const char *const UPCAddressofArraySubscriptTag =
114906c3fb27SDimitry Andric       "AddressofArraySubscriptUnderUPC";
115006c3fb27SDimitry Andric   const UnaryOperator *Node; // the `&DRE[any]` node
115106c3fb27SDimitry Andric 
115206c3fb27SDimitry Andric public:
115306c3fb27SDimitry Andric   UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
115406c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
115506c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<UnaryOperator>(
115606c3fb27SDimitry Andric             UPCAddressofArraySubscriptTag)) {
115706c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
115806c3fb27SDimitry Andric   }
115906c3fb27SDimitry Andric 
116006c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
116106c3fb27SDimitry Andric     return G->getKind() == Kind::UPCAddressofArraySubscript;
116206c3fb27SDimitry Andric   }
116306c3fb27SDimitry Andric 
116406c3fb27SDimitry Andric   static Matcher matcher() {
116506c3fb27SDimitry Andric     return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1166*0fca6ea1SDimitry Andric         unaryOperator(
1167*0fca6ea1SDimitry Andric             hasOperatorName("&"),
1168*0fca6ea1SDimitry Andric             hasUnaryOperand(arraySubscriptExpr(hasBase(
1169*0fca6ea1SDimitry Andric                 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
117006c3fb27SDimitry Andric             .bind(UPCAddressofArraySubscriptTag)))));
117106c3fb27SDimitry Andric   }
117206c3fb27SDimitry Andric 
1173*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
1174*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &) const override;
1175*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
117606c3fb27SDimitry Andric 
117706c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
117806c3fb27SDimitry Andric     const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
117906c3fb27SDimitry Andric     const auto *DRE =
1180*0fca6ea1SDimitry Andric         cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
118106c3fb27SDimitry Andric     return {DRE};
118206c3fb27SDimitry Andric   }
118306c3fb27SDimitry Andric };
1184bdd1243dSDimitry Andric } // namespace
1185bdd1243dSDimitry Andric 
1186bdd1243dSDimitry Andric namespace {
1187bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect
118806c3fb27SDimitry Andric // declarations to its uses and make sure we've covered all uses with our
118906c3fb27SDimitry Andric // analysis before we try to fix the declaration.
1190bdd1243dSDimitry Andric class DeclUseTracker {
1191bdd1243dSDimitry Andric   using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
1192bdd1243dSDimitry Andric   using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
1193bdd1243dSDimitry Andric 
1194bdd1243dSDimitry Andric   // Allocate on the heap for easier move.
1195bdd1243dSDimitry Andric   std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
1196bdd1243dSDimitry Andric   DefMapTy Defs{};
1197bdd1243dSDimitry Andric 
1198bdd1243dSDimitry Andric public:
1199bdd1243dSDimitry Andric   DeclUseTracker() = default;
1200bdd1243dSDimitry Andric   DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
120106c3fb27SDimitry Andric   DeclUseTracker &operator=(const DeclUseTracker &) = delete;
1202bdd1243dSDimitry Andric   DeclUseTracker(DeclUseTracker &&) = default;
1203bdd1243dSDimitry Andric   DeclUseTracker &operator=(DeclUseTracker &&) = default;
1204bdd1243dSDimitry Andric 
1205bdd1243dSDimitry Andric   // Start tracking a freshly discovered DRE.
1206bdd1243dSDimitry Andric   void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
1207bdd1243dSDimitry Andric 
1208bdd1243dSDimitry Andric   // Stop tracking the DRE as it's been fully figured out.
1209bdd1243dSDimitry Andric   void claimUse(const DeclRefExpr *DRE) {
1210bdd1243dSDimitry Andric     assert(Uses->count(DRE) &&
1211bdd1243dSDimitry Andric            "DRE not found or claimed by multiple matchers!");
1212bdd1243dSDimitry Andric     Uses->erase(DRE);
1213bdd1243dSDimitry Andric   }
1214bdd1243dSDimitry Andric 
1215bdd1243dSDimitry Andric   // A variable is unclaimed if at least one use is unclaimed.
1216bdd1243dSDimitry Andric   bool hasUnclaimedUses(const VarDecl *VD) const {
1217bdd1243dSDimitry Andric     // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
1218bdd1243dSDimitry Andric     return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
1219bdd1243dSDimitry Andric       return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
1220bdd1243dSDimitry Andric     });
1221bdd1243dSDimitry Andric   }
1222bdd1243dSDimitry Andric 
12235f757f3fSDimitry Andric   UseSetTy getUnclaimedUses(const VarDecl *VD) const {
12245f757f3fSDimitry Andric     UseSetTy ReturnSet;
12255f757f3fSDimitry Andric     for (auto use : *Uses) {
12265f757f3fSDimitry Andric       if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
12275f757f3fSDimitry Andric         ReturnSet.insert(use);
12285f757f3fSDimitry Andric       }
12295f757f3fSDimitry Andric     }
12305f757f3fSDimitry Andric     return ReturnSet;
12315f757f3fSDimitry Andric   }
12325f757f3fSDimitry Andric 
1233bdd1243dSDimitry Andric   void discoverDecl(const DeclStmt *DS) {
1234bdd1243dSDimitry Andric     for (const Decl *D : DS->decls()) {
1235bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(D)) {
1236bdd1243dSDimitry Andric         // FIXME: Assertion temporarily disabled due to a bug in
1237bdd1243dSDimitry Andric         // ASTMatcher internal behavior in presence of GNU
1238bdd1243dSDimitry Andric         // statement-expressions. We need to properly investigate this
1239bdd1243dSDimitry Andric         // because it can screw up our algorithm in other ways.
1240bdd1243dSDimitry Andric         // assert(Defs.count(VD) == 0 && "Definition already discovered!");
1241bdd1243dSDimitry Andric         Defs[VD] = DS;
1242bdd1243dSDimitry Andric       }
1243bdd1243dSDimitry Andric     }
1244bdd1243dSDimitry Andric   }
1245bdd1243dSDimitry Andric 
1246bdd1243dSDimitry Andric   const DeclStmt *lookupDecl(const VarDecl *VD) const {
12475f757f3fSDimitry Andric     return Defs.lookup(VD);
1248bdd1243dSDimitry Andric   }
1249bdd1243dSDimitry Andric };
1250bdd1243dSDimitry Andric } // namespace
1251bdd1243dSDimitry Andric 
125206c3fb27SDimitry Andric // Representing a pointer type expression of the form `++Ptr` in an Unspecified
125306c3fb27SDimitry Andric // Pointer Context (UPC):
125406c3fb27SDimitry Andric class UPCPreIncrementGadget : public FixableGadget {
125506c3fb27SDimitry Andric private:
125606c3fb27SDimitry Andric   static constexpr const char *const UPCPreIncrementTag =
125706c3fb27SDimitry Andric       "PointerPreIncrementUnderUPC";
125806c3fb27SDimitry Andric   const UnaryOperator *Node; // the `++Ptr` node
125906c3fb27SDimitry Andric 
126006c3fb27SDimitry Andric public:
126106c3fb27SDimitry Andric   UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
126206c3fb27SDimitry Andric       : FixableGadget(Kind::UPCPreIncrement),
126306c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
126406c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
126506c3fb27SDimitry Andric   }
126606c3fb27SDimitry Andric 
126706c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
126806c3fb27SDimitry Andric     return G->getKind() == Kind::UPCPreIncrement;
126906c3fb27SDimitry Andric   }
127006c3fb27SDimitry Andric 
127106c3fb27SDimitry Andric   static Matcher matcher() {
127206c3fb27SDimitry Andric     // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
127306c3fb27SDimitry Andric     // Although currently we can only provide fix-its when `Ptr` is a DRE, we
127406c3fb27SDimitry Andric     // can have the matcher be general, so long as `getClaimedVarUseSites` does
127506c3fb27SDimitry Andric     // things right.
127606c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
127706c3fb27SDimitry Andric         unaryOperator(isPreInc(),
1278*0fca6ea1SDimitry Andric                       hasUnaryOperand(declRefExpr(toSupportedVariable())))
1279*0fca6ea1SDimitry Andric             .bind(UPCPreIncrementTag)))));
128006c3fb27SDimitry Andric   }
128106c3fb27SDimitry Andric 
1282*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
1283*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
1284*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
128506c3fb27SDimitry Andric 
128606c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
128706c3fb27SDimitry Andric     return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
128806c3fb27SDimitry Andric   }
128906c3fb27SDimitry Andric };
129006c3fb27SDimitry Andric 
12915f757f3fSDimitry Andric // Representing a pointer type expression of the form `Ptr += n` in an
12925f757f3fSDimitry Andric // Unspecified Untyped Context (UUC):
12935f757f3fSDimitry Andric class UUCAddAssignGadget : public FixableGadget {
12945f757f3fSDimitry Andric private:
12955f757f3fSDimitry Andric   static constexpr const char *const UUCAddAssignTag =
12965f757f3fSDimitry Andric       "PointerAddAssignUnderUUC";
12975f757f3fSDimitry Andric   static constexpr const char *const OffsetTag = "Offset";
12985f757f3fSDimitry Andric 
12995f757f3fSDimitry Andric   const BinaryOperator *Node; // the `Ptr += n` node
13005f757f3fSDimitry Andric   const Expr *Offset = nullptr;
13015f757f3fSDimitry Andric 
13025f757f3fSDimitry Andric public:
13035f757f3fSDimitry Andric   UUCAddAssignGadget(const MatchFinder::MatchResult &Result)
13045f757f3fSDimitry Andric       : FixableGadget(Kind::UUCAddAssign),
13055f757f3fSDimitry Andric         Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
13065f757f3fSDimitry Andric         Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
13075f757f3fSDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
13085f757f3fSDimitry Andric   }
13095f757f3fSDimitry Andric 
13105f757f3fSDimitry Andric   static bool classof(const Gadget *G) {
13115f757f3fSDimitry Andric     return G->getKind() == Kind::UUCAddAssign;
13125f757f3fSDimitry Andric   }
13135f757f3fSDimitry Andric 
13145f757f3fSDimitry Andric   static Matcher matcher() {
1315*0fca6ea1SDimitry Andric     // clang-format off
13165f757f3fSDimitry Andric     return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
13175f757f3fSDimitry Andric         binaryOperator(hasOperatorName("+="),
1318*0fca6ea1SDimitry Andric                        hasLHS(
1319*0fca6ea1SDimitry Andric                         declRefExpr(
1320*0fca6ea1SDimitry Andric                           hasPointerType(),
1321*0fca6ea1SDimitry Andric                           toSupportedVariable())),
13225f757f3fSDimitry Andric                        hasRHS(expr().bind(OffsetTag)))
13235f757f3fSDimitry Andric             .bind(UUCAddAssignTag)))));
1324*0fca6ea1SDimitry Andric     // clang-format on
13255f757f3fSDimitry Andric   }
13265f757f3fSDimitry Andric 
1327*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
1328*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &S) const override;
1329*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
13305f757f3fSDimitry Andric 
13315f757f3fSDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
13325f757f3fSDimitry Andric     return {dyn_cast<DeclRefExpr>(Node->getLHS())};
13335f757f3fSDimitry Andric   }
13345f757f3fSDimitry Andric };
13355f757f3fSDimitry Andric 
133606c3fb27SDimitry Andric // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
133706c3fb27SDimitry Andric // ptr)`:
133806c3fb27SDimitry Andric class DerefSimplePtrArithFixableGadget : public FixableGadget {
133906c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
134006c3fb27SDimitry Andric   static constexpr const char *const DerefOpTag = "DerefOp";
134106c3fb27SDimitry Andric   static constexpr const char *const AddOpTag = "AddOp";
134206c3fb27SDimitry Andric   static constexpr const char *const OffsetTag = "Offset";
134306c3fb27SDimitry Andric 
134406c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
134506c3fb27SDimitry Andric   const UnaryOperator *DerefOp = nullptr;
134606c3fb27SDimitry Andric   const BinaryOperator *AddOp = nullptr;
134706c3fb27SDimitry Andric   const IntegerLiteral *Offset = nullptr;
134806c3fb27SDimitry Andric 
134906c3fb27SDimitry Andric public:
135006c3fb27SDimitry Andric   DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)
135106c3fb27SDimitry Andric       : FixableGadget(Kind::DerefSimplePtrArithFixable),
135206c3fb27SDimitry Andric         BaseDeclRefExpr(
135306c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
135406c3fb27SDimitry Andric         DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
135506c3fb27SDimitry Andric         AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
135606c3fb27SDimitry Andric         Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
135706c3fb27SDimitry Andric 
135806c3fb27SDimitry Andric   static Matcher matcher() {
135906c3fb27SDimitry Andric     // clang-format off
136006c3fb27SDimitry Andric     auto ThePtr = expr(hasPointerType(),
13615f757f3fSDimitry Andric                        ignoringImpCasts(declRefExpr(toSupportedVariable()).
13625f757f3fSDimitry Andric                                         bind(BaseDeclRefExprTag)));
136306c3fb27SDimitry Andric     auto PlusOverPtrAndInteger = expr(anyOf(
136406c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
136506c3fb27SDimitry Andric                          hasRHS(integerLiteral().bind(OffsetTag)))
136606c3fb27SDimitry Andric                          .bind(AddOpTag),
136706c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
136806c3fb27SDimitry Andric                          hasLHS(integerLiteral().bind(OffsetTag)))
136906c3fb27SDimitry Andric                          .bind(AddOpTag)));
137006c3fb27SDimitry Andric     return isInUnspecifiedLvalueContext(unaryOperator(
137106c3fb27SDimitry Andric         hasOperatorName("*"),
137206c3fb27SDimitry Andric         hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
137306c3fb27SDimitry Andric         .bind(DerefOpTag));
137406c3fb27SDimitry Andric     // clang-format on
137506c3fb27SDimitry Andric   }
137606c3fb27SDimitry Andric 
1377*0fca6ea1SDimitry Andric   virtual std::optional<FixItList>
1378*0fca6ea1SDimitry Andric   getFixits(const FixitStrategy &s) const final;
1379*0fca6ea1SDimitry Andric   SourceLocation getSourceLoc() const override {
1380*0fca6ea1SDimitry Andric     return DerefOp->getBeginLoc();
1381*0fca6ea1SDimitry Andric   }
138206c3fb27SDimitry Andric 
138306c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const final {
138406c3fb27SDimitry Andric     return {BaseDeclRefExpr};
138506c3fb27SDimitry Andric   }
138606c3fb27SDimitry Andric };
138706c3fb27SDimitry Andric 
1388bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits.
138906c3fb27SDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
139006c3fb27SDimitry Andric findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
139106c3fb27SDimitry Andric             bool EmitSuggestions) {
1392bdd1243dSDimitry Andric 
1393bdd1243dSDimitry Andric   struct GadgetFinderCallback : MatchFinder::MatchCallback {
1394bdd1243dSDimitry Andric     FixableGadgetList FixableGadgets;
1395bdd1243dSDimitry Andric     WarningGadgetList WarningGadgets;
1396bdd1243dSDimitry Andric     DeclUseTracker Tracker;
1397bdd1243dSDimitry Andric 
1398bdd1243dSDimitry Andric     void run(const MatchFinder::MatchResult &Result) override {
1399bdd1243dSDimitry Andric       // In debug mode, assert that we've found exactly one gadget.
1400bdd1243dSDimitry Andric       // This helps us avoid conflicts in .bind() tags.
1401bdd1243dSDimitry Andric #if NDEBUG
1402bdd1243dSDimitry Andric #define NEXT return
1403bdd1243dSDimitry Andric #else
1404bdd1243dSDimitry Andric       [[maybe_unused]] int numFound = 0;
1405bdd1243dSDimitry Andric #define NEXT ++numFound
1406bdd1243dSDimitry Andric #endif
1407bdd1243dSDimitry Andric 
1408bdd1243dSDimitry Andric       if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
1409bdd1243dSDimitry Andric         Tracker.discoverUse(DRE);
1410bdd1243dSDimitry Andric         NEXT;
1411bdd1243dSDimitry Andric       }
1412bdd1243dSDimitry Andric 
1413bdd1243dSDimitry Andric       if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
1414bdd1243dSDimitry Andric         Tracker.discoverDecl(DS);
1415bdd1243dSDimitry Andric         NEXT;
1416bdd1243dSDimitry Andric       }
1417bdd1243dSDimitry Andric 
1418bdd1243dSDimitry Andric       // Figure out which matcher we've found, and call the appropriate
1419bdd1243dSDimitry Andric       // subclass constructor.
1420bdd1243dSDimitry Andric       // FIXME: Can we do this more logarithmically?
1421bdd1243dSDimitry Andric #define FIXABLE_GADGET(name)                                                   \
1422bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1423bdd1243dSDimitry Andric     FixableGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1424bdd1243dSDimitry Andric     NEXT;                                                                      \
1425bdd1243dSDimitry Andric   }
1426bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1427bdd1243dSDimitry Andric #define WARNING_GADGET(name)                                                   \
1428bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1429bdd1243dSDimitry Andric     WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1430bdd1243dSDimitry Andric     NEXT;                                                                      \
1431bdd1243dSDimitry Andric   }
1432bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1433bdd1243dSDimitry Andric 
1434bdd1243dSDimitry Andric       assert(numFound >= 1 && "Gadgets not found in match result!");
1435bdd1243dSDimitry Andric       assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
1436bdd1243dSDimitry Andric     }
1437bdd1243dSDimitry Andric   };
1438bdd1243dSDimitry Andric 
1439bdd1243dSDimitry Andric   MatchFinder M;
1440bdd1243dSDimitry Andric   GadgetFinderCallback CB;
1441bdd1243dSDimitry Andric 
1442bdd1243dSDimitry Andric   // clang-format off
1443bdd1243dSDimitry Andric   M.addMatcher(
144406c3fb27SDimitry Andric       stmt(
144506c3fb27SDimitry Andric         forEachDescendantEvaluatedStmt(stmt(anyOf(
1446bdd1243dSDimitry Andric           // Add Gadget::matcher() for every gadget in the registry.
144706c3fb27SDimitry Andric #define WARNING_GADGET(x)                                                      \
144806c3fb27SDimitry Andric           allOf(x ## Gadget::matcher().bind(#x),                               \
144906c3fb27SDimitry Andric                 notInSafeBufferOptOut(&Handler)),
1450*0fca6ea1SDimitry Andric #define WARNING_CONTAINER_GADGET(x)                                            \
1451*0fca6ea1SDimitry Andric           allOf(x ## Gadget::matcher().bind(#x),                               \
1452*0fca6ea1SDimitry Andric                 notInSafeBufferOptOut(&Handler),                               \
1453*0fca6ea1SDimitry Andric                 unless(ignoreUnsafeBufferInContainer(&Handler))),
145406c3fb27SDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
145506c3fb27SDimitry Andric             // Avoid a hanging comma.
145606c3fb27SDimitry Andric             unless(stmt())
145706c3fb27SDimitry Andric         )))
145806c3fb27SDimitry Andric     ),
145906c3fb27SDimitry Andric     &CB
146006c3fb27SDimitry Andric   );
146106c3fb27SDimitry Andric   // clang-format on
146206c3fb27SDimitry Andric 
146306c3fb27SDimitry Andric   if (EmitSuggestions) {
146406c3fb27SDimitry Andric     // clang-format off
146506c3fb27SDimitry Andric     M.addMatcher(
146606c3fb27SDimitry Andric         stmt(
146706c3fb27SDimitry Andric           forEachDescendantStmt(stmt(eachOf(
146806c3fb27SDimitry Andric #define FIXABLE_GADGET(x)                                                      \
1469bdd1243dSDimitry Andric             x ## Gadget::matcher().bind(#x),
1470bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1471bdd1243dSDimitry Andric             // In parallel, match all DeclRefExprs so that to find out
1472bdd1243dSDimitry Andric             // whether there are any uncovered by gadgets.
1473bdd1243dSDimitry Andric             declRefExpr(anyOf(hasPointerType(), hasArrayType()),
14745f757f3fSDimitry Andric                         to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
1475bdd1243dSDimitry Andric             // Also match DeclStmts because we'll need them when fixing
1476bdd1243dSDimitry Andric             // their underlying VarDecls that otherwise don't have
1477bdd1243dSDimitry Andric             // any backreferences to DeclStmts.
1478bdd1243dSDimitry Andric             declStmt().bind("any_ds")
147906c3fb27SDimitry Andric           )))
148006c3fb27SDimitry Andric       ),
1481bdd1243dSDimitry Andric       &CB
1482bdd1243dSDimitry Andric     );
1483bdd1243dSDimitry Andric     // clang-format on
148406c3fb27SDimitry Andric   }
1485bdd1243dSDimitry Andric 
1486bdd1243dSDimitry Andric   M.match(*D->getBody(), D->getASTContext());
148706c3fb27SDimitry Andric   return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
148806c3fb27SDimitry Andric           std::move(CB.Tracker)};
1489bdd1243dSDimitry Andric }
1490bdd1243dSDimitry Andric 
149106c3fb27SDimitry Andric // Compares AST nodes by source locations.
149206c3fb27SDimitry Andric template <typename NodeTy> struct CompareNode {
149306c3fb27SDimitry Andric   bool operator()(const NodeTy *N1, const NodeTy *N2) const {
149406c3fb27SDimitry Andric     return N1->getBeginLoc().getRawEncoding() <
149506c3fb27SDimitry Andric            N2->getBeginLoc().getRawEncoding();
149606c3fb27SDimitry Andric   }
149706c3fb27SDimitry Andric };
149806c3fb27SDimitry Andric 
1499bdd1243dSDimitry Andric struct WarningGadgetSets {
150006c3fb27SDimitry Andric   std::map<const VarDecl *, std::set<const WarningGadget *>,
150106c3fb27SDimitry Andric            // To keep keys sorted by their locations in the map so that the
150206c3fb27SDimitry Andric            // order is deterministic:
150306c3fb27SDimitry Andric            CompareNode<VarDecl>>
150406c3fb27SDimitry Andric       byVar;
1505bdd1243dSDimitry Andric   // These Gadgets are not related to pointer variables (e. g. temporaries).
150606c3fb27SDimitry Andric   llvm::SmallVector<const WarningGadget *, 16> noVar;
1507bdd1243dSDimitry Andric };
1508bdd1243dSDimitry Andric 
1509bdd1243dSDimitry Andric static WarningGadgetSets
151006c3fb27SDimitry Andric groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
1511bdd1243dSDimitry Andric   WarningGadgetSets result;
1512bdd1243dSDimitry Andric   // If some gadgets cover more than one
1513bdd1243dSDimitry Andric   // variable, they'll appear more than once in the map.
1514bdd1243dSDimitry Andric   for (auto &G : AllUnsafeOperations) {
1515bdd1243dSDimitry Andric     DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
1516bdd1243dSDimitry Andric 
1517bdd1243dSDimitry Andric     bool AssociatedWithVarDecl = false;
1518bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
1519bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
152006c3fb27SDimitry Andric         result.byVar[VD].insert(G.get());
1521bdd1243dSDimitry Andric         AssociatedWithVarDecl = true;
1522bdd1243dSDimitry Andric       }
1523bdd1243dSDimitry Andric     }
1524bdd1243dSDimitry Andric 
1525bdd1243dSDimitry Andric     if (!AssociatedWithVarDecl) {
152606c3fb27SDimitry Andric       result.noVar.push_back(G.get());
1527bdd1243dSDimitry Andric       continue;
1528bdd1243dSDimitry Andric     }
1529bdd1243dSDimitry Andric   }
1530bdd1243dSDimitry Andric   return result;
1531bdd1243dSDimitry Andric }
1532bdd1243dSDimitry Andric 
1533bdd1243dSDimitry Andric struct FixableGadgetSets {
15345f757f3fSDimitry Andric   std::map<const VarDecl *, std::set<const FixableGadget *>,
15355f757f3fSDimitry Andric            // To keep keys sorted by their locations in the map so that the
15365f757f3fSDimitry Andric            // order is deterministic:
15375f757f3fSDimitry Andric            CompareNode<VarDecl>>
15385f757f3fSDimitry Andric       byVar;
1539bdd1243dSDimitry Andric };
1540bdd1243dSDimitry Andric 
1541bdd1243dSDimitry Andric static FixableGadgetSets
1542bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
1543bdd1243dSDimitry Andric   FixableGadgetSets FixablesForUnsafeVars;
1544bdd1243dSDimitry Andric   for (auto &F : AllFixableOperations) {
1545bdd1243dSDimitry Andric     DeclUseList DREs = F->getClaimedVarUseSites();
1546bdd1243dSDimitry Andric 
1547bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : DREs) {
1548bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
154906c3fb27SDimitry Andric         FixablesForUnsafeVars.byVar[VD].insert(F.get());
1550bdd1243dSDimitry Andric       }
1551bdd1243dSDimitry Andric     }
1552bdd1243dSDimitry Andric   }
1553bdd1243dSDimitry Andric   return FixablesForUnsafeVars;
1554bdd1243dSDimitry Andric }
1555bdd1243dSDimitry Andric 
155606c3fb27SDimitry Andric bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts,
155706c3fb27SDimitry Andric                                   const SourceManager &SM) {
155806c3fb27SDimitry Andric   // A simple interval overlap detection algorithm.  Sorts all ranges by their
155906c3fb27SDimitry Andric   // begin location then finds the first overlap in one pass.
156006c3fb27SDimitry Andric   std::vector<const FixItHint *> All; // a copy of `FixIts`
156106c3fb27SDimitry Andric 
156206c3fb27SDimitry Andric   for (const FixItHint &H : FixIts)
156306c3fb27SDimitry Andric     All.push_back(&H);
156406c3fb27SDimitry Andric   std::sort(All.begin(), All.end(),
156506c3fb27SDimitry Andric             [&SM](const FixItHint *H1, const FixItHint *H2) {
156606c3fb27SDimitry Andric               return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
156706c3fb27SDimitry Andric                                                   H2->RemoveRange.getBegin());
156806c3fb27SDimitry Andric             });
156906c3fb27SDimitry Andric 
157006c3fb27SDimitry Andric   const FixItHint *CurrHint = nullptr;
157106c3fb27SDimitry Andric 
157206c3fb27SDimitry Andric   for (const FixItHint *Hint : All) {
157306c3fb27SDimitry Andric     if (!CurrHint ||
157406c3fb27SDimitry Andric         SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
157506c3fb27SDimitry Andric                                      Hint->RemoveRange.getBegin())) {
157606c3fb27SDimitry Andric       // Either to initialize `CurrHint` or `CurrHint` does not
157706c3fb27SDimitry Andric       // overlap with `Hint`:
157806c3fb27SDimitry Andric       CurrHint = Hint;
157906c3fb27SDimitry Andric     } else
158006c3fb27SDimitry Andric       // In case `Hint` overlaps the `CurrHint`, we found at least one
158106c3fb27SDimitry Andric       // conflict:
158206c3fb27SDimitry Andric       return true;
158306c3fb27SDimitry Andric   }
158406c3fb27SDimitry Andric   return false;
158506c3fb27SDimitry Andric }
158606c3fb27SDimitry Andric 
158706c3fb27SDimitry Andric std::optional<FixItList>
1588*0fca6ea1SDimitry Andric PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
158906c3fb27SDimitry Andric   const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
159006c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
159106c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
1592*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Span:
1593*0fca6ea1SDimitry Andric     if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
159406c3fb27SDimitry Andric       return FixItList{};
159506c3fb27SDimitry Andric     return std::nullopt;
1596*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Wontfix:
159706c3fb27SDimitry Andric     return std::nullopt;
1598*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Iterator:
1599*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Array:
1600*0fca6ea1SDimitry Andric     return std::nullopt;
1601*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Vector:
160206c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
160306c3fb27SDimitry Andric   }
160406c3fb27SDimitry Andric   return std::nullopt;
160506c3fb27SDimitry Andric }
160606c3fb27SDimitry Andric 
1607*0fca6ea1SDimitry Andric /// \returns fixit that adds .data() call after \DRE.
1608*0fca6ea1SDimitry Andric static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
1609*0fca6ea1SDimitry Andric                                                        const DeclRefExpr *DRE);
1610*0fca6ea1SDimitry Andric 
161106c3fb27SDimitry Andric std::optional<FixItList>
1612*0fca6ea1SDimitry Andric CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
1613*0fca6ea1SDimitry Andric   const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
1614*0fca6ea1SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
1615*0fca6ea1SDimitry Andric   // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
1616*0fca6ea1SDimitry Andric   // non-trivial.
1617*0fca6ea1SDimitry Andric   //
1618*0fca6ea1SDimitry Andric   // CArrayToPtrAssignmentGadget doesn't have strategy implications because
1619*0fca6ea1SDimitry Andric   // constant size array propagates its bounds. Because of that LHS and RHS are
1620*0fca6ea1SDimitry Andric   // addressed by two different fixits.
1621*0fca6ea1SDimitry Andric   //
1622*0fca6ea1SDimitry Andric   // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
1623*0fca6ea1SDimitry Andric   // to and can't be generally relied on in multi-variable Fixables!
1624*0fca6ea1SDimitry Andric   //
1625*0fca6ea1SDimitry Andric   // E. g. If an instance of this gadget is fixing variable on LHS then the
1626*0fca6ea1SDimitry Andric   // variable on RHS is fixed by a different fixit and its strategy for LHS
1627*0fca6ea1SDimitry Andric   // fixit is as if Wontfix.
1628*0fca6ea1SDimitry Andric   //
1629*0fca6ea1SDimitry Andric   // The only exception is Wontfix strategy for a given variable as that is
1630*0fca6ea1SDimitry Andric   // valid for any fixit produced for the given input source code.
1631*0fca6ea1SDimitry Andric   if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
1632*0fca6ea1SDimitry Andric     if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
1633*0fca6ea1SDimitry Andric       return FixItList{};
1634*0fca6ea1SDimitry Andric     }
1635*0fca6ea1SDimitry Andric   } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
1636*0fca6ea1SDimitry Andric     if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
1637*0fca6ea1SDimitry Andric       return createDataFixit(RightVD->getASTContext(), PtrRHS);
1638*0fca6ea1SDimitry Andric     }
1639*0fca6ea1SDimitry Andric   }
1640*0fca6ea1SDimitry Andric   return std::nullopt;
1641*0fca6ea1SDimitry Andric }
1642*0fca6ea1SDimitry Andric 
1643*0fca6ea1SDimitry Andric std::optional<FixItList>
1644*0fca6ea1SDimitry Andric PointerInitGadget::getFixits(const FixitStrategy &S) const {
164506c3fb27SDimitry Andric   const auto *LeftVD = PtrInitLHS;
164606c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
164706c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
1648*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Span:
1649*0fca6ea1SDimitry Andric     if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
165006c3fb27SDimitry Andric       return FixItList{};
165106c3fb27SDimitry Andric     return std::nullopt;
1652*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Wontfix:
165306c3fb27SDimitry Andric     return std::nullopt;
1654*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Iterator:
1655*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Array:
1656*0fca6ea1SDimitry Andric     return std::nullopt;
1657*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Vector:
165806c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
165906c3fb27SDimitry Andric   }
166006c3fb27SDimitry Andric   return std::nullopt;
166106c3fb27SDimitry Andric }
166206c3fb27SDimitry Andric 
16635f757f3fSDimitry Andric static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
16645f757f3fSDimitry Andric                                      const ASTContext &Ctx) {
16655f757f3fSDimitry Andric   if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
16665f757f3fSDimitry Andric     if (ConstVal->isNegative())
16675f757f3fSDimitry Andric       return false;
16685f757f3fSDimitry Andric   } else if (!Expr->getType()->isUnsignedIntegerType())
16695f757f3fSDimitry Andric     return false;
16705f757f3fSDimitry Andric   return true;
16715f757f3fSDimitry Andric }
16725f757f3fSDimitry Andric 
167306c3fb27SDimitry Andric std::optional<FixItList>
1674*0fca6ea1SDimitry Andric ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
167506c3fb27SDimitry Andric   if (const auto *DRE =
167606c3fb27SDimitry Andric           dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
167706c3fb27SDimitry Andric     if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
167806c3fb27SDimitry Andric       switch (S.lookup(VD)) {
1679*0fca6ea1SDimitry Andric       case FixitStrategy::Kind::Span: {
16805f757f3fSDimitry Andric 
168106c3fb27SDimitry Andric         // If the index has a negative constant value, we give up as no valid
168206c3fb27SDimitry Andric         // fix-it can be generated:
168306c3fb27SDimitry Andric         const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
168406c3fb27SDimitry Andric             VD->getASTContext();
16855f757f3fSDimitry Andric         if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
168606c3fb27SDimitry Andric           return std::nullopt;
168706c3fb27SDimitry Andric         // no-op is a good fix-it, otherwise
168806c3fb27SDimitry Andric         return FixItList{};
168906c3fb27SDimitry Andric       }
1690*0fca6ea1SDimitry Andric       case FixitStrategy::Kind::Array:
1691*0fca6ea1SDimitry Andric         return FixItList{};
1692*0fca6ea1SDimitry Andric       case FixitStrategy::Kind::Wontfix:
1693*0fca6ea1SDimitry Andric       case FixitStrategy::Kind::Iterator:
1694*0fca6ea1SDimitry Andric       case FixitStrategy::Kind::Vector:
169506c3fb27SDimitry Andric         llvm_unreachable("unsupported strategies for FixableGadgets");
169606c3fb27SDimitry Andric       }
169706c3fb27SDimitry Andric     }
169806c3fb27SDimitry Andric   return std::nullopt;
169906c3fb27SDimitry Andric }
170006c3fb27SDimitry Andric 
170106c3fb27SDimitry Andric static std::optional<FixItList> // forward declaration
170206c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
170306c3fb27SDimitry Andric 
170406c3fb27SDimitry Andric std::optional<FixItList>
1705*0fca6ea1SDimitry Andric UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
170606c3fb27SDimitry Andric   auto DREs = getClaimedVarUseSites();
170706c3fb27SDimitry Andric   const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
170806c3fb27SDimitry Andric 
170906c3fb27SDimitry Andric   switch (S.lookup(VD)) {
1710*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Span:
171106c3fb27SDimitry Andric     return fixUPCAddressofArraySubscriptWithSpan(Node);
1712*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Wontfix:
1713*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Iterator:
1714*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Array:
1715*0fca6ea1SDimitry Andric     return std::nullopt;
1716*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Vector:
171706c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
171806c3fb27SDimitry Andric   }
171906c3fb27SDimitry Andric   return std::nullopt; // something went wrong, no fix-it
172006c3fb27SDimitry Andric }
172106c3fb27SDimitry Andric 
172206c3fb27SDimitry Andric // FIXME: this function should be customizable through format
172306c3fb27SDimitry Andric static StringRef getEndOfLine() {
172406c3fb27SDimitry Andric   static const char *const EOL = "\n";
172506c3fb27SDimitry Andric   return EOL;
172606c3fb27SDimitry Andric }
172706c3fb27SDimitry Andric 
172806c3fb27SDimitry Andric // Returns the text indicating that the user needs to provide input there:
172906c3fb27SDimitry Andric std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
173006c3fb27SDimitry Andric   std::string s = std::string("<# ");
173106c3fb27SDimitry Andric   s += HintTextToUser;
173206c3fb27SDimitry Andric   s += " #>";
173306c3fb27SDimitry Andric   return s;
173406c3fb27SDimitry Andric }
173506c3fb27SDimitry Andric 
173606c3fb27SDimitry Andric // Return the source location of the last character of the AST `Node`.
173706c3fb27SDimitry Andric template <typename NodeTy>
173806c3fb27SDimitry Andric static std::optional<SourceLocation>
173906c3fb27SDimitry Andric getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
174006c3fb27SDimitry Andric               const LangOptions &LangOpts) {
174106c3fb27SDimitry Andric   unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
174206c3fb27SDimitry Andric   SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
174306c3fb27SDimitry Andric 
174406c3fb27SDimitry Andric   if (Loc.isValid())
174506c3fb27SDimitry Andric     return Loc;
174606c3fb27SDimitry Andric 
174706c3fb27SDimitry Andric   return std::nullopt;
174806c3fb27SDimitry Andric }
174906c3fb27SDimitry Andric 
175006c3fb27SDimitry Andric // Return the source location just past the last character of the AST `Node`.
175106c3fb27SDimitry Andric template <typename NodeTy>
175206c3fb27SDimitry Andric static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
175306c3fb27SDimitry Andric                                                 const SourceManager &SM,
175406c3fb27SDimitry Andric                                                 const LangOptions &LangOpts) {
175506c3fb27SDimitry Andric   SourceLocation Loc =
175606c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
175706c3fb27SDimitry Andric   if (Loc.isValid())
175806c3fb27SDimitry Andric     return Loc;
175906c3fb27SDimitry Andric   return std::nullopt;
176006c3fb27SDimitry Andric }
176106c3fb27SDimitry Andric 
176206c3fb27SDimitry Andric // Return text representation of an `Expr`.
176306c3fb27SDimitry Andric static std::optional<StringRef> getExprText(const Expr *E,
176406c3fb27SDimitry Andric                                             const SourceManager &SM,
176506c3fb27SDimitry Andric                                             const LangOptions &LangOpts) {
176606c3fb27SDimitry Andric   std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
176706c3fb27SDimitry Andric 
176806c3fb27SDimitry Andric   if (LastCharLoc)
176906c3fb27SDimitry Andric     return Lexer::getSourceText(
177006c3fb27SDimitry Andric         CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
177106c3fb27SDimitry Andric         LangOpts);
177206c3fb27SDimitry Andric 
177306c3fb27SDimitry Andric   return std::nullopt;
177406c3fb27SDimitry Andric }
177506c3fb27SDimitry Andric 
177606c3fb27SDimitry Andric // Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
177706c3fb27SDimitry Andric static std::optional<StringRef> getRangeText(SourceRange SR,
177806c3fb27SDimitry Andric                                              const SourceManager &SM,
177906c3fb27SDimitry Andric                                              const LangOptions &LangOpts) {
178006c3fb27SDimitry Andric   bool Invalid = false;
17815f757f3fSDimitry Andric   CharSourceRange CSR = CharSourceRange::getCharRange(SR);
178206c3fb27SDimitry Andric   StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
178306c3fb27SDimitry Andric 
178406c3fb27SDimitry Andric   if (!Invalid)
178506c3fb27SDimitry Andric     return Text;
178606c3fb27SDimitry Andric   return std::nullopt;
178706c3fb27SDimitry Andric }
178806c3fb27SDimitry Andric 
17895f757f3fSDimitry Andric // Returns the begin location of the identifier of the given variable
17905f757f3fSDimitry Andric // declaration.
17915f757f3fSDimitry Andric static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) {
17925f757f3fSDimitry Andric   // According to the implementation of `VarDecl`, `VD->getLocation()` actually
17935f757f3fSDimitry Andric   // returns the begin location of the identifier of the declaration:
17945f757f3fSDimitry Andric   return VD->getLocation();
17955f757f3fSDimitry Andric }
17965f757f3fSDimitry Andric 
17975f757f3fSDimitry Andric // Returns the literal text of the identifier of the given variable declaration.
17985f757f3fSDimitry Andric static std::optional<StringRef>
17995f757f3fSDimitry Andric getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM,
18005f757f3fSDimitry Andric                          const LangOptions &LangOpts) {
18015f757f3fSDimitry Andric   SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
18025f757f3fSDimitry Andric   SourceLocation ParmIdentEndLoc =
18035f757f3fSDimitry Andric       Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
18045f757f3fSDimitry Andric 
18055f757f3fSDimitry Andric   if (ParmIdentEndLoc.isMacroID() &&
18065f757f3fSDimitry Andric       !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
18075f757f3fSDimitry Andric     return std::nullopt;
18085f757f3fSDimitry Andric   return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
18095f757f3fSDimitry Andric }
18105f757f3fSDimitry Andric 
18115f757f3fSDimitry Andric // We cannot fix a variable declaration if it has some other specifiers than the
18125f757f3fSDimitry Andric // type specifier.  Because the source ranges of those specifiers could overlap
18135f757f3fSDimitry Andric // with the source range that is being replaced using fix-its.  Especially when
18145f757f3fSDimitry Andric // we often cannot obtain accurate source ranges of cv-qualified type
18155f757f3fSDimitry Andric // specifiers.
18165f757f3fSDimitry Andric // FIXME: also deal with type attributes
18175f757f3fSDimitry Andric static bool hasUnsupportedSpecifiers(const VarDecl *VD,
18185f757f3fSDimitry Andric                                      const SourceManager &SM) {
18195f757f3fSDimitry Andric   // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
18205f757f3fSDimitry Andric   // source range of `VD`:
18215f757f3fSDimitry Andric   bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
18225f757f3fSDimitry Andric     return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
18235f757f3fSDimitry Andric                                           VD->getBeginLoc())) &&
18245f757f3fSDimitry Andric            !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
18255f757f3fSDimitry Andric                                           At->getRange().getBegin()));
18265f757f3fSDimitry Andric   });
18275f757f3fSDimitry Andric   return VD->isInlineSpecified() || VD->isConstexpr() ||
18285f757f3fSDimitry Andric          VD->hasConstantInitialization() || !VD->hasLocalStorage() ||
18295f757f3fSDimitry Andric          AttrRangeOverlapping;
18305f757f3fSDimitry Andric }
18315f757f3fSDimitry Andric 
18325f757f3fSDimitry Andric // Returns the `SourceRange` of `D`.  The reason why this function exists is
18335f757f3fSDimitry Andric // that `D->getSourceRange()` may return a range where the end location is the
18345f757f3fSDimitry Andric // starting location of the last token.  The end location of the source range
18355f757f3fSDimitry Andric // returned by this function is the last location of the last token.
18365f757f3fSDimitry Andric static SourceRange getSourceRangeToTokenEnd(const Decl *D,
18375f757f3fSDimitry Andric                                             const SourceManager &SM,
18385f757f3fSDimitry Andric                                             const LangOptions &LangOpts) {
18395f757f3fSDimitry Andric   SourceLocation Begin = D->getBeginLoc();
18405f757f3fSDimitry Andric   SourceLocation
18415f757f3fSDimitry Andric       End = // `D->getEndLoc` should always return the starting location of the
18425f757f3fSDimitry Andric       // last token, so we should get the end of the token
18435f757f3fSDimitry Andric       Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
18445f757f3fSDimitry Andric 
18455f757f3fSDimitry Andric   return SourceRange(Begin, End);
18465f757f3fSDimitry Andric }
18475f757f3fSDimitry Andric 
184806c3fb27SDimitry Andric // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
184906c3fb27SDimitry Andric // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does not
185006c3fb27SDimitry Andric // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
185106c3fb27SDimitry Andric // :( ), `Qualifiers` of the pointee type is returned separately through the
185206c3fb27SDimitry Andric // output parameter `QualifiersToAppend`.
185306c3fb27SDimitry Andric static std::optional<std::string>
18545f757f3fSDimitry Andric getPointeeTypeText(const VarDecl *VD, const SourceManager &SM,
185506c3fb27SDimitry Andric                    const LangOptions &LangOpts,
185606c3fb27SDimitry Andric                    std::optional<Qualifiers> *QualifiersToAppend) {
185706c3fb27SDimitry Andric   QualType Ty = VD->getType();
185806c3fb27SDimitry Andric   QualType PteTy;
185906c3fb27SDimitry Andric 
186006c3fb27SDimitry Andric   assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
186106c3fb27SDimitry Andric          "Expecting a VarDecl of type of pointer to object type");
186206c3fb27SDimitry Andric   PteTy = Ty->getPointeeType();
18635f757f3fSDimitry Andric 
18645f757f3fSDimitry Andric   TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
18655f757f3fSDimitry Andric   TypeLoc PteTyLoc;
18665f757f3fSDimitry Andric 
18675f757f3fSDimitry Andric   // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
18685f757f3fSDimitry Andric   // the `TypeLoc` of the pointee type:
18695f757f3fSDimitry Andric   switch (TyLoc.getTypeLocClass()) {
18705f757f3fSDimitry Andric   case TypeLoc::ConstantArray:
18715f757f3fSDimitry Andric   case TypeLoc::IncompleteArray:
18725f757f3fSDimitry Andric   case TypeLoc::VariableArray:
18735f757f3fSDimitry Andric   case TypeLoc::DependentSizedArray:
18745f757f3fSDimitry Andric   case TypeLoc::Decayed:
18755f757f3fSDimitry Andric     assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
18765f757f3fSDimitry Andric                                    "pointer type unless it decays.");
18775f757f3fSDimitry Andric     PteTyLoc = TyLoc.getNextTypeLoc();
18785f757f3fSDimitry Andric     break;
18795f757f3fSDimitry Andric   case TypeLoc::Pointer:
18805f757f3fSDimitry Andric     PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
18815f757f3fSDimitry Andric     break;
18825f757f3fSDimitry Andric   default:
18835f757f3fSDimitry Andric     return std::nullopt;
18845f757f3fSDimitry Andric   }
18855f757f3fSDimitry Andric   if (PteTyLoc.isNull())
18865f757f3fSDimitry Andric     // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
18875f757f3fSDimitry Andric     // when the pointer type is `auto`.
188806c3fb27SDimitry Andric     return std::nullopt;
188906c3fb27SDimitry Andric 
18905f757f3fSDimitry Andric   SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD);
189106c3fb27SDimitry Andric 
18925f757f3fSDimitry Andric   if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
189306c3fb27SDimitry Andric     // We are expecting these locations to be valid. But in some cases, they are
189406c3fb27SDimitry Andric     // not all valid. It is a Clang bug to me and we are not responsible for
189506c3fb27SDimitry Andric     // fixing it.  So we will just give up for now when it happens.
189606c3fb27SDimitry Andric     return std::nullopt;
189706c3fb27SDimitry Andric   }
189806c3fb27SDimitry Andric 
189906c3fb27SDimitry Andric   // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
190006c3fb27SDimitry Andric   SourceLocation PteEndOfTokenLoc =
190106c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
190206c3fb27SDimitry Andric 
19035f757f3fSDimitry Andric   if (!PteEndOfTokenLoc.isValid())
19045f757f3fSDimitry Andric     // Sometimes we cannot get the end location of the pointee type, e.g., when
19055f757f3fSDimitry Andric     // there are macros involved.
19065f757f3fSDimitry Andric     return std::nullopt;
19075f757f3fSDimitry Andric   if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
190806c3fb27SDimitry Andric     // We only deal with the cases where the source text of the pointee type
190906c3fb27SDimitry Andric     // appears on the left-hand side of the variable identifier completely,
191006c3fb27SDimitry Andric     // including the following forms:
191106c3fb27SDimitry Andric     // `T ident`,
191206c3fb27SDimitry Andric     // `T ident[]`, where `T` is any type.
191306c3fb27SDimitry Andric     // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
191406c3fb27SDimitry Andric     return std::nullopt;
191506c3fb27SDimitry Andric   }
191606c3fb27SDimitry Andric   if (PteTy.hasQualifiers()) {
191706c3fb27SDimitry Andric     // TypeLoc does not provide source ranges for qualifiers (it says it's
191806c3fb27SDimitry Andric     // intentional but seems fishy to me), so we cannot get the full text
191906c3fb27SDimitry Andric     // `PteTy` via source ranges.
192006c3fb27SDimitry Andric     *QualifiersToAppend = PteTy.getQualifiers();
192106c3fb27SDimitry Andric   }
192206c3fb27SDimitry Andric   return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
192306c3fb27SDimitry Andric       ->str();
192406c3fb27SDimitry Andric }
192506c3fb27SDimitry Andric 
192606c3fb27SDimitry Andric // Returns the text of the name (with qualifiers) of a `FunctionDecl`.
192706c3fb27SDimitry Andric static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
192806c3fb27SDimitry Andric                                                const SourceManager &SM,
192906c3fb27SDimitry Andric                                                const LangOptions &LangOpts) {
193006c3fb27SDimitry Andric   SourceLocation BeginLoc = FD->getQualifier()
193106c3fb27SDimitry Andric                                 ? FD->getQualifierLoc().getBeginLoc()
193206c3fb27SDimitry Andric                                 : FD->getNameInfo().getBeginLoc();
193306c3fb27SDimitry Andric   // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
193406c3fb27SDimitry Andric   // last token:
193506c3fb27SDimitry Andric   SourceLocation EndLoc = Lexer::getLocForEndOfToken(
193606c3fb27SDimitry Andric       FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
193706c3fb27SDimitry Andric   SourceRange NameRange{BeginLoc, EndLoc};
193806c3fb27SDimitry Andric 
193906c3fb27SDimitry Andric   return getRangeText(NameRange, SM, LangOpts);
194006c3fb27SDimitry Andric }
194106c3fb27SDimitry Andric 
19425f757f3fSDimitry Andric // Returns the text representing a `std::span` type where the element type is
19435f757f3fSDimitry Andric // represented by `EltTyText`.
19445f757f3fSDimitry Andric //
19455f757f3fSDimitry Andric // Note the optional parameter `Qualifiers`: one needs to pass qualifiers
19465f757f3fSDimitry Andric // explicitly if the element type needs to be qualified.
19475f757f3fSDimitry Andric static std::string
19485f757f3fSDimitry Andric getSpanTypeText(StringRef EltTyText,
19495f757f3fSDimitry Andric                 std::optional<Qualifiers> Quals = std::nullopt) {
19505f757f3fSDimitry Andric   const char *const SpanOpen = "std::span<";
19515f757f3fSDimitry Andric 
19525f757f3fSDimitry Andric   if (Quals)
19535f757f3fSDimitry Andric     return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
19545f757f3fSDimitry Andric   return SpanOpen + EltTyText.str() + '>';
19555f757f3fSDimitry Andric }
19565f757f3fSDimitry Andric 
195706c3fb27SDimitry Andric std::optional<FixItList>
1958*0fca6ea1SDimitry Andric DerefSimplePtrArithFixableGadget::getFixits(const FixitStrategy &s) const {
195906c3fb27SDimitry Andric   const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
196006c3fb27SDimitry Andric 
1961*0fca6ea1SDimitry Andric   if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
196206c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
196306c3fb27SDimitry Andric     // std::span can't represent elements before its begin()
196406c3fb27SDimitry Andric     if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
196506c3fb27SDimitry Andric       if (ConstVal->isNegative())
196606c3fb27SDimitry Andric         return std::nullopt;
196706c3fb27SDimitry Andric 
196806c3fb27SDimitry Andric     // note that the expr may (oddly) has multiple layers of parens
196906c3fb27SDimitry Andric     // example:
197006c3fb27SDimitry Andric     //   *((..(pointer + 123)..))
197106c3fb27SDimitry Andric     // goal:
197206c3fb27SDimitry Andric     //   pointer[123]
197306c3fb27SDimitry Andric     // Fix-It:
197406c3fb27SDimitry Andric     //   remove '*('
197506c3fb27SDimitry Andric     //   replace ' + ' with '['
197606c3fb27SDimitry Andric     //   replace ')' with ']'
197706c3fb27SDimitry Andric 
197806c3fb27SDimitry Andric     // example:
197906c3fb27SDimitry Andric     //   *((..(123 + pointer)..))
198006c3fb27SDimitry Andric     // goal:
198106c3fb27SDimitry Andric     //   123[pointer]
198206c3fb27SDimitry Andric     // Fix-It:
198306c3fb27SDimitry Andric     //   remove '*('
198406c3fb27SDimitry Andric     //   replace ' + ' with '['
198506c3fb27SDimitry Andric     //   replace ')' with ']'
198606c3fb27SDimitry Andric 
198706c3fb27SDimitry Andric     const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
198806c3fb27SDimitry Andric     const SourceManager &SM = Ctx.getSourceManager();
198906c3fb27SDimitry Andric     const LangOptions &LangOpts = Ctx.getLangOpts();
199006c3fb27SDimitry Andric     CharSourceRange StarWithTrailWhitespace =
199106c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
199206c3fb27SDimitry Andric                                              LHS->getBeginLoc());
199306c3fb27SDimitry Andric 
199406c3fb27SDimitry Andric     std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
199506c3fb27SDimitry Andric     if (!LHSLocation)
199606c3fb27SDimitry Andric       return std::nullopt;
199706c3fb27SDimitry Andric 
199806c3fb27SDimitry Andric     CharSourceRange PlusWithSurroundingWhitespace =
199906c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
200006c3fb27SDimitry Andric 
200106c3fb27SDimitry Andric     std::optional<SourceLocation> AddOpLocation =
200206c3fb27SDimitry Andric         getPastLoc(AddOp, SM, LangOpts);
200306c3fb27SDimitry Andric     std::optional<SourceLocation> DerefOpLocation =
200406c3fb27SDimitry Andric         getPastLoc(DerefOp, SM, LangOpts);
200506c3fb27SDimitry Andric 
200606c3fb27SDimitry Andric     if (!AddOpLocation || !DerefOpLocation)
200706c3fb27SDimitry Andric       return std::nullopt;
200806c3fb27SDimitry Andric 
200906c3fb27SDimitry Andric     CharSourceRange ClosingParenWithPrecWhitespace =
201006c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
201106c3fb27SDimitry Andric 
201206c3fb27SDimitry Andric     return FixItList{
201306c3fb27SDimitry Andric         {FixItHint::CreateRemoval(StarWithTrailWhitespace),
201406c3fb27SDimitry Andric          FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
201506c3fb27SDimitry Andric          FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
201606c3fb27SDimitry Andric   }
201706c3fb27SDimitry Andric   return std::nullopt; // something wrong or unsupported, give up
201806c3fb27SDimitry Andric }
201906c3fb27SDimitry Andric 
202006c3fb27SDimitry Andric std::optional<FixItList>
2021*0fca6ea1SDimitry Andric PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
202206c3fb27SDimitry Andric   const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
202306c3fb27SDimitry Andric   switch (S.lookup(VD)) {
2024*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Span: {
202506c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
202606c3fb27SDimitry Andric     SourceManager &SM = Ctx.getSourceManager();
202706c3fb27SDimitry Andric     // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
202806c3fb27SDimitry Andric     // Deletes the *operand
202906c3fb27SDimitry Andric     CharSourceRange derefRange = clang::CharSourceRange::getCharRange(
203006c3fb27SDimitry Andric         Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
203106c3fb27SDimitry Andric     // Inserts the [0]
20325f757f3fSDimitry Andric     if (auto LocPastOperand =
20335f757f3fSDimitry Andric             getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
203406c3fb27SDimitry Andric       return FixItList{{FixItHint::CreateRemoval(derefRange),
20355f757f3fSDimitry Andric                         FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
203606c3fb27SDimitry Andric     }
20375f757f3fSDimitry Andric     break;
203806c3fb27SDimitry Andric   }
2039*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Iterator:
2040*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Array:
2041*0fca6ea1SDimitry Andric     return std::nullopt;
2042*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Vector:
2043*0fca6ea1SDimitry Andric     llvm_unreachable("FixitStrategy not implemented yet!");
2044*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Wontfix:
204506c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
204606c3fb27SDimitry Andric   }
204706c3fb27SDimitry Andric 
204806c3fb27SDimitry Andric   return std::nullopt;
204906c3fb27SDimitry Andric }
205006c3fb27SDimitry Andric 
2051*0fca6ea1SDimitry Andric static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
2052*0fca6ea1SDimitry Andric                                                        const DeclRefExpr *DRE) {
2053*0fca6ea1SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
205406c3fb27SDimitry Andric   // Inserts the .data() after the DRE
205506c3fb27SDimitry Andric   std::optional<SourceLocation> EndOfOperand =
2056*0fca6ea1SDimitry Andric       getPastLoc(DRE, SM, Ctx.getLangOpts());
205706c3fb27SDimitry Andric 
205806c3fb27SDimitry Andric   if (EndOfOperand)
2059*0fca6ea1SDimitry Andric     return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
2060*0fca6ea1SDimitry Andric 
2061*0fca6ea1SDimitry Andric   return std::nullopt;
2062*0fca6ea1SDimitry Andric }
2063*0fca6ea1SDimitry Andric 
2064*0fca6ea1SDimitry Andric // Generates fix-its replacing an expression of the form UPC(DRE) with
2065*0fca6ea1SDimitry Andric // `DRE.data()`
2066*0fca6ea1SDimitry Andric std::optional<FixItList>
2067*0fca6ea1SDimitry Andric UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
2068*0fca6ea1SDimitry Andric   const auto VD = cast<VarDecl>(Node->getDecl());
2069*0fca6ea1SDimitry Andric   switch (S.lookup(VD)) {
2070*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Array:
2071*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Span: {
2072*0fca6ea1SDimitry Andric     return createDataFixit(VD->getASTContext(), Node);
20735f757f3fSDimitry Andric     // FIXME: Points inside a macro expansion.
20745f757f3fSDimitry Andric     break;
207506c3fb27SDimitry Andric   }
2076*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Wontfix:
2077*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Iterator:
2078*0fca6ea1SDimitry Andric     return std::nullopt;
2079*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Vector:
208006c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
208106c3fb27SDimitry Andric   }
208206c3fb27SDimitry Andric 
208306c3fb27SDimitry Andric   return std::nullopt;
208406c3fb27SDimitry Andric }
208506c3fb27SDimitry Andric 
208606c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form `&DRE[e]` with
208706c3fb27SDimitry Andric // `&DRE.data()[e]`:
208806c3fb27SDimitry Andric static std::optional<FixItList>
208906c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) {
209006c3fb27SDimitry Andric   const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
209106c3fb27SDimitry Andric   const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
209206c3fb27SDimitry Andric   // FIXME: this `getASTContext` call is costly, we should pass the
209306c3fb27SDimitry Andric   // ASTContext in:
209406c3fb27SDimitry Andric   const ASTContext &Ctx = DRE->getDecl()->getASTContext();
209506c3fb27SDimitry Andric   const Expr *Idx = ArraySub->getIdx();
209606c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
209706c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
209806c3fb27SDimitry Andric   std::stringstream SS;
209906c3fb27SDimitry Andric   bool IdxIsLitZero = false;
210006c3fb27SDimitry Andric 
210106c3fb27SDimitry Andric   if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
210206c3fb27SDimitry Andric     if ((*ICE).isZero())
210306c3fb27SDimitry Andric       IdxIsLitZero = true;
210406c3fb27SDimitry Andric   std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
210506c3fb27SDimitry Andric   if (!DreString)
210606c3fb27SDimitry Andric     return std::nullopt;
210706c3fb27SDimitry Andric 
210806c3fb27SDimitry Andric   if (IdxIsLitZero) {
210906c3fb27SDimitry Andric     // If the index is literal zero, we produce the most concise fix-it:
211006c3fb27SDimitry Andric     SS << (*DreString).str() << ".data()";
211106c3fb27SDimitry Andric   } else {
211206c3fb27SDimitry Andric     std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
211306c3fb27SDimitry Andric     if (!IndexString)
211406c3fb27SDimitry Andric       return std::nullopt;
211506c3fb27SDimitry Andric 
211606c3fb27SDimitry Andric     SS << "&" << (*DreString).str() << ".data()"
211706c3fb27SDimitry Andric        << "[" << (*IndexString).str() << "]";
211806c3fb27SDimitry Andric   }
211906c3fb27SDimitry Andric   return FixItList{
212006c3fb27SDimitry Andric       FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
212106c3fb27SDimitry Andric }
212206c3fb27SDimitry Andric 
21235f757f3fSDimitry Andric std::optional<FixItList>
2124*0fca6ea1SDimitry Andric UUCAddAssignGadget::getFixits(const FixitStrategy &S) const {
21255f757f3fSDimitry Andric   DeclUseList DREs = getClaimedVarUseSites();
21265f757f3fSDimitry Andric 
21275f757f3fSDimitry Andric   if (DREs.size() != 1)
21285f757f3fSDimitry Andric     return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
21295f757f3fSDimitry Andric                          // give up
21305f757f3fSDimitry Andric   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2131*0fca6ea1SDimitry Andric     if (S.lookup(VD) == FixitStrategy::Kind::Span) {
21325f757f3fSDimitry Andric       FixItList Fixes;
21335f757f3fSDimitry Andric 
2134*0fca6ea1SDimitry Andric       const Stmt *AddAssignNode = Node;
21355f757f3fSDimitry Andric       StringRef varName = VD->getName();
21365f757f3fSDimitry Andric       const ASTContext &Ctx = VD->getASTContext();
21375f757f3fSDimitry Andric 
21385f757f3fSDimitry Andric       if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
21395f757f3fSDimitry Andric         return std::nullopt;
21405f757f3fSDimitry Andric 
21415f757f3fSDimitry Andric       // To transform UUC(p += n) to UUC(p = p.subspan(..)):
21425f757f3fSDimitry Andric       bool NotParenExpr =
21435f757f3fSDimitry Andric           (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
21445f757f3fSDimitry Andric       std::string SS = varName.str() + " = " + varName.str() + ".subspan";
21455f757f3fSDimitry Andric       if (NotParenExpr)
21465f757f3fSDimitry Andric         SS += "(";
21475f757f3fSDimitry Andric 
21485f757f3fSDimitry Andric       std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
21495f757f3fSDimitry Andric           AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
21505f757f3fSDimitry Andric       if (!AddAssignLocation)
21515f757f3fSDimitry Andric         return std::nullopt;
21525f757f3fSDimitry Andric 
21535f757f3fSDimitry Andric       Fixes.push_back(FixItHint::CreateReplacement(
21545f757f3fSDimitry Andric           SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
21555f757f3fSDimitry Andric           SS));
21565f757f3fSDimitry Andric       if (NotParenExpr)
21575f757f3fSDimitry Andric         Fixes.push_back(FixItHint::CreateInsertion(
21585f757f3fSDimitry Andric             Offset->getEndLoc().getLocWithOffset(1), ")"));
21595f757f3fSDimitry Andric       return Fixes;
21605f757f3fSDimitry Andric     }
21615f757f3fSDimitry Andric   }
21625f757f3fSDimitry Andric   return std::nullopt; // Not in the cases that we can handle for now, give up.
21635f757f3fSDimitry Andric }
216406c3fb27SDimitry Andric 
2165*0fca6ea1SDimitry Andric std::optional<FixItList>
2166*0fca6ea1SDimitry Andric UPCPreIncrementGadget::getFixits(const FixitStrategy &S) const {
216706c3fb27SDimitry Andric   DeclUseList DREs = getClaimedVarUseSites();
216806c3fb27SDimitry Andric 
216906c3fb27SDimitry Andric   if (DREs.size() != 1)
217006c3fb27SDimitry Andric     return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
217106c3fb27SDimitry Andric                          // give up
217206c3fb27SDimitry Andric   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2173*0fca6ea1SDimitry Andric     if (S.lookup(VD) == FixitStrategy::Kind::Span) {
217406c3fb27SDimitry Andric       FixItList Fixes;
217506c3fb27SDimitry Andric       std::stringstream SS;
217606c3fb27SDimitry Andric       StringRef varName = VD->getName();
217706c3fb27SDimitry Andric       const ASTContext &Ctx = VD->getASTContext();
217806c3fb27SDimitry Andric 
217906c3fb27SDimitry Andric       // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
218006c3fb27SDimitry Andric       SS << "(" << varName.data() << " = " << varName.data()
218106c3fb27SDimitry Andric          << ".subspan(1)).data()";
218206c3fb27SDimitry Andric       std::optional<SourceLocation> PreIncLocation =
2183*0fca6ea1SDimitry Andric           getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts());
218406c3fb27SDimitry Andric       if (!PreIncLocation)
218506c3fb27SDimitry Andric         return std::nullopt;
218606c3fb27SDimitry Andric 
218706c3fb27SDimitry Andric       Fixes.push_back(FixItHint::CreateReplacement(
2188*0fca6ea1SDimitry Andric           SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
218906c3fb27SDimitry Andric       return Fixes;
219006c3fb27SDimitry Andric     }
219106c3fb27SDimitry Andric   }
219206c3fb27SDimitry Andric   return std::nullopt; // Not in the cases that we can handle for now, give up.
219306c3fb27SDimitry Andric }
219406c3fb27SDimitry Andric 
219506c3fb27SDimitry Andric // For a non-null initializer `Init` of `T *` type, this function returns
219606c3fb27SDimitry Andric // `FixItHint`s producing a list initializer `{Init,  S}` as a part of a fix-it
219706c3fb27SDimitry Andric // to output stream.
219806c3fb27SDimitry Andric // In many cases, this function cannot figure out the actual extent `S`.  It
219906c3fb27SDimitry Andric // then will use a place holder to replace `S` to ask users to fill `S` in.  The
220006c3fb27SDimitry Andric // initializer shall be used to initialize a variable of type `std::span<T>`.
2201*0fca6ea1SDimitry Andric // In some cases (e. g. constant size array) the initializer should remain
2202*0fca6ea1SDimitry Andric // unchanged and the function returns empty list. In case the function can't
2203*0fca6ea1SDimitry Andric // provide the right fixit it will return nullopt.
220406c3fb27SDimitry Andric //
220506c3fb27SDimitry Andric // FIXME: Support multi-level pointers
220606c3fb27SDimitry Andric //
220706c3fb27SDimitry Andric // Parameters:
220806c3fb27SDimitry Andric //   `Init` a pointer to the initializer expression
220906c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
2210*0fca6ea1SDimitry Andric static std::optional<FixItList>
22115f757f3fSDimitry Andric FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
221206c3fb27SDimitry Andric                           const StringRef UserFillPlaceHolder) {
221306c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
221406c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
221506c3fb27SDimitry Andric 
221606c3fb27SDimitry Andric   // If `Init` has a constant value that is (or equivalent to) a
221706c3fb27SDimitry Andric   // NULL pointer, we use the default constructor to initialize the span
221806c3fb27SDimitry Andric   // object, i.e., a `std:span` variable declaration with no initializer.
221906c3fb27SDimitry Andric   // So the fix-it is just to remove the initializer.
2220*0fca6ea1SDimitry Andric   if (Init->isNullPointerConstant(
2221*0fca6ea1SDimitry Andric           Ctx,
222206c3fb27SDimitry Andric           // FIXME: Why does this function not ask for `const ASTContext
222306c3fb27SDimitry Andric           // &`? It should. Maybe worth an NFC patch later.
222406c3fb27SDimitry Andric           Expr::NullPointerConstantValueDependence::
222506c3fb27SDimitry Andric               NPC_ValueDependentIsNotNull)) {
222606c3fb27SDimitry Andric     std::optional<SourceLocation> InitLocation =
222706c3fb27SDimitry Andric         getEndCharLoc(Init, SM, LangOpts);
222806c3fb27SDimitry Andric     if (!InitLocation)
2229*0fca6ea1SDimitry Andric       return std::nullopt;
223006c3fb27SDimitry Andric 
223106c3fb27SDimitry Andric     SourceRange SR(Init->getBeginLoc(), *InitLocation);
223206c3fb27SDimitry Andric 
2233*0fca6ea1SDimitry Andric     return FixItList{FixItHint::CreateRemoval(SR)};
223406c3fb27SDimitry Andric   }
223506c3fb27SDimitry Andric 
223606c3fb27SDimitry Andric   FixItList FixIts{};
223706c3fb27SDimitry Andric   std::string ExtentText = UserFillPlaceHolder.data();
223806c3fb27SDimitry Andric   StringRef One = "1";
223906c3fb27SDimitry Andric 
224006c3fb27SDimitry Andric   // Insert `{` before `Init`:
224106c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
224206c3fb27SDimitry Andric   // Try to get the data extent. Break into different cases:
224306c3fb27SDimitry Andric   if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
224406c3fb27SDimitry Andric     // In cases `Init` is `new T[n]` and there is no explicit cast over
224506c3fb27SDimitry Andric     // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
224606c3fb27SDimitry Andric     // of `T`. So the extent is `n` unless `n` has side effects.  Similar but
224706c3fb27SDimitry Andric     // simpler for the case where `Init` is `new T`.
224806c3fb27SDimitry Andric     if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
224906c3fb27SDimitry Andric       if (!Ext->HasSideEffects(Ctx)) {
225006c3fb27SDimitry Andric         std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
225106c3fb27SDimitry Andric         if (!ExtentString)
2252*0fca6ea1SDimitry Andric           return std::nullopt;
225306c3fb27SDimitry Andric         ExtentText = *ExtentString;
225406c3fb27SDimitry Andric       }
225506c3fb27SDimitry Andric     } else if (!CxxNew->isArray())
225606c3fb27SDimitry Andric       // Although the initializer is not allocating a buffer, the pointer
225706c3fb27SDimitry Andric       // variable could still be used in buffer access operations.
225806c3fb27SDimitry Andric       ExtentText = One;
2259*0fca6ea1SDimitry Andric   } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
2260*0fca6ea1SDimitry Andric     // std::span has a single parameter constructor for initialization with
2261*0fca6ea1SDimitry Andric     // constant size array. The size is auto-deduced as the constructor is a
2262*0fca6ea1SDimitry Andric     // function template. The correct fixit is empty - no changes should happen.
2263*0fca6ea1SDimitry Andric     return FixItList{};
226406c3fb27SDimitry Andric   } else {
226506c3fb27SDimitry Andric     // In cases `Init` is of the form `&Var` after stripping of implicit
226606c3fb27SDimitry Andric     // casts, where `&` is the built-in operator, the extent is 1.
226706c3fb27SDimitry Andric     if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
226806c3fb27SDimitry Andric       if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
226906c3fb27SDimitry Andric           isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
227006c3fb27SDimitry Andric         ExtentText = One;
227106c3fb27SDimitry Andric     // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
227206c3fb27SDimitry Andric     // and explicit casting, etc. etc.
227306c3fb27SDimitry Andric   }
227406c3fb27SDimitry Andric 
227506c3fb27SDimitry Andric   SmallString<32> StrBuffer{};
227606c3fb27SDimitry Andric   std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
227706c3fb27SDimitry Andric 
227806c3fb27SDimitry Andric   if (!LocPassInit)
2279*0fca6ea1SDimitry Andric     return std::nullopt;
228006c3fb27SDimitry Andric 
228106c3fb27SDimitry Andric   StrBuffer.append(", ");
228206c3fb27SDimitry Andric   StrBuffer.append(ExtentText);
228306c3fb27SDimitry Andric   StrBuffer.append("}");
228406c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
228506c3fb27SDimitry Andric   return FixIts;
228606c3fb27SDimitry Andric }
228706c3fb27SDimitry Andric 
22885f757f3fSDimitry Andric #ifndef NDEBUG
22895f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg)                                           \
2290*0fca6ea1SDimitry Andric   Handler.addDebugNoteForVar((D), (D)->getBeginLoc(),                          \
2291*0fca6ea1SDimitry Andric                              "failed to produce fixit for declaration '" +     \
2292*0fca6ea1SDimitry Andric                                  (D)->getNameAsString() + "'" + (Msg))
22935f757f3fSDimitry Andric #else
22945f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg)
22955f757f3fSDimitry Andric #endif
22965f757f3fSDimitry Andric 
22975f757f3fSDimitry Andric // For the given variable declaration with a pointer-to-T type, returns the text
22985f757f3fSDimitry Andric // `std::span<T>`.  If it is unable to generate the text, returns
22995f757f3fSDimitry Andric // `std::nullopt`.
2300*0fca6ea1SDimitry Andric static std::optional<std::string>
2301*0fca6ea1SDimitry Andric createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx) {
23025f757f3fSDimitry Andric   assert(VD->getType()->isPointerType());
23035f757f3fSDimitry Andric 
23045f757f3fSDimitry Andric   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
23055f757f3fSDimitry Andric   std::optional<std::string> PteTyText = getPointeeTypeText(
23065f757f3fSDimitry Andric       VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
23075f757f3fSDimitry Andric 
23085f757f3fSDimitry Andric   if (!PteTyText)
23095f757f3fSDimitry Andric     return std::nullopt;
23105f757f3fSDimitry Andric 
23115f757f3fSDimitry Andric   std::string SpanTyText = "std::span<";
23125f757f3fSDimitry Andric 
23135f757f3fSDimitry Andric   SpanTyText.append(*PteTyText);
23145f757f3fSDimitry Andric   // Append qualifiers to span element type if any:
23155f757f3fSDimitry Andric   if (PteTyQualifiers) {
23165f757f3fSDimitry Andric     SpanTyText.append(" ");
23175f757f3fSDimitry Andric     SpanTyText.append(PteTyQualifiers->getAsString());
23185f757f3fSDimitry Andric   }
23195f757f3fSDimitry Andric   SpanTyText.append(">");
23205f757f3fSDimitry Andric   return SpanTyText;
23215f757f3fSDimitry Andric }
23225f757f3fSDimitry Andric 
232306c3fb27SDimitry Andric // For a `VarDecl` of the form `T  * var (= Init)?`, this
23245f757f3fSDimitry Andric // function generates fix-its that
23255f757f3fSDimitry Andric //  1) replace `T * var` with `std::span<T> var`; and
23265f757f3fSDimitry Andric //  2) change `Init` accordingly to a span constructor, if it exists.
232706c3fb27SDimitry Andric //
232806c3fb27SDimitry Andric // FIXME: support Multi-level pointers
232906c3fb27SDimitry Andric //
233006c3fb27SDimitry Andric // Parameters:
233106c3fb27SDimitry Andric //   `D` a pointer the variable declaration node
233206c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
23335f757f3fSDimitry Andric //   `UserFillPlaceHolder` the user-input placeholder text
233406c3fb27SDimitry Andric // Returns:
23355f757f3fSDimitry Andric //    the non-empty fix-it list, if fix-its are successfuly generated; empty
23365f757f3fSDimitry Andric //    list otherwise.
23375f757f3fSDimitry Andric static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
23385f757f3fSDimitry Andric                                          const StringRef UserFillPlaceHolder,
23395f757f3fSDimitry Andric                                          UnsafeBufferUsageHandler &Handler) {
23405f757f3fSDimitry Andric   if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager()))
23415f757f3fSDimitry Andric     return {};
234206c3fb27SDimitry Andric 
234306c3fb27SDimitry Andric   FixItList FixIts{};
23445f757f3fSDimitry Andric   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
234506c3fb27SDimitry Andric 
23465f757f3fSDimitry Andric   if (!SpanTyText) {
23475f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
23485f757f3fSDimitry Andric     return {};
23495f757f3fSDimitry Andric   }
23505f757f3fSDimitry Andric 
23515f757f3fSDimitry Andric   // Will hold the text for `std::span<T> Ident`:
23525f757f3fSDimitry Andric   std::stringstream SS;
23535f757f3fSDimitry Andric 
23545f757f3fSDimitry Andric   SS << *SpanTyText;
23555f757f3fSDimitry Andric   // Fix the initializer if it exists:
235606c3fb27SDimitry Andric   if (const Expr *Init = D->getInit()) {
2357*0fca6ea1SDimitry Andric     std::optional<FixItList> InitFixIts =
23585f757f3fSDimitry Andric         FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
2359*0fca6ea1SDimitry Andric     if (!InitFixIts)
236006c3fb27SDimitry Andric       return {};
2361*0fca6ea1SDimitry Andric     FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
2362*0fca6ea1SDimitry Andric                   std::make_move_iterator(InitFixIts->end()));
23635f757f3fSDimitry Andric   }
2364*0fca6ea1SDimitry Andric   // For declaration of the form `T * ident = init;`, we want to replace
2365*0fca6ea1SDimitry Andric   // `T * ` with `std::span<T>`.
2366*0fca6ea1SDimitry Andric   // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
2367*0fca6ea1SDimitry Andric   // just `T *` with `std::span<T>`.
2368*0fca6ea1SDimitry Andric   const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
23695f757f3fSDimitry Andric   if (!EndLocForReplacement.isValid()) {
23705f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
237106c3fb27SDimitry Andric     return {};
23725f757f3fSDimitry Andric   }
2373*0fca6ea1SDimitry Andric   // The only exception is that for `T *ident` we'll add a single space between
2374*0fca6ea1SDimitry Andric   // "std::span<T>" and "ident".
2375*0fca6ea1SDimitry Andric   // FIXME: The condition is false for identifiers expended from macros.
2376*0fca6ea1SDimitry Andric   if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
2377*0fca6ea1SDimitry Andric     SS << " ";
2378*0fca6ea1SDimitry Andric 
237906c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateReplacement(
23805f757f3fSDimitry Andric       SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
238106c3fb27SDimitry Andric   return FixIts;
238206c3fb27SDimitry Andric }
238306c3fb27SDimitry Andric 
238406c3fb27SDimitry Andric static bool hasConflictingOverload(const FunctionDecl *FD) {
238506c3fb27SDimitry Andric   return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
238606c3fb27SDimitry Andric }
238706c3fb27SDimitry Andric 
23885f757f3fSDimitry Andric // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
23895f757f3fSDimitry Andric // types, this function produces fix-its to make the change self-contained.  Let
23905f757f3fSDimitry Andric // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
23915f757f3fSDimitry Andric // entity defined by the `FunctionDecl` after the change to the parameters.
23925f757f3fSDimitry Andric // Fix-its produced by this function are
239306c3fb27SDimitry Andric //   1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
239406c3fb27SDimitry Andric //   of 'F';
239506c3fb27SDimitry Andric //   2. Create a declaration of "NewF" next to each declaration of `F`;
239606c3fb27SDimitry Andric //   3. Create a definition of "F" (as its' original definition is now belongs
239706c3fb27SDimitry Andric //      to "NewF") next to its original definition.  The body of the creating
239806c3fb27SDimitry Andric //      definition calls to "NewF".
239906c3fb27SDimitry Andric //
240006c3fb27SDimitry Andric // Example:
240106c3fb27SDimitry Andric //
240206c3fb27SDimitry Andric // void f(int *p);  // original declaration
240306c3fb27SDimitry Andric // void f(int *p) { // original definition
240406c3fb27SDimitry Andric //    p[5];
240506c3fb27SDimitry Andric // }
240606c3fb27SDimitry Andric //
240706c3fb27SDimitry Andric // To change the parameter `p` to be of `std::span<int>` type, we
240806c3fb27SDimitry Andric // also add overloads:
240906c3fb27SDimitry Andric //
241006c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
241106c3fb27SDimitry Andric // void f(std::span<int> p);                      // added overload decl
241206c3fb27SDimitry Andric // void f(std::span<int> p) {     // original def where param is changed
241306c3fb27SDimitry Andric //    p[5];
241406c3fb27SDimitry Andric // }
241506c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p) {  // added def
241606c3fb27SDimitry Andric //   return f(std::span(p, <# size #>));
241706c3fb27SDimitry Andric // }
241806c3fb27SDimitry Andric //
241906c3fb27SDimitry Andric static std::optional<FixItList>
2420*0fca6ea1SDimitry Andric createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD,
24215f757f3fSDimitry Andric                               const ASTContext &Ctx,
242206c3fb27SDimitry Andric                               UnsafeBufferUsageHandler &Handler) {
242306c3fb27SDimitry Andric   // FIXME: need to make this conflict checking better:
242406c3fb27SDimitry Andric   if (hasConflictingOverload(FD))
242506c3fb27SDimitry Andric     return std::nullopt;
242606c3fb27SDimitry Andric 
242706c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
242806c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
24295f757f3fSDimitry Andric   const unsigned NumParms = FD->getNumParams();
24305f757f3fSDimitry Andric   std::vector<std::string> NewTysTexts(NumParms);
24315f757f3fSDimitry Andric   std::vector<bool> ParmsMask(NumParms, false);
24325f757f3fSDimitry Andric   bool AtLeastOneParmToFix = false;
24335f757f3fSDimitry Andric 
24345f757f3fSDimitry Andric   for (unsigned i = 0; i < NumParms; i++) {
24355f757f3fSDimitry Andric     const ParmVarDecl *PVD = FD->getParamDecl(i);
24365f757f3fSDimitry Andric 
2437*0fca6ea1SDimitry Andric     if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
24385f757f3fSDimitry Andric       continue;
2439*0fca6ea1SDimitry Andric     if (S.lookup(PVD) != FixitStrategy::Kind::Span)
24405f757f3fSDimitry Andric       // Not supported, not suppose to happen:
24415f757f3fSDimitry Andric       return std::nullopt;
24425f757f3fSDimitry Andric 
24435f757f3fSDimitry Andric     std::optional<Qualifiers> PteTyQuals = std::nullopt;
24445f757f3fSDimitry Andric     std::optional<std::string> PteTyText =
24455f757f3fSDimitry Andric         getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
24465f757f3fSDimitry Andric 
24475f757f3fSDimitry Andric     if (!PteTyText)
24485f757f3fSDimitry Andric       // something wrong in obtaining the text of the pointee type, give up
24495f757f3fSDimitry Andric       return std::nullopt;
2450*0fca6ea1SDimitry Andric     // FIXME: whether we should create std::span type depends on the
2451*0fca6ea1SDimitry Andric     // FixitStrategy.
24525f757f3fSDimitry Andric     NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
24535f757f3fSDimitry Andric     ParmsMask[i] = true;
24545f757f3fSDimitry Andric     AtLeastOneParmToFix = true;
24555f757f3fSDimitry Andric   }
24565f757f3fSDimitry Andric   if (!AtLeastOneParmToFix)
24575f757f3fSDimitry Andric     // No need to create function overloads:
24585f757f3fSDimitry Andric     return {};
245906c3fb27SDimitry Andric   // FIXME Respect indentation of the original code.
246006c3fb27SDimitry Andric 
246106c3fb27SDimitry Andric   // A lambda that creates the text representation of a function declaration
24625f757f3fSDimitry Andric   // with the new type signatures:
246306c3fb27SDimitry Andric   const auto NewOverloadSignatureCreator =
24645f757f3fSDimitry Andric       [&SM, &LangOpts, &NewTysTexts,
24655f757f3fSDimitry Andric        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
246606c3fb27SDimitry Andric     std::stringstream SS;
246706c3fb27SDimitry Andric 
246806c3fb27SDimitry Andric     SS << ";";
246906c3fb27SDimitry Andric     SS << getEndOfLine().str();
247006c3fb27SDimitry Andric     // Append: ret-type func-name "("
247106c3fb27SDimitry Andric     if (auto Prefix = getRangeText(
247206c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
247306c3fb27SDimitry Andric             SM, LangOpts))
247406c3fb27SDimitry Andric       SS << Prefix->str();
247506c3fb27SDimitry Andric     else
247606c3fb27SDimitry Andric       return std::nullopt; // give up
247706c3fb27SDimitry Andric     // Append: parameter-type-list
247806c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
247906c3fb27SDimitry Andric 
248006c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
248106c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
248206c3fb27SDimitry Andric 
248306c3fb27SDimitry Andric       if (Parm->isImplicit())
248406c3fb27SDimitry Andric         continue;
24855f757f3fSDimitry Andric       if (ParmsMask[i]) {
24865f757f3fSDimitry Andric         // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
24875f757f3fSDimitry Andric         // new type:
24885f757f3fSDimitry Andric         SS << NewTysTexts[i];
248906c3fb27SDimitry Andric         // print parameter name if provided:
249006c3fb27SDimitry Andric         if (IdentifierInfo *II = Parm->getIdentifier())
24915f757f3fSDimitry Andric           SS << ' ' << II->getName().str();
2492*0fca6ea1SDimitry Andric       } else if (auto ParmTypeText =
2493*0fca6ea1SDimitry Andric                      getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
24945f757f3fSDimitry Andric                                   SM, LangOpts)) {
249506c3fb27SDimitry Andric         // print the whole `Parm` without modification:
249606c3fb27SDimitry Andric         SS << ParmTypeText->str();
249706c3fb27SDimitry Andric       } else
249806c3fb27SDimitry Andric         return std::nullopt; // something wrong, give up
249906c3fb27SDimitry Andric       if (i != NumParms - 1)
250006c3fb27SDimitry Andric         SS << ", ";
250106c3fb27SDimitry Andric     }
250206c3fb27SDimitry Andric     SS << ")";
250306c3fb27SDimitry Andric     return SS.str();
250406c3fb27SDimitry Andric   };
250506c3fb27SDimitry Andric 
250606c3fb27SDimitry Andric   // A lambda that creates the text representation of a function definition with
250706c3fb27SDimitry Andric   // the original signature:
250806c3fb27SDimitry Andric   const auto OldOverloadDefCreator =
25095f757f3fSDimitry Andric       [&Handler, &SM, &LangOpts, &NewTysTexts,
25105f757f3fSDimitry Andric        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
251106c3fb27SDimitry Andric     std::stringstream SS;
251206c3fb27SDimitry Andric 
251306c3fb27SDimitry Andric     SS << getEndOfLine().str();
251406c3fb27SDimitry Andric     // Append: attr-name ret-type func-name "(" param-list ")" "{"
251506c3fb27SDimitry Andric     if (auto FDPrefix = getRangeText(
251606c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
251706c3fb27SDimitry Andric             LangOpts))
251806c3fb27SDimitry Andric       SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
251906c3fb27SDimitry Andric          << FDPrefix->str() << "{";
252006c3fb27SDimitry Andric     else
252106c3fb27SDimitry Andric       return std::nullopt;
252206c3fb27SDimitry Andric     // Append: "return" func-name "("
252306c3fb27SDimitry Andric     if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
252406c3fb27SDimitry Andric       SS << "return " << FunQualName->str() << "(";
252506c3fb27SDimitry Andric     else
252606c3fb27SDimitry Andric       return std::nullopt;
252706c3fb27SDimitry Andric 
252806c3fb27SDimitry Andric     // Append: arg-list
252906c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
253006c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
253106c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
253206c3fb27SDimitry Andric 
253306c3fb27SDimitry Andric       if (Parm->isImplicit())
253406c3fb27SDimitry Andric         continue;
253506c3fb27SDimitry Andric       // FIXME: If a parameter has no name, it is unused in the
253606c3fb27SDimitry Andric       // definition. So we could just leave it as it is.
253706c3fb27SDimitry Andric       if (!Parm->getIdentifier())
253806c3fb27SDimitry Andric         // If a parameter of a function definition has no name:
253906c3fb27SDimitry Andric         return std::nullopt;
25405f757f3fSDimitry Andric       if (ParmsMask[i])
254106c3fb27SDimitry Andric         // This is our spanified paramter!
25425f757f3fSDimitry Andric         SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
25435f757f3fSDimitry Andric            << ", " << getUserFillPlaceHolder("size") << ")";
254406c3fb27SDimitry Andric       else
254506c3fb27SDimitry Andric         SS << Parm->getIdentifier()->getName().str();
254606c3fb27SDimitry Andric       if (i != NumParms - 1)
254706c3fb27SDimitry Andric         SS << ", ";
254806c3fb27SDimitry Andric     }
254906c3fb27SDimitry Andric     // finish call and the body
255006c3fb27SDimitry Andric     SS << ");}" << getEndOfLine().str();
255106c3fb27SDimitry Andric     // FIXME: 80-char line formatting?
255206c3fb27SDimitry Andric     return SS.str();
255306c3fb27SDimitry Andric   };
255406c3fb27SDimitry Andric 
255506c3fb27SDimitry Andric   FixItList FixIts{};
255606c3fb27SDimitry Andric   for (FunctionDecl *FReDecl : FD->redecls()) {
255706c3fb27SDimitry Andric     std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
255806c3fb27SDimitry Andric 
255906c3fb27SDimitry Andric     if (!Loc)
256006c3fb27SDimitry Andric       return {};
256106c3fb27SDimitry Andric     if (FReDecl->isThisDeclarationADefinition()) {
256206c3fb27SDimitry Andric       assert(FReDecl == FD && "inconsistent function definition");
256306c3fb27SDimitry Andric       // Inserts a definition with the old signature to the end of
256406c3fb27SDimitry Andric       // `FReDecl`:
25655f757f3fSDimitry Andric       if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
256606c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
256706c3fb27SDimitry Andric       else
256806c3fb27SDimitry Andric         return {}; // give up
256906c3fb27SDimitry Andric     } else {
257006c3fb27SDimitry Andric       // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
257106c3fb27SDimitry Andric       if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
257206c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(
257306c3fb27SDimitry Andric             FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
257406c3fb27SDimitry Andric                                         FReDecl->getBeginLoc(), " ")));
257506c3fb27SDimitry Andric       }
257606c3fb27SDimitry Andric       // Inserts a declaration with the new signature to the end of `FReDecl`:
25775f757f3fSDimitry Andric       if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
257806c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
257906c3fb27SDimitry Andric       else
258006c3fb27SDimitry Andric         return {};
258106c3fb27SDimitry Andric     }
258206c3fb27SDimitry Andric   }
258306c3fb27SDimitry Andric   return FixIts;
258406c3fb27SDimitry Andric }
258506c3fb27SDimitry Andric 
25865f757f3fSDimitry Andric // To fix a `ParmVarDecl` to be of `std::span` type.
258706c3fb27SDimitry Andric static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
258806c3fb27SDimitry Andric                                   UnsafeBufferUsageHandler &Handler) {
25895f757f3fSDimitry Andric   if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) {
25905f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
25915f757f3fSDimitry Andric     return {};
25925f757f3fSDimitry Andric   }
25935f757f3fSDimitry Andric   if (PVD->hasDefaultArg()) {
259406c3fb27SDimitry Andric     // FIXME: generate fix-its for default values:
25955f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
259606c3fb27SDimitry Andric     return {};
25975f757f3fSDimitry Andric   }
259806c3fb27SDimitry Andric 
259906c3fb27SDimitry Andric   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
260006c3fb27SDimitry Andric   std::optional<std::string> PteTyText = getPointeeTypeText(
260106c3fb27SDimitry Andric       PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
260206c3fb27SDimitry Andric 
26035f757f3fSDimitry Andric   if (!PteTyText) {
26045f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
260506c3fb27SDimitry Andric     return {};
26065f757f3fSDimitry Andric   }
260706c3fb27SDimitry Andric 
260806c3fb27SDimitry Andric   std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
260906c3fb27SDimitry Andric 
26105f757f3fSDimitry Andric   if (!PVDNameText) {
26115f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
261206c3fb27SDimitry Andric     return {};
26135f757f3fSDimitry Andric   }
261406c3fb27SDimitry Andric 
261506c3fb27SDimitry Andric   std::stringstream SS;
26165f757f3fSDimitry Andric   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
261706c3fb27SDimitry Andric 
261806c3fb27SDimitry Andric   if (PteTyQualifiers)
26195f757f3fSDimitry Andric     // Append qualifiers if they exist:
26205f757f3fSDimitry Andric     SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
26215f757f3fSDimitry Andric   else
26225f757f3fSDimitry Andric     SS << getSpanTypeText(*PteTyText);
262306c3fb27SDimitry Andric   // Append qualifiers to the type of the parameter:
262406c3fb27SDimitry Andric   if (PVD->getType().hasQualifiers())
26255f757f3fSDimitry Andric     SS << ' ' << PVD->getType().getQualifiers().getAsString();
262606c3fb27SDimitry Andric   // Append parameter's name:
26275f757f3fSDimitry Andric   SS << ' ' << PVDNameText->str();
26285f757f3fSDimitry Andric   // Add replacement fix-it:
26295f757f3fSDimitry Andric   return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
263006c3fb27SDimitry Andric }
263106c3fb27SDimitry Andric 
263206c3fb27SDimitry Andric static FixItList fixVariableWithSpan(const VarDecl *VD,
263306c3fb27SDimitry Andric                                      const DeclUseTracker &Tracker,
263406c3fb27SDimitry Andric                                      ASTContext &Ctx,
263506c3fb27SDimitry Andric                                      UnsafeBufferUsageHandler &Handler) {
263606c3fb27SDimitry Andric   const DeclStmt *DS = Tracker.lookupDecl(VD);
26375f757f3fSDimitry Andric   if (!DS) {
2638*0fca6ea1SDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD,
2639*0fca6ea1SDimitry Andric                          " : variables declared this way not implemented yet");
26405f757f3fSDimitry Andric     return {};
26415f757f3fSDimitry Andric   }
264206c3fb27SDimitry Andric   if (!DS->isSingleDecl()) {
264306c3fb27SDimitry Andric     // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
26445f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
264506c3fb27SDimitry Andric     return {};
264606c3fb27SDimitry Andric   }
264706c3fb27SDimitry Andric   // Currently DS is an unused variable but we'll need it when
264806c3fb27SDimitry Andric   // non-single decls are implemented, where the pointee type name
264906c3fb27SDimitry Andric   // and the '*' are spread around the place.
265006c3fb27SDimitry Andric   (void)DS;
265106c3fb27SDimitry Andric 
265206c3fb27SDimitry Andric   // FIXME: handle cases where DS has multiple declarations
26535f757f3fSDimitry Andric   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
265406c3fb27SDimitry Andric }
265506c3fb27SDimitry Andric 
2656*0fca6ea1SDimitry Andric static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
2657*0fca6ea1SDimitry Andric                                      UnsafeBufferUsageHandler &Handler) {
2658*0fca6ea1SDimitry Andric   FixItList FixIts{};
2659*0fca6ea1SDimitry Andric 
2660*0fca6ea1SDimitry Andric   // Note: the code below expects the declaration to not use any type sugar like
2661*0fca6ea1SDimitry Andric   // typedef.
2662*0fca6ea1SDimitry Andric   if (auto CAT = dyn_cast<clang::ConstantArrayType>(D->getType())) {
2663*0fca6ea1SDimitry Andric     const QualType &ArrayEltT = CAT->getElementType();
2664*0fca6ea1SDimitry Andric     assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
2665*0fca6ea1SDimitry Andric     // FIXME: support multi-dimensional arrays
2666*0fca6ea1SDimitry Andric     if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
2667*0fca6ea1SDimitry Andric       return {};
2668*0fca6ea1SDimitry Andric 
2669*0fca6ea1SDimitry Andric     const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D);
2670*0fca6ea1SDimitry Andric 
2671*0fca6ea1SDimitry Andric     // Get the spelling of the element type as written in the source file
2672*0fca6ea1SDimitry Andric     // (including macros, etc.).
2673*0fca6ea1SDimitry Andric     auto MaybeElemTypeTxt =
2674*0fca6ea1SDimitry Andric         getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(),
2675*0fca6ea1SDimitry Andric                      Ctx.getLangOpts());
2676*0fca6ea1SDimitry Andric     if (!MaybeElemTypeTxt)
2677*0fca6ea1SDimitry Andric       return {};
2678*0fca6ea1SDimitry Andric     const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
2679*0fca6ea1SDimitry Andric 
2680*0fca6ea1SDimitry Andric     // Find the '[' token.
2681*0fca6ea1SDimitry Andric     std::optional<Token> NextTok = Lexer::findNextToken(
2682*0fca6ea1SDimitry Andric         IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts());
2683*0fca6ea1SDimitry Andric     while (NextTok && !NextTok->is(tok::l_square) &&
2684*0fca6ea1SDimitry Andric            NextTok->getLocation() <= D->getSourceRange().getEnd())
2685*0fca6ea1SDimitry Andric       NextTok = Lexer::findNextToken(NextTok->getLocation(),
2686*0fca6ea1SDimitry Andric                                      Ctx.getSourceManager(), Ctx.getLangOpts());
2687*0fca6ea1SDimitry Andric     if (!NextTok)
2688*0fca6ea1SDimitry Andric       return {};
2689*0fca6ea1SDimitry Andric     const SourceLocation LSqBracketLoc = NextTok->getLocation();
2690*0fca6ea1SDimitry Andric 
2691*0fca6ea1SDimitry Andric     // Get the spelling of the array size as written in the source file
2692*0fca6ea1SDimitry Andric     // (including macros, etc.).
2693*0fca6ea1SDimitry Andric     auto MaybeArraySizeTxt = getRangeText(
2694*0fca6ea1SDimitry Andric         {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
2695*0fca6ea1SDimitry Andric         Ctx.getSourceManager(), Ctx.getLangOpts());
2696*0fca6ea1SDimitry Andric     if (!MaybeArraySizeTxt)
2697*0fca6ea1SDimitry Andric       return {};
2698*0fca6ea1SDimitry Andric     const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
2699*0fca6ea1SDimitry Andric     if (ArraySizeTxt.empty()) {
2700*0fca6ea1SDimitry Andric       // FIXME: Support array size getting determined from the initializer.
2701*0fca6ea1SDimitry Andric       // Examples:
2702*0fca6ea1SDimitry Andric       //    int arr1[] = {0, 1, 2};
2703*0fca6ea1SDimitry Andric       //    int arr2{3, 4, 5};
2704*0fca6ea1SDimitry Andric       // We might be able to preserve the non-specified size with `auto` and
2705*0fca6ea1SDimitry Andric       // `std::to_array`:
2706*0fca6ea1SDimitry Andric       //    auto arr1 = std::to_array<int>({0, 1, 2});
2707*0fca6ea1SDimitry Andric       return {};
2708*0fca6ea1SDimitry Andric     }
2709*0fca6ea1SDimitry Andric 
2710*0fca6ea1SDimitry Andric     std::optional<StringRef> IdentText =
2711*0fca6ea1SDimitry Andric         getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
2712*0fca6ea1SDimitry Andric 
2713*0fca6ea1SDimitry Andric     if (!IdentText) {
2714*0fca6ea1SDimitry Andric       DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
2715*0fca6ea1SDimitry Andric       return {};
2716*0fca6ea1SDimitry Andric     }
2717*0fca6ea1SDimitry Andric 
2718*0fca6ea1SDimitry Andric     SmallString<32> Replacement;
2719*0fca6ea1SDimitry Andric     raw_svector_ostream OS(Replacement);
2720*0fca6ea1SDimitry Andric     OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
2721*0fca6ea1SDimitry Andric        << IdentText->str();
2722*0fca6ea1SDimitry Andric 
2723*0fca6ea1SDimitry Andric     FixIts.push_back(FixItHint::CreateReplacement(
2724*0fca6ea1SDimitry Andric         SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
2725*0fca6ea1SDimitry Andric   }
2726*0fca6ea1SDimitry Andric 
2727*0fca6ea1SDimitry Andric   return FixIts;
2728*0fca6ea1SDimitry Andric }
2729*0fca6ea1SDimitry Andric 
2730*0fca6ea1SDimitry Andric static FixItList fixVariableWithArray(const VarDecl *VD,
2731*0fca6ea1SDimitry Andric                                       const DeclUseTracker &Tracker,
2732*0fca6ea1SDimitry Andric                                       const ASTContext &Ctx,
2733*0fca6ea1SDimitry Andric                                       UnsafeBufferUsageHandler &Handler) {
2734*0fca6ea1SDimitry Andric   const DeclStmt *DS = Tracker.lookupDecl(VD);
2735*0fca6ea1SDimitry Andric   assert(DS && "Fixing non-local variables not implemented yet!");
2736*0fca6ea1SDimitry Andric   if (!DS->isSingleDecl()) {
2737*0fca6ea1SDimitry Andric     // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2738*0fca6ea1SDimitry Andric     return {};
2739*0fca6ea1SDimitry Andric   }
2740*0fca6ea1SDimitry Andric   // Currently DS is an unused variable but we'll need it when
2741*0fca6ea1SDimitry Andric   // non-single decls are implemented, where the pointee type name
2742*0fca6ea1SDimitry Andric   // and the '*' are spread around the place.
2743*0fca6ea1SDimitry Andric   (void)DS;
2744*0fca6ea1SDimitry Andric 
2745*0fca6ea1SDimitry Andric   // FIXME: handle cases where DS has multiple declarations
2746*0fca6ea1SDimitry Andric   return fixVarDeclWithArray(VD, Ctx, Handler);
2747*0fca6ea1SDimitry Andric }
2748*0fca6ea1SDimitry Andric 
274906c3fb27SDimitry Andric // TODO: we should be consistent to use `std::nullopt` to represent no-fix due
275006c3fb27SDimitry Andric // to any unexpected problem.
275106c3fb27SDimitry Andric static FixItList
2752*0fca6ea1SDimitry Andric fixVariable(const VarDecl *VD, FixitStrategy::Kind K,
275306c3fb27SDimitry Andric             /* The function decl under analysis */ const Decl *D,
275406c3fb27SDimitry Andric             const DeclUseTracker &Tracker, ASTContext &Ctx,
275506c3fb27SDimitry Andric             UnsafeBufferUsageHandler &Handler) {
275606c3fb27SDimitry Andric   if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
275706c3fb27SDimitry Andric     auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
27585f757f3fSDimitry Andric     if (!FD || FD != D) {
275906c3fb27SDimitry Andric       // `FD != D` means that `PVD` belongs to a function that is not being
276006c3fb27SDimitry Andric       // analyzed currently.  Thus `FD` may not be complete.
27615f757f3fSDimitry Andric       DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
276206c3fb27SDimitry Andric       return {};
27635f757f3fSDimitry Andric     }
276406c3fb27SDimitry Andric 
276506c3fb27SDimitry Andric     // TODO If function has a try block we can't change params unless we check
276606c3fb27SDimitry Andric     // also its catch block for their use.
276706c3fb27SDimitry Andric     // FIXME We might support static class methods, some select methods,
276806c3fb27SDimitry Andric     // operators and possibly lamdas.
276906c3fb27SDimitry Andric     if (FD->isMain() || FD->isConstexpr() ||
277006c3fb27SDimitry Andric         FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
277106c3fb27SDimitry Andric         FD->isVariadic() ||
277206c3fb27SDimitry Andric         // also covers call-operator of lamdas
277306c3fb27SDimitry Andric         isa<CXXMethodDecl>(FD) ||
277406c3fb27SDimitry Andric         // skip when the function body is a try-block
277506c3fb27SDimitry Andric         (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
27765f757f3fSDimitry Andric         FD->isOverloadedOperator()) {
27775f757f3fSDimitry Andric       DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
277806c3fb27SDimitry Andric       return {}; // TODO test all these cases
277906c3fb27SDimitry Andric     }
27805f757f3fSDimitry Andric   }
278106c3fb27SDimitry Andric 
278206c3fb27SDimitry Andric   switch (K) {
2783*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Span: {
278406c3fb27SDimitry Andric     if (VD->getType()->isPointerType()) {
278506c3fb27SDimitry Andric       if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
278606c3fb27SDimitry Andric         return fixParamWithSpan(PVD, Ctx, Handler);
278706c3fb27SDimitry Andric 
278806c3fb27SDimitry Andric       if (VD->isLocalVarDecl())
278906c3fb27SDimitry Andric         return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
279006c3fb27SDimitry Andric     }
27915f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
279206c3fb27SDimitry Andric     return {};
279306c3fb27SDimitry Andric   }
2794*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Array: {
2795*0fca6ea1SDimitry Andric     if (VD->isLocalVarDecl() &&
2796*0fca6ea1SDimitry Andric         isa<clang::ConstantArrayType>(VD->getType().getCanonicalType()))
2797*0fca6ea1SDimitry Andric       return fixVariableWithArray(VD, Tracker, Ctx, Handler);
2798*0fca6ea1SDimitry Andric 
2799*0fca6ea1SDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
2800*0fca6ea1SDimitry Andric     return {};
2801*0fca6ea1SDimitry Andric   }
2802*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Iterator:
2803*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Vector:
2804*0fca6ea1SDimitry Andric     llvm_unreachable("FixitStrategy not implemented yet!");
2805*0fca6ea1SDimitry Andric   case FixitStrategy::Kind::Wontfix:
280606c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
280706c3fb27SDimitry Andric   }
280806c3fb27SDimitry Andric   llvm_unreachable("Unknown strategy!");
280906c3fb27SDimitry Andric }
281006c3fb27SDimitry Andric 
281106c3fb27SDimitry Andric // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
281206c3fb27SDimitry Andric // `RemoveRange` of 'h' overlaps with a macro use.
281306c3fb27SDimitry Andric static bool overlapWithMacro(const FixItList &FixIts) {
281406c3fb27SDimitry Andric   // FIXME: For now we only check if the range (or the first token) is (part of)
281506c3fb27SDimitry Andric   // a macro expansion.  Ideally, we want to check for all tokens in the range.
281606c3fb27SDimitry Andric   return llvm::any_of(FixIts, [](const FixItHint &Hint) {
281706c3fb27SDimitry Andric     auto Range = Hint.RemoveRange;
281806c3fb27SDimitry Andric     if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
281906c3fb27SDimitry Andric       // If the range (or the first token) is (part of) a macro expansion:
282006c3fb27SDimitry Andric       return true;
282106c3fb27SDimitry Andric     return false;
282206c3fb27SDimitry Andric   });
282306c3fb27SDimitry Andric }
282406c3fb27SDimitry Andric 
28255f757f3fSDimitry Andric // Returns true iff `VD` is a parameter of the declaration `D`:
28265f757f3fSDimitry Andric static bool isParameterOf(const VarDecl *VD, const Decl *D) {
28275f757f3fSDimitry Andric   return isa<ParmVarDecl>(VD) &&
28285f757f3fSDimitry Andric          VD->getDeclContext() == dyn_cast<DeclContext>(D);
282906c3fb27SDimitry Andric }
283006c3fb27SDimitry Andric 
28315f757f3fSDimitry Andric // Erases variables in `FixItsForVariable`, if such a variable has an unfixable
28325f757f3fSDimitry Andric // group mate.  A variable `v` is unfixable iff `FixItsForVariable` does not
28335f757f3fSDimitry Andric // contain `v`.
28345f757f3fSDimitry Andric static void eraseVarsForUnfixableGroupMates(
28355f757f3fSDimitry Andric     std::map<const VarDecl *, FixItList> &FixItsForVariable,
28365f757f3fSDimitry Andric     const VariableGroupsManager &VarGrpMgr) {
28375f757f3fSDimitry Andric   // Variables will be removed from `FixItsForVariable`:
28385f757f3fSDimitry Andric   SmallVector<const VarDecl *, 8> ToErase;
28395f757f3fSDimitry Andric 
28405f757f3fSDimitry Andric   for (const auto &[VD, Ignore] : FixItsForVariable) {
28415f757f3fSDimitry Andric     VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
28425f757f3fSDimitry Andric     if (llvm::any_of(Grp,
28435f757f3fSDimitry Andric                      [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
28445f757f3fSDimitry Andric                        return !FixItsForVariable.count(GrpMember);
28455f757f3fSDimitry Andric                      })) {
28465f757f3fSDimitry Andric       // At least one group member cannot be fixed, so we have to erase the
28475f757f3fSDimitry Andric       // whole group:
28485f757f3fSDimitry Andric       for (const VarDecl *Member : Grp)
28495f757f3fSDimitry Andric         ToErase.push_back(Member);
28505f757f3fSDimitry Andric     }
28515f757f3fSDimitry Andric   }
28525f757f3fSDimitry Andric   for (auto *VarToErase : ToErase)
28535f757f3fSDimitry Andric     FixItsForVariable.erase(VarToErase);
28545f757f3fSDimitry Andric }
28555f757f3fSDimitry Andric 
28565f757f3fSDimitry Andric // Returns the fix-its that create bounds-safe function overloads for the
28575f757f3fSDimitry Andric // function `D`, if `D`'s parameters will be changed to safe-types through
28585f757f3fSDimitry Andric // fix-its in `FixItsForVariable`.
28595f757f3fSDimitry Andric //
28605f757f3fSDimitry Andric // NOTE: In case `D`'s parameters will be changed but bounds-safe function
28615f757f3fSDimitry Andric // overloads cannot created, the whole group that contains the parameters will
28625f757f3fSDimitry Andric // be erased from `FixItsForVariable`.
28635f757f3fSDimitry Andric static FixItList createFunctionOverloadsForParms(
28645f757f3fSDimitry Andric     std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
28655f757f3fSDimitry Andric     const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
2866*0fca6ea1SDimitry Andric     const FixitStrategy &S, ASTContext &Ctx,
2867*0fca6ea1SDimitry Andric     UnsafeBufferUsageHandler &Handler) {
28685f757f3fSDimitry Andric   FixItList FixItsSharedByParms{};
28695f757f3fSDimitry Andric 
28705f757f3fSDimitry Andric   std::optional<FixItList> OverloadFixes =
28715f757f3fSDimitry Andric       createOverloadsForFixedParams(S, FD, Ctx, Handler);
28725f757f3fSDimitry Andric 
28735f757f3fSDimitry Andric   if (OverloadFixes) {
28745f757f3fSDimitry Andric     FixItsSharedByParms.append(*OverloadFixes);
28755f757f3fSDimitry Andric   } else {
28765f757f3fSDimitry Andric     // Something wrong in generating `OverloadFixes`, need to remove the
28775f757f3fSDimitry Andric     // whole group, where parameters are in, from `FixItsForVariable` (Note
28785f757f3fSDimitry Andric     // that all parameters should be in the same group):
28795f757f3fSDimitry Andric     for (auto *Member : VarGrpMgr.getGroupOfParms())
28805f757f3fSDimitry Andric       FixItsForVariable.erase(Member);
28815f757f3fSDimitry Andric   }
28825f757f3fSDimitry Andric   return FixItsSharedByParms;
28835f757f3fSDimitry Andric }
28845f757f3fSDimitry Andric 
28855f757f3fSDimitry Andric // Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
2886bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList>
2887*0fca6ea1SDimitry Andric getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
288806c3fb27SDimitry Andric           ASTContext &Ctx,
288906c3fb27SDimitry Andric           /* The function decl under analysis */ const Decl *D,
289006c3fb27SDimitry Andric           const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
28915f757f3fSDimitry Andric           const VariableGroupsManager &VarGrpMgr) {
28925f757f3fSDimitry Andric   // `FixItsForVariable` will map each variable to a set of fix-its directly
28935f757f3fSDimitry Andric   // associated to the variable itself.  Fix-its of distinct variables in
28945f757f3fSDimitry Andric   // `FixItsForVariable` are disjoint.
2895bdd1243dSDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariable;
28965f757f3fSDimitry Andric 
28975f757f3fSDimitry Andric   // Populate `FixItsForVariable` with fix-its directly associated with each
28985f757f3fSDimitry Andric   // variable.  Fix-its directly associated to a variable 'v' are the ones
28995f757f3fSDimitry Andric   // produced by the `FixableGadget`s whose claimed variable is 'v'.
290006c3fb27SDimitry Andric   for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
290106c3fb27SDimitry Andric     FixItsForVariable[VD] =
290206c3fb27SDimitry Andric         fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
290306c3fb27SDimitry Andric     // If we fail to produce Fix-It for the declaration we have to skip the
290406c3fb27SDimitry Andric     // variable entirely.
290506c3fb27SDimitry Andric     if (FixItsForVariable[VD].empty()) {
290606c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
290706c3fb27SDimitry Andric       continue;
290806c3fb27SDimitry Andric     }
2909bdd1243dSDimitry Andric     for (const auto &F : Fixables) {
291006c3fb27SDimitry Andric       std::optional<FixItList> Fixits = F->getFixits(S);
291106c3fb27SDimitry Andric 
29125f757f3fSDimitry Andric       if (Fixits) {
2913bdd1243dSDimitry Andric         FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
29145f757f3fSDimitry Andric                                      Fixits->begin(), Fixits->end());
29155f757f3fSDimitry Andric         continue;
29165f757f3fSDimitry Andric       }
29175f757f3fSDimitry Andric #ifndef NDEBUG
29185f757f3fSDimitry Andric       Handler.addDebugNoteForVar(
2919*0fca6ea1SDimitry Andric           VD, F->getSourceLoc(),
29205f757f3fSDimitry Andric           ("gadget '" + F->getDebugName() + "' refused to produce a fix")
29215f757f3fSDimitry Andric               .str());
29225f757f3fSDimitry Andric #endif
292306c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
29245f757f3fSDimitry Andric       break;
29255f757f3fSDimitry Andric     }
29265f757f3fSDimitry Andric   }
29275f757f3fSDimitry Andric 
29285f757f3fSDimitry Andric   // `FixItsForVariable` now contains only variables that can be
29295f757f3fSDimitry Andric   // fixed. A variable can be fixed if its' declaration and all Fixables
29305f757f3fSDimitry Andric   // associated to it can all be fixed.
29315f757f3fSDimitry Andric 
29325f757f3fSDimitry Andric   // To further remove from `FixItsForVariable` variables whose group mates
29335f757f3fSDimitry Andric   // cannot be fixed...
29345f757f3fSDimitry Andric   eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
29355f757f3fSDimitry Andric   // Now `FixItsForVariable` gets further reduced: a variable is in
29365f757f3fSDimitry Andric   // `FixItsForVariable` iff it can be fixed and all its group mates can be
29375f757f3fSDimitry Andric   // fixed.
29385f757f3fSDimitry Andric 
29395f757f3fSDimitry Andric   // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
29405f757f3fSDimitry Andric   // That is,  when fixing multiple parameters in one step,  these fix-its will
29415f757f3fSDimitry Andric   // be applied only once (instead of being applied per parameter).
29425f757f3fSDimitry Andric   FixItList FixItsSharedByParms{};
29435f757f3fSDimitry Andric 
29445f757f3fSDimitry Andric   if (auto *FD = dyn_cast<FunctionDecl>(D))
29455f757f3fSDimitry Andric     FixItsSharedByParms = createFunctionOverloadsForParms(
29465f757f3fSDimitry Andric         FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
29475f757f3fSDimitry Andric 
29485f757f3fSDimitry Andric   // The map that maps each variable `v` to fix-its for the whole group where
29495f757f3fSDimitry Andric   // `v` is in:
29505f757f3fSDimitry Andric   std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
29515f757f3fSDimitry Andric       FixItsForVariable};
29525f757f3fSDimitry Andric 
29535f757f3fSDimitry Andric   for (auto &[Var, Ignore] : FixItsForVariable) {
29545f757f3fSDimitry Andric     bool AnyParm = false;
29555f757f3fSDimitry Andric     const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
29565f757f3fSDimitry Andric 
29575f757f3fSDimitry Andric     for (const VarDecl *GrpMate : VarGroupForVD) {
29585f757f3fSDimitry Andric       if (Var == GrpMate)
295906c3fb27SDimitry Andric         continue;
29605f757f3fSDimitry Andric       if (FixItsForVariable.count(GrpMate))
29615f757f3fSDimitry Andric         FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
296206c3fb27SDimitry Andric     }
29635f757f3fSDimitry Andric     if (AnyParm) {
29645f757f3fSDimitry Andric       // This assertion should never fail.  Otherwise we have a bug.
29655f757f3fSDimitry Andric       assert(!FixItsSharedByParms.empty() &&
29665f757f3fSDimitry Andric              "Should not try to fix a parameter that does not belong to a "
29675f757f3fSDimitry Andric              "FunctionDecl");
29685f757f3fSDimitry Andric       FinalFixItsForVariable[Var].append(FixItsSharedByParms);
29695f757f3fSDimitry Andric     }
29705f757f3fSDimitry Andric   }
29715f757f3fSDimitry Andric   // Fix-its that will be applied in one step shall NOT:
29725f757f3fSDimitry Andric   // 1. overlap with macros or/and templates; or
29735f757f3fSDimitry Andric   // 2. conflict with each other.
29745f757f3fSDimitry Andric   // Otherwise, the fix-its will be dropped.
29755f757f3fSDimitry Andric   for (auto Iter = FinalFixItsForVariable.begin();
29765f757f3fSDimitry Andric        Iter != FinalFixItsForVariable.end();)
29775f757f3fSDimitry Andric     if (overlapWithMacro(Iter->second) ||
29785f757f3fSDimitry Andric         clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
29795f757f3fSDimitry Andric       Iter = FinalFixItsForVariable.erase(Iter);
29805f757f3fSDimitry Andric     } else
29815f757f3fSDimitry Andric       Iter++;
29825f757f3fSDimitry Andric   return FinalFixItsForVariable;
298306c3fb27SDimitry Andric }
298406c3fb27SDimitry Andric 
29855f757f3fSDimitry Andric template <typename VarDeclIterTy>
2986*0fca6ea1SDimitry Andric static FixitStrategy
29875f757f3fSDimitry Andric getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
2988*0fca6ea1SDimitry Andric   FixitStrategy S;
2989bdd1243dSDimitry Andric   for (const VarDecl *VD : UnsafeVars) {
2990*0fca6ea1SDimitry Andric     if (isa<ConstantArrayType>(VD->getType().getCanonicalType()))
2991*0fca6ea1SDimitry Andric       S.set(VD, FixitStrategy::Kind::Array);
2992*0fca6ea1SDimitry Andric     else
2993*0fca6ea1SDimitry Andric       S.set(VD, FixitStrategy::Kind::Span);
2994bdd1243dSDimitry Andric   }
2995bdd1243dSDimitry Andric   return S;
2996bdd1243dSDimitry Andric }
2997bdd1243dSDimitry Andric 
29985f757f3fSDimitry Andric //  Manages variable groups:
29995f757f3fSDimitry Andric class VariableGroupsManagerImpl : public VariableGroupsManager {
30005f757f3fSDimitry Andric   const std::vector<VarGrpTy> Groups;
30015f757f3fSDimitry Andric   const std::map<const VarDecl *, unsigned> &VarGrpMap;
30025f757f3fSDimitry Andric   const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
30035f757f3fSDimitry Andric 
30045f757f3fSDimitry Andric public:
30055f757f3fSDimitry Andric   VariableGroupsManagerImpl(
30065f757f3fSDimitry Andric       const std::vector<VarGrpTy> &Groups,
30075f757f3fSDimitry Andric       const std::map<const VarDecl *, unsigned> &VarGrpMap,
30085f757f3fSDimitry Andric       const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
30095f757f3fSDimitry Andric       : Groups(Groups), VarGrpMap(VarGrpMap),
30105f757f3fSDimitry Andric         GrpsUnionForParms(GrpsUnionForParms) {}
30115f757f3fSDimitry Andric 
30125f757f3fSDimitry Andric   VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
30135f757f3fSDimitry Andric     if (GrpsUnionForParms.contains(Var)) {
30145f757f3fSDimitry Andric       if (HasParm)
30155f757f3fSDimitry Andric         *HasParm = true;
30165f757f3fSDimitry Andric       return GrpsUnionForParms.getArrayRef();
30175f757f3fSDimitry Andric     }
30185f757f3fSDimitry Andric     if (HasParm)
30195f757f3fSDimitry Andric       *HasParm = false;
30205f757f3fSDimitry Andric 
30215f757f3fSDimitry Andric     auto It = VarGrpMap.find(Var);
30225f757f3fSDimitry Andric 
30235f757f3fSDimitry Andric     if (It == VarGrpMap.end())
30245f757f3fSDimitry Andric       return std::nullopt;
30255f757f3fSDimitry Andric     return Groups[It->second];
30265f757f3fSDimitry Andric   }
30275f757f3fSDimitry Andric 
30285f757f3fSDimitry Andric   VarGrpRef getGroupOfParms() const override {
30295f757f3fSDimitry Andric     return GrpsUnionForParms.getArrayRef();
30305f757f3fSDimitry Andric   }
30315f757f3fSDimitry Andric };
30325f757f3fSDimitry Andric 
3033bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D,
303406c3fb27SDimitry Andric                                    UnsafeBufferUsageHandler &Handler,
303506c3fb27SDimitry Andric                                    bool EmitSuggestions) {
30365f757f3fSDimitry Andric #ifndef NDEBUG
30375f757f3fSDimitry Andric   Handler.clearDebugNotes();
30385f757f3fSDimitry Andric #endif
3039bdd1243dSDimitry Andric 
30405f757f3fSDimitry Andric   assert(D && D->getBody());
3041*0fca6ea1SDimitry Andric   // We do not want to visit a Lambda expression defined inside a method
3042*0fca6ea1SDimitry Andric   // independently. Instead, it should be visited along with the outer method.
30435f757f3fSDimitry Andric   // FIXME: do we want to do the same thing for `BlockDecl`s?
304406c3fb27SDimitry Andric   if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
304506c3fb27SDimitry Andric     if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
304606c3fb27SDimitry Andric       return;
3047bdd1243dSDimitry Andric   }
3048bdd1243dSDimitry Andric 
304906c3fb27SDimitry Andric   // Do not emit fixit suggestions for functions declared in an
305006c3fb27SDimitry Andric   // extern "C" block.
305106c3fb27SDimitry Andric   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
305206c3fb27SDimitry Andric     for (FunctionDecl *FReDecl : FD->redecls()) {
305306c3fb27SDimitry Andric       if (FReDecl->isExternC()) {
305406c3fb27SDimitry Andric         EmitSuggestions = false;
305506c3fb27SDimitry Andric         break;
305606c3fb27SDimitry Andric       }
305706c3fb27SDimitry Andric     }
305806c3fb27SDimitry Andric   }
305906c3fb27SDimitry Andric 
306006c3fb27SDimitry Andric   WarningGadgetSets UnsafeOps;
306106c3fb27SDimitry Andric   FixableGadgetSets FixablesForAllVars;
306206c3fb27SDimitry Andric 
306306c3fb27SDimitry Andric   auto [FixableGadgets, WarningGadgets, Tracker] =
306406c3fb27SDimitry Andric       findGadgets(D, Handler, EmitSuggestions);
306506c3fb27SDimitry Andric 
306606c3fb27SDimitry Andric   if (!EmitSuggestions) {
306706c3fb27SDimitry Andric     // Our job is very easy without suggestions. Just warn about
306806c3fb27SDimitry Andric     // every problematic operation and consider it done. No need to deal
306906c3fb27SDimitry Andric     // with fixable gadgets, no need to group operations by variable.
307006c3fb27SDimitry Andric     for (const auto &G : WarningGadgets) {
3071*0fca6ea1SDimitry Andric       G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
30721db9f3b2SDimitry Andric                                D->getASTContext());
307306c3fb27SDimitry Andric     }
307406c3fb27SDimitry Andric 
307506c3fb27SDimitry Andric     // This return guarantees that most of the machine doesn't run when
307606c3fb27SDimitry Andric     // suggestions aren't requested.
307706c3fb27SDimitry Andric     assert(FixableGadgets.size() == 0 &&
307806c3fb27SDimitry Andric            "Fixable gadgets found but suggestions not requested!");
307906c3fb27SDimitry Andric     return;
308006c3fb27SDimitry Andric   }
308106c3fb27SDimitry Andric 
30825f757f3fSDimitry Andric   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
30835f757f3fSDimitry Andric   //  function under the analysis. No need to fix any Fixables.
30845f757f3fSDimitry Andric   if (!WarningGadgets.empty()) {
30855f757f3fSDimitry Andric     // Gadgets "claim" variables they're responsible for. Once this loop
30865f757f3fSDimitry Andric     // finishes, the tracker will only track DREs that weren't claimed by any
30875f757f3fSDimitry Andric     // gadgets, i.e. not understood by the analysis.
30885f757f3fSDimitry Andric     for (const auto &G : FixableGadgets) {
30895f757f3fSDimitry Andric       for (const auto *DRE : G->getClaimedVarUseSites()) {
30905f757f3fSDimitry Andric         Tracker.claimUse(DRE);
30915f757f3fSDimitry Andric       }
30925f757f3fSDimitry Andric     }
30935f757f3fSDimitry Andric   }
30945f757f3fSDimitry Andric 
30955f757f3fSDimitry Andric   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
30965f757f3fSDimitry Andric   // function under the analysis.  Thus, it early returns here as there is
30975f757f3fSDimitry Andric   // nothing needs to be fixed.
30985f757f3fSDimitry Andric   //
30995f757f3fSDimitry Andric   // Note this claim is based on the assumption that there is no unsafe
31005f757f3fSDimitry Andric   // variable whose declaration is invisible from the analyzing function.
31015f757f3fSDimitry Andric   // Otherwise, we need to consider if the uses of those unsafe varuables needs
31025f757f3fSDimitry Andric   // fix.
31035f757f3fSDimitry Andric   // So far, we are not fixing any global variables or class members. And,
31045f757f3fSDimitry Andric   // lambdas will be analyzed along with the enclosing function. So this early
31055f757f3fSDimitry Andric   // return is correct for now.
31065f757f3fSDimitry Andric   if (WarningGadgets.empty())
31075f757f3fSDimitry Andric     return;
31085f757f3fSDimitry Andric 
310906c3fb27SDimitry Andric   UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
311006c3fb27SDimitry Andric   FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
311106c3fb27SDimitry Andric 
311206c3fb27SDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
311306c3fb27SDimitry Andric 
3114bdd1243dSDimitry Andric   // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
311506c3fb27SDimitry Andric   for (auto it = FixablesForAllVars.byVar.cbegin();
311606c3fb27SDimitry Andric        it != FixablesForAllVars.byVar.cend();) {
311706c3fb27SDimitry Andric     // FIXME: need to deal with global variables later
31185f757f3fSDimitry Andric     if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
31195f757f3fSDimitry Andric #ifndef NDEBUG
3120*0fca6ea1SDimitry Andric       Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3121*0fca6ea1SDimitry Andric                                  ("failed to produce fixit for '" +
3122*0fca6ea1SDimitry Andric                                   it->first->getNameAsString() +
31235f757f3fSDimitry Andric                                   "' : neither local nor a parameter"));
31245f757f3fSDimitry Andric #endif
31255f757f3fSDimitry Andric       it = FixablesForAllVars.byVar.erase(it);
31265f757f3fSDimitry Andric     } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
31275f757f3fSDimitry Andric #ifndef NDEBUG
31285f757f3fSDimitry Andric       Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
31295f757f3fSDimitry Andric                                  ("failed to produce fixit for '" +
31305f757f3fSDimitry Andric                                   it->first->getNameAsString() +
31315f757f3fSDimitry Andric                                   "' : has a reference type"));
31325f757f3fSDimitry Andric #endif
31335f757f3fSDimitry Andric       it = FixablesForAllVars.byVar.erase(it);
31345f757f3fSDimitry Andric     } else if (Tracker.hasUnclaimedUses(it->first)) {
31355f757f3fSDimitry Andric       it = FixablesForAllVars.byVar.erase(it);
31365f757f3fSDimitry Andric     } else if (it->first->isInitCapture()) {
31375f757f3fSDimitry Andric #ifndef NDEBUG
3138*0fca6ea1SDimitry Andric       Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3139*0fca6ea1SDimitry Andric                                  ("failed to produce fixit for '" +
3140*0fca6ea1SDimitry Andric                                   it->first->getNameAsString() +
31415f757f3fSDimitry Andric                                   "' : init capture"));
31425f757f3fSDimitry Andric #endif
314306c3fb27SDimitry Andric       it = FixablesForAllVars.byVar.erase(it);
3144bdd1243dSDimitry Andric     } else {
3145bdd1243dSDimitry Andric       ++it;
3146bdd1243dSDimitry Andric     }
3147bdd1243dSDimitry Andric   }
3148bdd1243dSDimitry Andric 
3149*0fca6ea1SDimitry Andric #ifndef NDEBUG
3150*0fca6ea1SDimitry Andric   for (const auto &it : UnsafeOps.byVar) {
3151*0fca6ea1SDimitry Andric     const VarDecl *const UnsafeVD = it.first;
3152*0fca6ea1SDimitry Andric     auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
3153*0fca6ea1SDimitry Andric     if (UnclaimedDREs.empty())
3154*0fca6ea1SDimitry Andric       continue;
3155*0fca6ea1SDimitry Andric     const auto UnfixedVDName = UnsafeVD->getNameAsString();
3156*0fca6ea1SDimitry Andric     for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
3157*0fca6ea1SDimitry Andric       std::string UnclaimedUseTrace =
3158*0fca6ea1SDimitry Andric           getDREAncestorString(UnclaimedDRE, D->getASTContext());
3159*0fca6ea1SDimitry Andric 
3160*0fca6ea1SDimitry Andric       Handler.addDebugNoteForVar(
3161*0fca6ea1SDimitry Andric           UnsafeVD, UnclaimedDRE->getBeginLoc(),
3162*0fca6ea1SDimitry Andric           ("failed to produce fixit for '" + UnfixedVDName +
3163*0fca6ea1SDimitry Andric            "' : has an unclaimed use\nThe unclaimed DRE trace: " +
3164*0fca6ea1SDimitry Andric            UnclaimedUseTrace));
3165*0fca6ea1SDimitry Andric     }
3166*0fca6ea1SDimitry Andric   }
3167*0fca6ea1SDimitry Andric #endif
3168*0fca6ea1SDimitry Andric 
316906c3fb27SDimitry Andric   // Fixpoint iteration for pointer assignments
31705f757f3fSDimitry Andric   using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
317106c3fb27SDimitry Andric   DepMapTy DependenciesMap{};
317206c3fb27SDimitry Andric   DepMapTy PtrAssignmentGraph{};
317306c3fb27SDimitry Andric 
317406c3fb27SDimitry Andric   for (auto it : FixablesForAllVars.byVar) {
317506c3fb27SDimitry Andric     for (const FixableGadget *fixable : it.second) {
317606c3fb27SDimitry Andric       std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
317706c3fb27SDimitry Andric           fixable->getStrategyImplications();
317806c3fb27SDimitry Andric       if (ImplPair) {
31795f757f3fSDimitry Andric         std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
318006c3fb27SDimitry Andric         PtrAssignmentGraph[Impl.first].insert(Impl.second);
318106c3fb27SDimitry Andric       }
318206c3fb27SDimitry Andric     }
318306c3fb27SDimitry Andric   }
318406c3fb27SDimitry Andric 
318506c3fb27SDimitry Andric   /*
318606c3fb27SDimitry Andric    The following code does a BFS traversal of the `PtrAssignmentGraph`
318706c3fb27SDimitry Andric    considering all unsafe vars as starting nodes and constructs an undirected
318806c3fb27SDimitry Andric    graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
318906c3fb27SDimitry Andric    elimiates all variables that are unreachable from any unsafe var. In other
319006c3fb27SDimitry Andric    words, this removes all dependencies that don't include any unsafe variable
319106c3fb27SDimitry Andric    and consequently don't need any fixit generation.
319206c3fb27SDimitry Andric    Note: A careful reader would observe that the code traverses
319306c3fb27SDimitry Andric    `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
319406c3fb27SDimitry Andric    `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
319506c3fb27SDimitry Andric    achieve the same result but the one used here dramatically cuts the
319606c3fb27SDimitry Andric    amount of hoops the second part of the algorithm needs to jump, given that
319706c3fb27SDimitry Andric    a lot of these connections become "direct". The reader is advised not to
319806c3fb27SDimitry Andric    imagine how the graph is transformed because of using `Var` instead of
319906c3fb27SDimitry Andric    `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
320006c3fb27SDimitry Andric    and think about why it's equivalent later.
320106c3fb27SDimitry Andric    */
320206c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVarsDirected{};
320306c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
320406c3fb27SDimitry Andric     if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
320506c3fb27SDimitry Andric 
320606c3fb27SDimitry Andric       std::queue<const VarDecl *> QueueDirected{};
320706c3fb27SDimitry Andric       QueueDirected.push(Var);
320806c3fb27SDimitry Andric       while (!QueueDirected.empty()) {
320906c3fb27SDimitry Andric         const VarDecl *CurrentVar = QueueDirected.front();
321006c3fb27SDimitry Andric         QueueDirected.pop();
321106c3fb27SDimitry Andric         VisitedVarsDirected.insert(CurrentVar);
321206c3fb27SDimitry Andric         auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
321306c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
321406c3fb27SDimitry Andric           if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
321506c3fb27SDimitry Andric             QueueDirected.push(Adj);
321606c3fb27SDimitry Andric           }
321706c3fb27SDimitry Andric           DependenciesMap[Var].insert(Adj);
321806c3fb27SDimitry Andric           DependenciesMap[Adj].insert(Var);
321906c3fb27SDimitry Andric         }
322006c3fb27SDimitry Andric       }
322106c3fb27SDimitry Andric     }
322206c3fb27SDimitry Andric   }
322306c3fb27SDimitry Andric 
32245f757f3fSDimitry Andric   // `Groups` stores the set of Connected Components in the graph.
32255f757f3fSDimitry Andric   std::vector<VarGrpTy> Groups;
32265f757f3fSDimitry Andric   // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
32275f757f3fSDimitry Andric   // variables belong to.  Group indexes refer to the elements in `Groups`.
32285f757f3fSDimitry Andric   // `VarGrpMap` is complete in that every variable that needs fix is in it.
32295f757f3fSDimitry Andric   std::map<const VarDecl *, unsigned> VarGrpMap;
32305f757f3fSDimitry Andric   // The union group over the ones in "Groups" that contain parameters of `D`:
32315f757f3fSDimitry Andric   llvm::SetVector<const VarDecl *>
32325f757f3fSDimitry Andric       GrpsUnionForParms; // these variables need to be fixed in one step
32335f757f3fSDimitry Andric 
323406c3fb27SDimitry Andric   // Group Connected Components for Unsafe Vars
323506c3fb27SDimitry Andric   // (Dependencies based on pointer assignments)
323606c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVars{};
323706c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
323806c3fb27SDimitry Andric     if (VisitedVars.find(Var) == VisitedVars.end()) {
32395f757f3fSDimitry Andric       VarGrpTy &VarGroup = Groups.emplace_back();
324006c3fb27SDimitry Andric       std::queue<const VarDecl *> Queue{};
32415f757f3fSDimitry Andric 
324206c3fb27SDimitry Andric       Queue.push(Var);
324306c3fb27SDimitry Andric       while (!Queue.empty()) {
324406c3fb27SDimitry Andric         const VarDecl *CurrentVar = Queue.front();
324506c3fb27SDimitry Andric         Queue.pop();
324606c3fb27SDimitry Andric         VisitedVars.insert(CurrentVar);
324706c3fb27SDimitry Andric         VarGroup.push_back(CurrentVar);
324806c3fb27SDimitry Andric         auto AdjacentNodes = DependenciesMap[CurrentVar];
324906c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
325006c3fb27SDimitry Andric           if (VisitedVars.find(Adj) == VisitedVars.end()) {
325106c3fb27SDimitry Andric             Queue.push(Adj);
325206c3fb27SDimitry Andric           }
325306c3fb27SDimitry Andric         }
325406c3fb27SDimitry Andric       }
32555f757f3fSDimitry Andric 
32565f757f3fSDimitry Andric       bool HasParm = false;
32575f757f3fSDimitry Andric       unsigned GrpIdx = Groups.size() - 1;
32585f757f3fSDimitry Andric 
325906c3fb27SDimitry Andric       for (const VarDecl *V : VarGroup) {
32605f757f3fSDimitry Andric         VarGrpMap[V] = GrpIdx;
32615f757f3fSDimitry Andric         if (!HasParm && isParameterOf(V, D))
32625f757f3fSDimitry Andric           HasParm = true;
326306c3fb27SDimitry Andric       }
32645f757f3fSDimitry Andric       if (HasParm)
32655f757f3fSDimitry Andric         GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
326606c3fb27SDimitry Andric     }
326706c3fb27SDimitry Andric   }
326806c3fb27SDimitry Andric 
32695f757f3fSDimitry Andric   // Remove a `FixableGadget` if the associated variable is not in the graph
32705f757f3fSDimitry Andric   // computed above.  We do not want to generate fix-its for such variables,
32715f757f3fSDimitry Andric   // since they are neither warned nor reachable from a warned one.
32725f757f3fSDimitry Andric   //
32735f757f3fSDimitry Andric   // Note a variable is not warned if it is not directly used in any unsafe
32745f757f3fSDimitry Andric   // operation. A variable `v` is NOT reachable from an unsafe variable, if it
32755f757f3fSDimitry Andric   // does not exist another variable `u` such that `u` is warned and fixing `u`
32765f757f3fSDimitry Andric   // (transitively) implicates fixing `v`.
32775f757f3fSDimitry Andric   //
32785f757f3fSDimitry Andric   // For example,
32795f757f3fSDimitry Andric   // ```
32805f757f3fSDimitry Andric   // void f(int * p) {
32815f757f3fSDimitry Andric   //   int * a = p; *p = 0;
32825f757f3fSDimitry Andric   // }
32835f757f3fSDimitry Andric   // ```
32845f757f3fSDimitry Andric   // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
32855f757f3fSDimitry Andric   // warned nor reachable from a warned one.  If we add `a[5] = 0` to the end of
32865f757f3fSDimitry Andric   // the function above, `p` becomes reachable from a warned variable.
32875f757f3fSDimitry Andric   for (auto I = FixablesForAllVars.byVar.begin();
32885f757f3fSDimitry Andric        I != FixablesForAllVars.byVar.end();) {
32895f757f3fSDimitry Andric     // Note `VisitedVars` contain all the variables in the graph:
32905f757f3fSDimitry Andric     if (!VisitedVars.count((*I).first)) {
32915f757f3fSDimitry Andric       // no such var in graph:
32925f757f3fSDimitry Andric       I = FixablesForAllVars.byVar.erase(I);
32935f757f3fSDimitry Andric     } else
32945f757f3fSDimitry Andric       ++I;
32955f757f3fSDimitry Andric   }
329606c3fb27SDimitry Andric 
32975f757f3fSDimitry Andric   // We assign strategies to variables that are 1) in the graph and 2) can be
32985f757f3fSDimitry Andric   // fixed. Other variables have the default "Won't fix" strategy.
3299*0fca6ea1SDimitry Andric   FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
33005f757f3fSDimitry Andric       VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
33015f757f3fSDimitry Andric         // If a warned variable has no "Fixable", it is considered unfixable:
33025f757f3fSDimitry Andric         return FixablesForAllVars.byVar.count(V);
33035f757f3fSDimitry Andric       }));
33045f757f3fSDimitry Andric   VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
33055f757f3fSDimitry Andric 
33065f757f3fSDimitry Andric   if (isa<NamedDecl>(D))
33075f757f3fSDimitry Andric     // The only case where `D` is not a `NamedDecl` is when `D` is a
33085f757f3fSDimitry Andric     // `BlockDecl`. Let's not fix variables in blocks for now
330906c3fb27SDimitry Andric     FixItsForVariableGroup =
331006c3fb27SDimitry Andric         getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
33115f757f3fSDimitry Andric                   Tracker, Handler, VarGrpMgr);
3312bdd1243dSDimitry Andric 
3313bdd1243dSDimitry Andric   for (const auto &G : UnsafeOps.noVar) {
3314*0fca6ea1SDimitry Andric     G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
33151db9f3b2SDimitry Andric                              D->getASTContext());
3316bdd1243dSDimitry Andric   }
3317bdd1243dSDimitry Andric 
3318bdd1243dSDimitry Andric   for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
331906c3fb27SDimitry Andric     auto FixItsIt = FixItsForVariableGroup.find(VD);
33205f757f3fSDimitry Andric     Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
332106c3fb27SDimitry Andric                                       FixItsIt != FixItsForVariableGroup.end()
3322bdd1243dSDimitry Andric                                           ? std::move(FixItsIt->second)
33235f757f3fSDimitry Andric                                           : FixItList{},
3324*0fca6ea1SDimitry Andric                                       D, NaiveStrategy);
3325bdd1243dSDimitry Andric     for (const auto &G : WarningGadgets) {
3326*0fca6ea1SDimitry Andric       G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
33271db9f3b2SDimitry Andric                                D->getASTContext());
3328bdd1243dSDimitry Andric     }
3329bdd1243dSDimitry Andric   }
3330bdd1243dSDimitry Andric }
3331