1bdd1243dSDimitry Andric //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===//
2bdd1243dSDimitry Andric //
3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bdd1243dSDimitry Andric //
7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
8bdd1243dSDimitry Andric 
9bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
1006c3fb27SDimitry Andric #include "clang/AST/Decl.h"
115f757f3fSDimitry Andric #include "clang/AST/Expr.h"
12bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
135f757f3fSDimitry Andric #include "clang/AST/StmtVisitor.h"
14bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
1506c3fb27SDimitry Andric #include "clang/Lex/Lexer.h"
1606c3fb27SDimitry Andric #include "clang/Lex/Preprocessor.h"
17bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h"
18bdd1243dSDimitry Andric #include <memory>
19bdd1243dSDimitry Andric #include <optional>
2006c3fb27SDimitry Andric #include <sstream>
2106c3fb27SDimitry Andric #include <queue>
22bdd1243dSDimitry Andric 
23bdd1243dSDimitry Andric using namespace llvm;
24bdd1243dSDimitry Andric using namespace clang;
25bdd1243dSDimitry Andric using namespace ast_matchers;
26bdd1243dSDimitry Andric 
275f757f3fSDimitry Andric #ifndef NDEBUG
285f757f3fSDimitry Andric namespace {
295f757f3fSDimitry Andric class StmtDebugPrinter
305f757f3fSDimitry Andric     : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
315f757f3fSDimitry Andric public:
VisitStmt(const Stmt * S)325f757f3fSDimitry Andric   std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
335f757f3fSDimitry Andric 
VisitBinaryOperator(const BinaryOperator * BO)345f757f3fSDimitry Andric   std::string VisitBinaryOperator(const BinaryOperator *BO) {
355f757f3fSDimitry Andric     return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
365f757f3fSDimitry Andric   }
375f757f3fSDimitry Andric 
VisitUnaryOperator(const UnaryOperator * UO)385f757f3fSDimitry Andric   std::string VisitUnaryOperator(const UnaryOperator *UO) {
395f757f3fSDimitry Andric     return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
405f757f3fSDimitry Andric   }
415f757f3fSDimitry Andric 
VisitImplicitCastExpr(const ImplicitCastExpr * ICE)425f757f3fSDimitry Andric   std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
435f757f3fSDimitry Andric     return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
445f757f3fSDimitry Andric   }
455f757f3fSDimitry Andric };
465f757f3fSDimitry Andric 
475f757f3fSDimitry Andric // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
485f757f3fSDimitry Andric // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
getDREAncestorString(const DeclRefExpr * DRE,ASTContext & Ctx)495f757f3fSDimitry Andric static std::string getDREAncestorString(const DeclRefExpr *DRE,
505f757f3fSDimitry Andric                                         ASTContext &Ctx) {
515f757f3fSDimitry Andric   std::stringstream SS;
525f757f3fSDimitry Andric   const Stmt *St = DRE;
535f757f3fSDimitry Andric   StmtDebugPrinter StmtPriner;
545f757f3fSDimitry Andric 
555f757f3fSDimitry Andric   do {
565f757f3fSDimitry Andric     SS << StmtPriner.Visit(St);
575f757f3fSDimitry Andric 
585f757f3fSDimitry Andric     DynTypedNodeList StParents = Ctx.getParents(*St);
595f757f3fSDimitry Andric 
605f757f3fSDimitry Andric     if (StParents.size() > 1)
615f757f3fSDimitry Andric       return "unavailable due to multiple parents";
625f757f3fSDimitry Andric     if (StParents.size() == 0)
635f757f3fSDimitry Andric       break;
645f757f3fSDimitry Andric     St = StParents.begin()->get<Stmt>();
655f757f3fSDimitry Andric     if (St)
665f757f3fSDimitry Andric       SS << " ==> ";
675f757f3fSDimitry Andric   } while (St);
685f757f3fSDimitry Andric   return SS.str();
695f757f3fSDimitry Andric }
705f757f3fSDimitry Andric } // namespace
715f757f3fSDimitry Andric #endif /* NDEBUG */
725f757f3fSDimitry Andric 
73bdd1243dSDimitry Andric namespace clang::ast_matchers {
74bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
75bdd1243dSDimitry Andric // except for those belonging to a different callable of "n".
76bdd1243dSDimitry Andric class MatchDescendantVisitor
77bdd1243dSDimitry Andric     : public RecursiveASTVisitor<MatchDescendantVisitor> {
78bdd1243dSDimitry Andric public:
79bdd1243dSDimitry Andric   typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase;
80bdd1243dSDimitry Andric 
81bdd1243dSDimitry Andric   // Creates an AST visitor that matches `Matcher` on all
82bdd1243dSDimitry Andric   // descendants of a given node "n" except for the ones
83bdd1243dSDimitry Andric   // belonging to a different callable of "n".
MatchDescendantVisitor(const internal::DynTypedMatcher * Matcher,internal::ASTMatchFinder * Finder,internal::BoundNodesTreeBuilder * Builder,internal::ASTMatchFinder::BindKind Bind,const bool ignoreUnevaluatedContext)84bdd1243dSDimitry Andric   MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
85bdd1243dSDimitry Andric                          internal::ASTMatchFinder *Finder,
86bdd1243dSDimitry Andric                          internal::BoundNodesTreeBuilder *Builder,
8706c3fb27SDimitry Andric                          internal::ASTMatchFinder::BindKind Bind,
8806c3fb27SDimitry Andric                          const bool ignoreUnevaluatedContext)
89bdd1243dSDimitry Andric       : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
9006c3fb27SDimitry Andric         Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {}
91bdd1243dSDimitry Andric 
92bdd1243dSDimitry Andric   // Returns true if a match is found in a subtree of `DynNode`, which belongs
93bdd1243dSDimitry Andric   // to the same callable of `DynNode`.
findMatch(const DynTypedNode & DynNode)94bdd1243dSDimitry Andric   bool findMatch(const DynTypedNode &DynNode) {
95bdd1243dSDimitry Andric     Matches = false;
96bdd1243dSDimitry Andric     if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
97bdd1243dSDimitry Andric       TraverseStmt(const_cast<Stmt *>(StmtNode));
98bdd1243dSDimitry Andric       *Builder = ResultBindings;
99bdd1243dSDimitry Andric       return Matches;
100bdd1243dSDimitry Andric     }
101bdd1243dSDimitry Andric     return false;
102bdd1243dSDimitry Andric   }
103bdd1243dSDimitry Andric 
104bdd1243dSDimitry Andric   // The following are overriding methods from the base visitor class.
105bdd1243dSDimitry Andric   // They are public only to allow CRTP to work. They are *not *part
106bdd1243dSDimitry Andric   // of the public API of this class.
107bdd1243dSDimitry Andric 
108bdd1243dSDimitry Andric   // For the matchers so far used in safe buffers, we only need to match
109bdd1243dSDimitry Andric   // `Stmt`s.  To override more as needed.
110bdd1243dSDimitry Andric 
TraverseDecl(Decl * Node)111bdd1243dSDimitry Andric   bool TraverseDecl(Decl *Node) {
112bdd1243dSDimitry Andric     if (!Node)
113bdd1243dSDimitry Andric       return true;
114bdd1243dSDimitry Andric     if (!match(*Node))
115bdd1243dSDimitry Andric       return false;
116bdd1243dSDimitry Andric     // To skip callables:
117bdd1243dSDimitry Andric     if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
118bdd1243dSDimitry Andric       return true;
119bdd1243dSDimitry Andric     // Traverse descendants
120bdd1243dSDimitry Andric     return VisitorBase::TraverseDecl(Node);
121bdd1243dSDimitry Andric   }
122bdd1243dSDimitry Andric 
TraverseGenericSelectionExpr(GenericSelectionExpr * Node)12306c3fb27SDimitry Andric   bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) {
12406c3fb27SDimitry Andric     // These are unevaluated, except the result expression.
12506c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
12606c3fb27SDimitry Andric       return TraverseStmt(Node->getResultExpr());
12706c3fb27SDimitry Andric     return VisitorBase::TraverseGenericSelectionExpr(Node);
12806c3fb27SDimitry Andric   }
12906c3fb27SDimitry Andric 
TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr * Node)13006c3fb27SDimitry Andric   bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) {
13106c3fb27SDimitry Andric     // Unevaluated context.
13206c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
13306c3fb27SDimitry Andric       return true;
13406c3fb27SDimitry Andric     return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node);
13506c3fb27SDimitry Andric   }
13606c3fb27SDimitry Andric 
TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node)13706c3fb27SDimitry Andric   bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) {
13806c3fb27SDimitry Andric     // Unevaluated context.
13906c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
14006c3fb27SDimitry Andric       return true;
14106c3fb27SDimitry Andric     return VisitorBase::TraverseTypeOfExprTypeLoc(Node);
14206c3fb27SDimitry Andric   }
14306c3fb27SDimitry Andric 
TraverseDecltypeTypeLoc(DecltypeTypeLoc Node)14406c3fb27SDimitry Andric   bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) {
14506c3fb27SDimitry Andric     // Unevaluated context.
14606c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
14706c3fb27SDimitry Andric       return true;
14806c3fb27SDimitry Andric     return VisitorBase::TraverseDecltypeTypeLoc(Node);
14906c3fb27SDimitry Andric   }
15006c3fb27SDimitry Andric 
TraverseCXXNoexceptExpr(CXXNoexceptExpr * Node)15106c3fb27SDimitry Andric   bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) {
15206c3fb27SDimitry Andric     // Unevaluated context.
15306c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
15406c3fb27SDimitry Andric       return true;
15506c3fb27SDimitry Andric     return VisitorBase::TraverseCXXNoexceptExpr(Node);
15606c3fb27SDimitry Andric   }
15706c3fb27SDimitry Andric 
TraverseCXXTypeidExpr(CXXTypeidExpr * Node)15806c3fb27SDimitry Andric   bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) {
15906c3fb27SDimitry Andric     // Unevaluated context.
16006c3fb27SDimitry Andric     if(ignoreUnevaluatedContext)
16106c3fb27SDimitry Andric       return true;
16206c3fb27SDimitry Andric     return VisitorBase::TraverseCXXTypeidExpr(Node);
16306c3fb27SDimitry Andric   }
16406c3fb27SDimitry Andric 
TraverseStmt(Stmt * Node,DataRecursionQueue * Queue=nullptr)165bdd1243dSDimitry Andric   bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
166bdd1243dSDimitry Andric     if (!Node)
167bdd1243dSDimitry Andric       return true;
168bdd1243dSDimitry Andric     if (!match(*Node))
169bdd1243dSDimitry Andric       return false;
170bdd1243dSDimitry Andric     return VisitorBase::TraverseStmt(Node);
171bdd1243dSDimitry Andric   }
172bdd1243dSDimitry Andric 
shouldVisitTemplateInstantiations() const173bdd1243dSDimitry Andric   bool shouldVisitTemplateInstantiations() const { return true; }
shouldVisitImplicitCode() const174bdd1243dSDimitry Andric   bool shouldVisitImplicitCode() const {
175bdd1243dSDimitry Andric     // TODO: let's ignore implicit code for now
176bdd1243dSDimitry Andric     return false;
177bdd1243dSDimitry Andric   }
178bdd1243dSDimitry Andric 
179bdd1243dSDimitry Andric private:
180bdd1243dSDimitry Andric   // Sets 'Matched' to true if 'Matcher' matches 'Node'
181bdd1243dSDimitry Andric   //
182bdd1243dSDimitry Andric   // Returns 'true' if traversal should continue after this function
183bdd1243dSDimitry Andric   // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
match(const T & Node)184bdd1243dSDimitry Andric   template <typename T> bool match(const T &Node) {
185bdd1243dSDimitry Andric     internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
186bdd1243dSDimitry Andric 
187bdd1243dSDimitry Andric     if (Matcher->matches(DynTypedNode::create(Node), Finder,
188bdd1243dSDimitry Andric                          &RecursiveBuilder)) {
189bdd1243dSDimitry Andric       ResultBindings.addMatch(RecursiveBuilder);
190bdd1243dSDimitry Andric       Matches = true;
191bdd1243dSDimitry Andric       if (Bind != internal::ASTMatchFinder::BK_All)
192bdd1243dSDimitry Andric         return false; // Abort as soon as a match is found.
193bdd1243dSDimitry Andric     }
194bdd1243dSDimitry Andric     return true;
195bdd1243dSDimitry Andric   }
196bdd1243dSDimitry Andric 
197bdd1243dSDimitry Andric   const internal::DynTypedMatcher *const Matcher;
198bdd1243dSDimitry Andric   internal::ASTMatchFinder *const Finder;
199bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder *const Builder;
200bdd1243dSDimitry Andric   internal::BoundNodesTreeBuilder ResultBindings;
201bdd1243dSDimitry Andric   const internal::ASTMatchFinder::BindKind Bind;
202bdd1243dSDimitry Andric   bool Matches;
20306c3fb27SDimitry Andric   bool ignoreUnevaluatedContext;
204bdd1243dSDimitry Andric };
205bdd1243dSDimitry Andric 
20606c3fb27SDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that.
hasPointerType()20706c3fb27SDimitry Andric static auto hasPointerType() {
20806c3fb27SDimitry Andric     return hasType(hasCanonicalType(pointerType()));
20906c3fb27SDimitry Andric }
21006c3fb27SDimitry Andric 
hasArrayType()21106c3fb27SDimitry Andric static auto hasArrayType() {
21206c3fb27SDimitry Andric     return hasType(hasCanonicalType(arrayType()));
21306c3fb27SDimitry Andric }
21406c3fb27SDimitry Andric 
AST_MATCHER_P(Stmt,forEachDescendantEvaluatedStmt,internal::Matcher<Stmt>,innerMatcher)21506c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>, innerMatcher) {
216bdd1243dSDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
217bdd1243dSDimitry Andric 
21806c3fb27SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, true);
219bdd1243dSDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
220bdd1243dSDimitry Andric }
22106c3fb27SDimitry Andric 
AST_MATCHER_P(Stmt,forEachDescendantStmt,internal::Matcher<Stmt>,innerMatcher)22206c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>, innerMatcher) {
22306c3fb27SDimitry Andric   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
22406c3fb27SDimitry Andric 
22506c3fb27SDimitry Andric   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, false);
22606c3fb27SDimitry Andric   return Visitor.findMatch(DynTypedNode::create(Node));
22706c3fb27SDimitry Andric }
22806c3fb27SDimitry Andric 
22906c3fb27SDimitry Andric // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
AST_MATCHER_P(Stmt,notInSafeBufferOptOut,const UnsafeBufferUsageHandler *,Handler)23006c3fb27SDimitry Andric AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *,
23106c3fb27SDimitry Andric               Handler) {
23206c3fb27SDimitry Andric   return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
23306c3fb27SDimitry Andric }
23406c3fb27SDimitry Andric 
AST_MATCHER_P(CastExpr,castSubExpr,internal::Matcher<Expr>,innerMatcher)23506c3fb27SDimitry Andric AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
23606c3fb27SDimitry Andric   return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
23706c3fb27SDimitry Andric }
23806c3fb27SDimitry Andric 
23906c3fb27SDimitry Andric // Matches a `UnaryOperator` whose operator is pre-increment:
AST_MATCHER(UnaryOperator,isPreInc)24006c3fb27SDimitry Andric AST_MATCHER(UnaryOperator, isPreInc) {
24106c3fb27SDimitry Andric   return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
24206c3fb27SDimitry Andric }
24306c3fb27SDimitry Andric 
24406c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
24506c3fb27SDimitry Andric // matches 'e' and 'e' is in an Unspecified Lvalue Context.
isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher)24606c3fb27SDimitry Andric static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) {
24706c3fb27SDimitry Andric   // clang-format off
24806c3fb27SDimitry Andric   return
24906c3fb27SDimitry Andric     expr(anyOf(
25006c3fb27SDimitry Andric       implicitCastExpr(
25106c3fb27SDimitry Andric         hasCastKind(CastKind::CK_LValueToRValue),
25206c3fb27SDimitry Andric         castSubExpr(innerMatcher)),
25306c3fb27SDimitry Andric       binaryOperator(
25406c3fb27SDimitry Andric         hasAnyOperatorName("="),
25506c3fb27SDimitry Andric         hasLHS(innerMatcher)
25606c3fb27SDimitry Andric       )
25706c3fb27SDimitry Andric     ));
25806c3fb27SDimitry Andric // clang-format on
25906c3fb27SDimitry Andric }
26006c3fb27SDimitry Andric 
26106c3fb27SDimitry Andric 
26206c3fb27SDimitry Andric // Returns a matcher that matches any expression `e` such that `InnerMatcher`
26306c3fb27SDimitry Andric // matches `e` and `e` is in an Unspecified Pointer Context (UPC).
26406c3fb27SDimitry Andric static internal::Matcher<Stmt>
isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher)26506c3fb27SDimitry Andric isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
26606c3fb27SDimitry Andric   // A UPC can be
26706c3fb27SDimitry Andric   // 1. an argument of a function call (except the callee has [[unsafe_...]]
26806c3fb27SDimitry Andric   //    attribute), or
26906c3fb27SDimitry Andric   // 2. the operand of a pointer-to-(integer or bool) cast operation; or
27006c3fb27SDimitry Andric   // 3. the operand of a comparator operation; or
27106c3fb27SDimitry Andric   // 4. the operand of a pointer subtraction operation
27206c3fb27SDimitry Andric   //    (i.e., computing the distance between two pointers); or ...
27306c3fb27SDimitry Andric 
27406c3fb27SDimitry Andric   auto CallArgMatcher =
27506c3fb27SDimitry Andric       callExpr(forEachArgumentWithParam(InnerMatcher,
27606c3fb27SDimitry Andric                   hasPointerType() /* array also decays to pointer type*/),
27706c3fb27SDimitry Andric           unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
27806c3fb27SDimitry Andric 
27906c3fb27SDimitry Andric   auto CastOperandMatcher =
28006c3fb27SDimitry Andric       castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
28106c3fb27SDimitry Andric 		     hasCastKind(CastKind::CK_PointerToBoolean)),
28206c3fb27SDimitry Andric 	       castSubExpr(allOf(hasPointerType(), InnerMatcher)));
28306c3fb27SDimitry Andric 
28406c3fb27SDimitry Andric   auto CompOperandMatcher =
28506c3fb27SDimitry Andric       binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
28606c3fb27SDimitry Andric                      eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
28706c3fb27SDimitry Andric                             hasRHS(allOf(hasPointerType(), InnerMatcher))));
28806c3fb27SDimitry Andric 
28906c3fb27SDimitry Andric   // A matcher that matches pointer subtractions:
29006c3fb27SDimitry Andric   auto PtrSubtractionMatcher =
29106c3fb27SDimitry Andric       binaryOperator(hasOperatorName("-"),
29206c3fb27SDimitry Andric 		     // Note that here we need both LHS and RHS to be
29306c3fb27SDimitry Andric 		     // pointer. Then the inner matcher can match any of
29406c3fb27SDimitry Andric 		     // them:
29506c3fb27SDimitry Andric 		     allOf(hasLHS(hasPointerType()),
29606c3fb27SDimitry Andric 			   hasRHS(hasPointerType())),
29706c3fb27SDimitry Andric 		     eachOf(hasLHS(InnerMatcher),
29806c3fb27SDimitry Andric 			    hasRHS(InnerMatcher)));
29906c3fb27SDimitry Andric 
30006c3fb27SDimitry Andric   return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
30106c3fb27SDimitry Andric 		    PtrSubtractionMatcher));
30206c3fb27SDimitry Andric   // FIXME: any more cases? (UPC excludes the RHS of an assignment.  For now we
30306c3fb27SDimitry Andric   // don't have to check that.)
30406c3fb27SDimitry Andric }
30506c3fb27SDimitry Andric 
30606c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher`
30706c3fb27SDimitry Andric // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression
30806c3fb27SDimitry Andric // 'e' isn't evaluated to an RValue). For example, consider the following code:
30906c3fb27SDimitry Andric //    int *p = new int[4];
31006c3fb27SDimitry Andric //    int *q = new int[4];
31106c3fb27SDimitry Andric //    if ((p = q)) {}
31206c3fb27SDimitry Andric //    p = q;
31306c3fb27SDimitry Andric // The expression `p = q` in the conditional of the `if` statement
31406c3fb27SDimitry Andric // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
31506c3fb27SDimitry Andric // in the assignment statement is in an untyped context.
31606c3fb27SDimitry Andric static internal::Matcher<Stmt>
isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher)31706c3fb27SDimitry Andric isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
31806c3fb27SDimitry Andric   // An unspecified context can be
31906c3fb27SDimitry Andric   // 1. A compound statement,
32006c3fb27SDimitry Andric   // 2. The body of an if statement
32106c3fb27SDimitry Andric   // 3. Body of a loop
32206c3fb27SDimitry Andric   auto CompStmt = compoundStmt(forEach(InnerMatcher));
32306c3fb27SDimitry Andric   auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
32406c3fb27SDimitry Andric   auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
32506c3fb27SDimitry Andric   // FIXME: Handle loop bodies.
32606c3fb27SDimitry Andric   return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
32706c3fb27SDimitry Andric }
328bdd1243dSDimitry Andric } // namespace clang::ast_matchers
329bdd1243dSDimitry Andric 
330bdd1243dSDimitry Andric namespace {
331bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to
332bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs).
333bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
334bdd1243dSDimitry Andric 
335bdd1243dSDimitry Andric // Convenience typedef.
336bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>;
337bdd1243dSDimitry Andric 
338bdd1243dSDimitry Andric // Defined below.
339bdd1243dSDimitry Andric class Strategy;
340bdd1243dSDimitry Andric } // namespace
341bdd1243dSDimitry Andric 
342bdd1243dSDimitry Andric namespace {
343bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to
344bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific
345bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object.
346bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand
347bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it.
348bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not
349bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify
350bdd1243dSDimitry Andric /// it warrants a fixit).
351bdd1243dSDimitry Andric class Gadget {
352bdd1243dSDimitry Andric public:
353bdd1243dSDimitry Andric   enum class Kind {
354bdd1243dSDimitry Andric #define GADGET(x) x,
355bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
356bdd1243dSDimitry Andric   };
357bdd1243dSDimitry Andric 
358bdd1243dSDimitry Andric   /// Common type of ASTMatchers used for discovering gadgets.
359bdd1243dSDimitry Andric   /// Useful for implementing the static matcher() methods
360bdd1243dSDimitry Andric   /// that are expected from all non-abstract subclasses.
361bdd1243dSDimitry Andric   using Matcher = decltype(stmt());
362bdd1243dSDimitry Andric 
Gadget(Kind K)363bdd1243dSDimitry Andric   Gadget(Kind K) : K(K) {}
364bdd1243dSDimitry Andric 
getKind() const365bdd1243dSDimitry Andric   Kind getKind() const { return K; }
366bdd1243dSDimitry Andric 
3675f757f3fSDimitry Andric #ifndef NDEBUG
getDebugName() const3685f757f3fSDimitry Andric   StringRef getDebugName() const {
3695f757f3fSDimitry Andric     switch (K) {
3705f757f3fSDimitry Andric #define GADGET(x) case Kind::x: return #x;
3715f757f3fSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3725f757f3fSDimitry Andric     }
3735f757f3fSDimitry Andric     llvm_unreachable("Unhandled Gadget::Kind enum");
3745f757f3fSDimitry Andric   }
3755f757f3fSDimitry Andric #endif
3765f757f3fSDimitry Andric 
377bdd1243dSDimitry Andric   virtual bool isWarningGadget() const = 0;
378bdd1243dSDimitry Andric   virtual const Stmt *getBaseStmt() const = 0;
379bdd1243dSDimitry Andric 
380bdd1243dSDimitry Andric   /// Returns the list of pointer-type variables on which this gadget performs
381bdd1243dSDimitry Andric   /// its operation. Typically, there's only one variable. This isn't a list
382bdd1243dSDimitry Andric   /// of all DeclRefExprs in the gadget's AST!
383bdd1243dSDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const = 0;
384bdd1243dSDimitry Andric 
385bdd1243dSDimitry Andric   virtual ~Gadget() = default;
386bdd1243dSDimitry Andric 
387bdd1243dSDimitry Andric private:
388bdd1243dSDimitry Andric   Kind K;
389bdd1243dSDimitry Andric };
390bdd1243dSDimitry Andric 
391bdd1243dSDimitry Andric 
392bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants
393bdd1243dSDimitry Andric /// an immediate warning.
394bdd1243dSDimitry Andric class WarningGadget : public Gadget {
395bdd1243dSDimitry Andric public:
WarningGadget(Kind K)396bdd1243dSDimitry Andric   WarningGadget(Kind K) : Gadget(K) {}
397bdd1243dSDimitry Andric 
classof(const Gadget * G)398bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return G->isWarningGadget(); }
isWarningGadget() const399bdd1243dSDimitry Andric   bool isWarningGadget() const final { return true; }
400bdd1243dSDimitry Andric };
401bdd1243dSDimitry Andric 
402bdd1243dSDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but need to be
403bdd1243dSDimitry Andric /// properly recognized in order to emit fixes. For example, if a raw pointer-type
404bdd1243dSDimitry Andric /// variable is replaced by a safe C++ container, every use of such variable must be
405bdd1243dSDimitry Andric /// carefully considered and possibly updated.
406bdd1243dSDimitry Andric class FixableGadget : public Gadget {
407bdd1243dSDimitry Andric public:
FixableGadget(Kind K)408bdd1243dSDimitry Andric   FixableGadget(Kind K) : Gadget(K) {}
409bdd1243dSDimitry Andric 
classof(const Gadget * G)410bdd1243dSDimitry Andric   static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
isWarningGadget() const411bdd1243dSDimitry Andric   bool isWarningGadget() const final { return false; }
412bdd1243dSDimitry Andric 
413bdd1243dSDimitry Andric   /// Returns a fixit that would fix the current gadget according to
41406c3fb27SDimitry Andric   /// the current strategy. Returns std::nullopt if the fix cannot be produced;
415bdd1243dSDimitry Andric   /// returns an empty list if no fixes are necessary.
getFixits(const Strategy &) const416bdd1243dSDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &) const {
417bdd1243dSDimitry Andric     return std::nullopt;
418bdd1243dSDimitry Andric   }
41906c3fb27SDimitry Andric 
42006c3fb27SDimitry Andric   /// Returns a list of two elements where the first element is the LHS of a pointer assignment
42106c3fb27SDimitry Andric   /// statement and the second element is the RHS. This two-element list represents the fact that
42206c3fb27SDimitry Andric   /// the LHS buffer gets its bounds information from the RHS buffer. This information will be used
42306c3fb27SDimitry Andric   /// later to group all those variables whose types must be modified together to prevent type
42406c3fb27SDimitry Andric   /// mismatches.
42506c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
getStrategyImplications() const42606c3fb27SDimitry Andric   getStrategyImplications() const {
42706c3fb27SDimitry Andric     return std::nullopt;
42806c3fb27SDimitry Andric   }
429bdd1243dSDimitry Andric };
430bdd1243dSDimitry Andric 
toSupportedVariable()4315f757f3fSDimitry Andric static auto toSupportedVariable() {
4325f757f3fSDimitry Andric   return to(varDecl());
4335f757f3fSDimitry Andric }
4345f757f3fSDimitry Andric 
435bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
436bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
437bdd1243dSDimitry Andric 
438bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer
439bdd1243dSDimitry Andric /// out of bounds.
440bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget {
441bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
442bdd1243dSDimitry Andric   const UnaryOperator *Op;
443bdd1243dSDimitry Andric 
444bdd1243dSDimitry Andric public:
IncrementGadget(const MatchFinder::MatchResult & Result)445bdd1243dSDimitry Andric   IncrementGadget(const MatchFinder::MatchResult &Result)
446bdd1243dSDimitry Andric       : WarningGadget(Kind::Increment),
447bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
448bdd1243dSDimitry Andric 
classof(const Gadget * G)449bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
450bdd1243dSDimitry Andric     return G->getKind() == Kind::Increment;
451bdd1243dSDimitry Andric   }
452bdd1243dSDimitry Andric 
matcher()453bdd1243dSDimitry Andric   static Matcher matcher() {
454bdd1243dSDimitry Andric     return stmt(unaryOperator(
455bdd1243dSDimitry Andric       hasOperatorName("++"),
456bdd1243dSDimitry Andric       hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
457bdd1243dSDimitry Andric     ).bind(OpTag));
458bdd1243dSDimitry Andric   }
459bdd1243dSDimitry Andric 
getBaseStmt() const460bdd1243dSDimitry Andric   const UnaryOperator *getBaseStmt() const override { return Op; }
461bdd1243dSDimitry Andric 
getClaimedVarUseSites() const462bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
463bdd1243dSDimitry Andric     SmallVector<const DeclRefExpr *, 2> Uses;
464bdd1243dSDimitry Andric     if (const auto *DRE =
465bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
466bdd1243dSDimitry Andric       Uses.push_back(DRE);
467bdd1243dSDimitry Andric     }
468bdd1243dSDimitry Andric 
469bdd1243dSDimitry Andric     return std::move(Uses);
470bdd1243dSDimitry Andric   }
471bdd1243dSDimitry Andric };
472bdd1243dSDimitry Andric 
473bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer
474bdd1243dSDimitry Andric /// out of bounds.
475bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget {
476bdd1243dSDimitry Andric   static constexpr const char *const OpTag = "op";
477bdd1243dSDimitry Andric   const UnaryOperator *Op;
478bdd1243dSDimitry Andric 
479bdd1243dSDimitry Andric public:
DecrementGadget(const MatchFinder::MatchResult & Result)480bdd1243dSDimitry Andric   DecrementGadget(const MatchFinder::MatchResult &Result)
481bdd1243dSDimitry Andric       : WarningGadget(Kind::Decrement),
482bdd1243dSDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
483bdd1243dSDimitry Andric 
classof(const Gadget * G)484bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
485bdd1243dSDimitry Andric     return G->getKind() == Kind::Decrement;
486bdd1243dSDimitry Andric   }
487bdd1243dSDimitry Andric 
matcher()488bdd1243dSDimitry Andric   static Matcher matcher() {
489bdd1243dSDimitry Andric     return stmt(unaryOperator(
490bdd1243dSDimitry Andric       hasOperatorName("--"),
491bdd1243dSDimitry Andric       hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
492bdd1243dSDimitry Andric     ).bind(OpTag));
493bdd1243dSDimitry Andric   }
494bdd1243dSDimitry Andric 
getBaseStmt() const495bdd1243dSDimitry Andric   const UnaryOperator *getBaseStmt() const override { return Op; }
496bdd1243dSDimitry Andric 
getClaimedVarUseSites() const497bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
498bdd1243dSDimitry Andric     if (const auto *DRE =
499bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
500bdd1243dSDimitry Andric       return {DRE};
501bdd1243dSDimitry Andric     }
502bdd1243dSDimitry Andric 
503bdd1243dSDimitry Andric     return {};
504bdd1243dSDimitry Andric   }
505bdd1243dSDimitry Andric };
506bdd1243dSDimitry Andric 
507bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
508bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array.
509bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget {
51006c3fb27SDimitry Andric   static constexpr const char *const ArraySubscrTag = "ArraySubscript";
511bdd1243dSDimitry Andric   const ArraySubscriptExpr *ASE;
512bdd1243dSDimitry Andric 
513bdd1243dSDimitry Andric public:
ArraySubscriptGadget(const MatchFinder::MatchResult & Result)514bdd1243dSDimitry Andric   ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
515bdd1243dSDimitry Andric       : WarningGadget(Kind::ArraySubscript),
516bdd1243dSDimitry Andric         ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
517bdd1243dSDimitry Andric 
classof(const Gadget * G)518bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
519bdd1243dSDimitry Andric     return G->getKind() == Kind::ArraySubscript;
520bdd1243dSDimitry Andric   }
521bdd1243dSDimitry Andric 
matcher()522bdd1243dSDimitry Andric   static Matcher matcher() {
523bdd1243dSDimitry Andric     // FIXME: What if the index is integer literal 0? Should this be
524bdd1243dSDimitry Andric     // a safe gadget in this case?
525bdd1243dSDimitry Andric       // clang-format off
526bdd1243dSDimitry Andric       return stmt(arraySubscriptExpr(
527bdd1243dSDimitry Andric             hasBase(ignoringParenImpCasts(
528bdd1243dSDimitry Andric               anyOf(hasPointerType(), hasArrayType()))),
52906c3fb27SDimitry Andric             unless(hasIndex(
53006c3fb27SDimitry Andric                 anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
53106c3fb27SDimitry Andric              )))
532bdd1243dSDimitry Andric             .bind(ArraySubscrTag));
533bdd1243dSDimitry Andric     // clang-format on
534bdd1243dSDimitry Andric   }
535bdd1243dSDimitry Andric 
getBaseStmt() const536bdd1243dSDimitry Andric   const ArraySubscriptExpr *getBaseStmt() const override { return ASE; }
537bdd1243dSDimitry Andric 
getClaimedVarUseSites() const538bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
539bdd1243dSDimitry Andric     if (const auto *DRE =
540bdd1243dSDimitry Andric             dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
541bdd1243dSDimitry Andric       return {DRE};
542bdd1243dSDimitry Andric     }
543bdd1243dSDimitry Andric 
544bdd1243dSDimitry Andric     return {};
545bdd1243dSDimitry Andric   }
546bdd1243dSDimitry Andric };
547bdd1243dSDimitry Andric 
548bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms:
549bdd1243dSDimitry Andric ///  \code
550bdd1243dSDimitry Andric ///  ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
551bdd1243dSDimitry Andric ///  \endcode
552bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget {
553bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticTag = "ptrAdd";
554bdd1243dSDimitry Andric   static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
555bdd1243dSDimitry Andric   const BinaryOperator *PA; // pointer arithmetic expression
556bdd1243dSDimitry Andric   const Expr *Ptr;          // the pointer expression in `PA`
557bdd1243dSDimitry Andric 
558bdd1243dSDimitry Andric public:
PointerArithmeticGadget(const MatchFinder::MatchResult & Result)559bdd1243dSDimitry Andric   PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
560bdd1243dSDimitry Andric       : WarningGadget(Kind::PointerArithmetic),
561bdd1243dSDimitry Andric         PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
562bdd1243dSDimitry Andric         Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
563bdd1243dSDimitry Andric 
classof(const Gadget * G)564bdd1243dSDimitry Andric   static bool classof(const Gadget *G) {
565bdd1243dSDimitry Andric     return G->getKind() == Kind::PointerArithmetic;
566bdd1243dSDimitry Andric   }
567bdd1243dSDimitry Andric 
matcher()568bdd1243dSDimitry Andric   static Matcher matcher() {
56906c3fb27SDimitry Andric     auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
57006c3fb27SDimitry Andric     auto PtrAtRight =
57106c3fb27SDimitry Andric         allOf(hasOperatorName("+"),
572bdd1243dSDimitry Andric               hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
573bdd1243dSDimitry Andric               hasLHS(HasIntegerType));
57406c3fb27SDimitry Andric     auto PtrAtLeft =
57506c3fb27SDimitry Andric         allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
576bdd1243dSDimitry Andric                     hasOperatorName("+="), hasOperatorName("-=")),
577bdd1243dSDimitry Andric               hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
578bdd1243dSDimitry Andric               hasRHS(HasIntegerType));
579bdd1243dSDimitry Andric 
58006c3fb27SDimitry Andric     return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight))
58106c3fb27SDimitry Andric                     .bind(PointerArithmeticTag));
582bdd1243dSDimitry Andric   }
583bdd1243dSDimitry Andric 
getBaseStmt() const584bdd1243dSDimitry Andric   const Stmt *getBaseStmt() const override { return PA; }
585bdd1243dSDimitry Andric 
getClaimedVarUseSites() const586bdd1243dSDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
58706c3fb27SDimitry Andric     if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
588bdd1243dSDimitry Andric       return {DRE};
589bdd1243dSDimitry Andric     }
590bdd1243dSDimitry Andric 
591bdd1243dSDimitry Andric     return {};
592bdd1243dSDimitry Andric   }
593bdd1243dSDimitry Andric   // FIXME: pointer adding zero should be fine
594bdd1243dSDimitry Andric   // FIXME: this gadge will need a fix-it
595bdd1243dSDimitry Andric };
59606c3fb27SDimitry Andric 
59706c3fb27SDimitry Andric /// A pointer initialization expression of the form:
59806c3fb27SDimitry Andric ///  \code
59906c3fb27SDimitry Andric ///  int *p = q;
60006c3fb27SDimitry Andric ///  \endcode
60106c3fb27SDimitry Andric class PointerInitGadget : public FixableGadget {
60206c3fb27SDimitry Andric private:
60306c3fb27SDimitry Andric   static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
60406c3fb27SDimitry Andric   static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
60506c3fb27SDimitry Andric   const VarDecl * PtrInitLHS;         // the LHS pointer expression in `PI`
60606c3fb27SDimitry Andric   const DeclRefExpr * PtrInitRHS;         // the RHS pointer expression in `PI`
60706c3fb27SDimitry Andric 
60806c3fb27SDimitry Andric public:
PointerInitGadget(const MatchFinder::MatchResult & Result)60906c3fb27SDimitry Andric   PointerInitGadget(const MatchFinder::MatchResult &Result)
61006c3fb27SDimitry Andric       : FixableGadget(Kind::PointerInit),
61106c3fb27SDimitry Andric     PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
61206c3fb27SDimitry Andric     PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
61306c3fb27SDimitry Andric 
classof(const Gadget * G)61406c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
61506c3fb27SDimitry Andric     return G->getKind() == Kind::PointerInit;
61606c3fb27SDimitry Andric   }
61706c3fb27SDimitry Andric 
matcher()61806c3fb27SDimitry Andric   static Matcher matcher() {
61906c3fb27SDimitry Andric     auto PtrInitStmt = declStmt(hasSingleDecl(varDecl(
62006c3fb27SDimitry Andric                                  hasInitializer(ignoringImpCasts(declRefExpr(
6215f757f3fSDimitry Andric                                                   hasPointerType(),
6225f757f3fSDimitry Andric                                                     toSupportedVariable()).
62306c3fb27SDimitry Andric                                                   bind(PointerInitRHSTag)))).
62406c3fb27SDimitry Andric                                               bind(PointerInitLHSTag)));
62506c3fb27SDimitry Andric 
62606c3fb27SDimitry Andric     return stmt(PtrInitStmt);
62706c3fb27SDimitry Andric   }
62806c3fb27SDimitry Andric 
62906c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
63006c3fb27SDimitry Andric 
getBaseStmt() const6315f757f3fSDimitry Andric   virtual const Stmt *getBaseStmt() const override {
6325f757f3fSDimitry Andric     // FIXME: This needs to be the entire DeclStmt, assuming that this method
6335f757f3fSDimitry Andric     // makes sense at all on a FixableGadget.
6345f757f3fSDimitry Andric     return PtrInitRHS;
6355f757f3fSDimitry Andric   }
63606c3fb27SDimitry Andric 
getClaimedVarUseSites() const63706c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
63806c3fb27SDimitry Andric     return DeclUseList{PtrInitRHS};
63906c3fb27SDimitry Andric   }
64006c3fb27SDimitry Andric 
64106c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
getStrategyImplications() const64206c3fb27SDimitry Andric   getStrategyImplications() const override {
64306c3fb27SDimitry Andric       return std::make_pair(PtrInitLHS,
64406c3fb27SDimitry Andric                             cast<VarDecl>(PtrInitRHS->getDecl()));
64506c3fb27SDimitry Andric   }
64606c3fb27SDimitry Andric };
64706c3fb27SDimitry Andric 
64806c3fb27SDimitry Andric /// A pointer assignment expression of the form:
64906c3fb27SDimitry Andric ///  \code
65006c3fb27SDimitry Andric ///  p = q;
65106c3fb27SDimitry Andric ///  \endcode
65206c3fb27SDimitry Andric class PointerAssignmentGadget : public FixableGadget {
65306c3fb27SDimitry Andric private:
65406c3fb27SDimitry Andric   static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
65506c3fb27SDimitry Andric   static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
65606c3fb27SDimitry Andric   const DeclRefExpr * PtrLHS;         // the LHS pointer expression in `PA`
65706c3fb27SDimitry Andric   const DeclRefExpr * PtrRHS;         // the RHS pointer expression in `PA`
65806c3fb27SDimitry Andric 
65906c3fb27SDimitry Andric public:
PointerAssignmentGadget(const MatchFinder::MatchResult & Result)66006c3fb27SDimitry Andric   PointerAssignmentGadget(const MatchFinder::MatchResult &Result)
66106c3fb27SDimitry Andric       : FixableGadget(Kind::PointerAssignment),
66206c3fb27SDimitry Andric     PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
66306c3fb27SDimitry Andric     PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
66406c3fb27SDimitry Andric 
classof(const Gadget * G)66506c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
66606c3fb27SDimitry Andric     return G->getKind() == Kind::PointerAssignment;
66706c3fb27SDimitry Andric   }
66806c3fb27SDimitry Andric 
matcher()66906c3fb27SDimitry Andric   static Matcher matcher() {
67006c3fb27SDimitry Andric     auto PtrAssignExpr = binaryOperator(allOf(hasOperatorName("="),
67106c3fb27SDimitry Andric       hasRHS(ignoringParenImpCasts(declRefExpr(hasPointerType(),
6725f757f3fSDimitry Andric                                                toSupportedVariable()).
67306c3fb27SDimitry Andric                                    bind(PointerAssignRHSTag))),
67406c3fb27SDimitry Andric                                    hasLHS(declRefExpr(hasPointerType(),
6755f757f3fSDimitry Andric                                                       toSupportedVariable()).
67606c3fb27SDimitry Andric                                           bind(PointerAssignLHSTag))));
67706c3fb27SDimitry Andric 
67806c3fb27SDimitry Andric     return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
67906c3fb27SDimitry Andric   }
68006c3fb27SDimitry Andric 
68106c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
68206c3fb27SDimitry Andric 
getBaseStmt() const6835f757f3fSDimitry Andric   virtual const Stmt *getBaseStmt() const override {
6845f757f3fSDimitry Andric     // FIXME: This should be the binary operator, assuming that this method
6855f757f3fSDimitry Andric     // makes sense at all on a FixableGadget.
6865f757f3fSDimitry Andric     return PtrLHS;
6875f757f3fSDimitry Andric   }
68806c3fb27SDimitry Andric 
getClaimedVarUseSites() const68906c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
69006c3fb27SDimitry Andric     return DeclUseList{PtrLHS, PtrRHS};
69106c3fb27SDimitry Andric   }
69206c3fb27SDimitry Andric 
69306c3fb27SDimitry Andric   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
getStrategyImplications() const69406c3fb27SDimitry Andric   getStrategyImplications() const override {
69506c3fb27SDimitry Andric     return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
69606c3fb27SDimitry Andric                           cast<VarDecl>(PtrRHS->getDecl()));
69706c3fb27SDimitry Andric   }
69806c3fb27SDimitry Andric };
69906c3fb27SDimitry Andric 
70006c3fb27SDimitry Andric /// A call of a function or method that performs unchecked buffer operations
70106c3fb27SDimitry Andric /// over one of its pointer parameters.
70206c3fb27SDimitry Andric class UnsafeBufferUsageAttrGadget : public WarningGadget {
70306c3fb27SDimitry Andric   constexpr static const char *const OpTag = "call_expr";
70406c3fb27SDimitry Andric   const CallExpr *Op;
70506c3fb27SDimitry Andric 
70606c3fb27SDimitry Andric public:
UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult & Result)70706c3fb27SDimitry Andric   UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
70806c3fb27SDimitry Andric       : WarningGadget(Kind::UnsafeBufferUsageAttr),
70906c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {}
71006c3fb27SDimitry Andric 
classof(const Gadget * G)71106c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
71206c3fb27SDimitry Andric     return G->getKind() == Kind::UnsafeBufferUsageAttr;
71306c3fb27SDimitry Andric   }
71406c3fb27SDimitry Andric 
matcher()71506c3fb27SDimitry Andric   static Matcher matcher() {
71606c3fb27SDimitry Andric     return stmt(callExpr(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))))
71706c3fb27SDimitry Andric                     .bind(OpTag));
71806c3fb27SDimitry Andric   }
getBaseStmt() const71906c3fb27SDimitry Andric   const Stmt *getBaseStmt() const override { return Op; }
72006c3fb27SDimitry Andric 
getClaimedVarUseSites() const72106c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override { return {}; }
72206c3fb27SDimitry Andric };
72306c3fb27SDimitry Andric 
7241db9f3b2SDimitry Andric // Warning gadget for unsafe invocation of span::data method.
7251db9f3b2SDimitry Andric // Triggers when the pointer returned by the invocation is immediately
7261db9f3b2SDimitry Andric // cast to a larger type.
7271db9f3b2SDimitry Andric 
7281db9f3b2SDimitry Andric class DataInvocationGadget : public WarningGadget {
7291db9f3b2SDimitry Andric   constexpr static const char *const OpTag = "data_invocation_expr";
7301db9f3b2SDimitry Andric   const ExplicitCastExpr *Op;
7311db9f3b2SDimitry Andric 
7321db9f3b2SDimitry Andric public:
DataInvocationGadget(const MatchFinder::MatchResult & Result)7331db9f3b2SDimitry Andric   DataInvocationGadget(const MatchFinder::MatchResult &Result)
7341db9f3b2SDimitry Andric       : WarningGadget(Kind::DataInvocation),
7351db9f3b2SDimitry Andric         Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {}
7361db9f3b2SDimitry Andric 
classof(const Gadget * G)7371db9f3b2SDimitry Andric   static bool classof(const Gadget *G) {
7381db9f3b2SDimitry Andric     return G->getKind() == Kind::DataInvocation;
7391db9f3b2SDimitry Andric   }
7401db9f3b2SDimitry Andric 
matcher()7411db9f3b2SDimitry Andric   static Matcher matcher() {
742*7a6dacacSDimitry Andric     Matcher callExpr = cxxMemberCallExpr(
743*7a6dacacSDimitry Andric         callee(cxxMethodDecl(hasName("data"), ofClass(hasName("std::span")))));
7441db9f3b2SDimitry Andric     return stmt(
745*7a6dacacSDimitry Andric         explicitCastExpr(anyOf(has(callExpr), has(parenExpr(has(callExpr)))))
7461db9f3b2SDimitry Andric             .bind(OpTag));
7471db9f3b2SDimitry Andric   }
getBaseStmt() const7481db9f3b2SDimitry Andric   const Stmt *getBaseStmt() const override { return Op; }
7491db9f3b2SDimitry Andric 
getClaimedVarUseSites() const7501db9f3b2SDimitry Andric   DeclUseList getClaimedVarUseSites() const override { return {}; }
7511db9f3b2SDimitry Andric };
7521db9f3b2SDimitry Andric 
75306c3fb27SDimitry Andric // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
75406c3fb27SDimitry Andric // Context (see `isInUnspecifiedLvalueContext`).
75506c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
75606c3fb27SDimitry Andric class ULCArraySubscriptGadget : public FixableGadget {
75706c3fb27SDimitry Andric private:
75806c3fb27SDimitry Andric   static constexpr const char *const ULCArraySubscriptTag =
75906c3fb27SDimitry Andric       "ArraySubscriptUnderULC";
76006c3fb27SDimitry Andric   const ArraySubscriptExpr *Node;
76106c3fb27SDimitry Andric 
76206c3fb27SDimitry Andric public:
ULCArraySubscriptGadget(const MatchFinder::MatchResult & Result)76306c3fb27SDimitry Andric   ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
76406c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
76506c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
76606c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
76706c3fb27SDimitry Andric   }
76806c3fb27SDimitry Andric 
classof(const Gadget * G)76906c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
77006c3fb27SDimitry Andric     return G->getKind() == Kind::ULCArraySubscript;
77106c3fb27SDimitry Andric   }
77206c3fb27SDimitry Andric 
matcher()77306c3fb27SDimitry Andric   static Matcher matcher() {
77406c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
77506c3fb27SDimitry Andric     auto BaseIsArrayOrPtrDRE =
7765f757f3fSDimitry Andric         hasBase(ignoringParenImpCasts(declRefExpr(ArrayOrPtr,
7775f757f3fSDimitry Andric                                                   toSupportedVariable())));
77806c3fb27SDimitry Andric     auto Target =
77906c3fb27SDimitry Andric         arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
78006c3fb27SDimitry Andric 
78106c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
78206c3fb27SDimitry Andric   }
78306c3fb27SDimitry Andric 
78406c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
78506c3fb27SDimitry Andric 
getBaseStmt() const78606c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
78706c3fb27SDimitry Andric 
getClaimedVarUseSites() const78806c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
78906c3fb27SDimitry Andric     if (const auto *DRE =
79006c3fb27SDimitry Andric             dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
79106c3fb27SDimitry Andric       return {DRE};
79206c3fb27SDimitry Andric     }
79306c3fb27SDimitry Andric     return {};
79406c3fb27SDimitry Andric   }
79506c3fb27SDimitry Andric };
79606c3fb27SDimitry Andric 
79706c3fb27SDimitry Andric // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
79806c3fb27SDimitry Andric // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
79906c3fb27SDimitry Andric // fixit of the form `UPC(DRE.data())`.
80006c3fb27SDimitry Andric class UPCStandalonePointerGadget : public FixableGadget {
80106c3fb27SDimitry Andric private:
80206c3fb27SDimitry Andric   static constexpr const char *const DeclRefExprTag = "StandalonePointer";
80306c3fb27SDimitry Andric   const DeclRefExpr *Node;
80406c3fb27SDimitry Andric 
80506c3fb27SDimitry Andric public:
UPCStandalonePointerGadget(const MatchFinder::MatchResult & Result)80606c3fb27SDimitry Andric   UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
80706c3fb27SDimitry Andric       : FixableGadget(Kind::UPCStandalonePointer),
80806c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
80906c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
81006c3fb27SDimitry Andric   }
81106c3fb27SDimitry Andric 
classof(const Gadget * G)81206c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
81306c3fb27SDimitry Andric     return G->getKind() == Kind::UPCStandalonePointer;
81406c3fb27SDimitry Andric   }
81506c3fb27SDimitry Andric 
matcher()81606c3fb27SDimitry Andric   static Matcher matcher() {
81706c3fb27SDimitry Andric     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
81806c3fb27SDimitry Andric     auto target = expr(
8195f757f3fSDimitry Andric         ignoringParenImpCasts(declRefExpr(allOf(ArrayOrPtr,
8205f757f3fSDimitry Andric                               toSupportedVariable())).bind(DeclRefExprTag)));
82106c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(target));
82206c3fb27SDimitry Andric   }
82306c3fb27SDimitry Andric 
82406c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
82506c3fb27SDimitry Andric 
getBaseStmt() const82606c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
82706c3fb27SDimitry Andric 
getClaimedVarUseSites() const82806c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
82906c3fb27SDimitry Andric     return {Node};
83006c3fb27SDimitry Andric   }
83106c3fb27SDimitry Andric };
83206c3fb27SDimitry Andric 
83306c3fb27SDimitry Andric class PointerDereferenceGadget : public FixableGadget {
83406c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
83506c3fb27SDimitry Andric   static constexpr const char *const OperatorTag = "op";
83606c3fb27SDimitry Andric 
83706c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
83806c3fb27SDimitry Andric   const UnaryOperator *Op = nullptr;
83906c3fb27SDimitry Andric 
84006c3fb27SDimitry Andric public:
PointerDereferenceGadget(const MatchFinder::MatchResult & Result)84106c3fb27SDimitry Andric   PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
84206c3fb27SDimitry Andric       : FixableGadget(Kind::PointerDereference),
84306c3fb27SDimitry Andric         BaseDeclRefExpr(
84406c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
84506c3fb27SDimitry Andric         Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
84606c3fb27SDimitry Andric 
classof(const Gadget * G)84706c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
84806c3fb27SDimitry Andric     return G->getKind() == Kind::PointerDereference;
84906c3fb27SDimitry Andric   }
85006c3fb27SDimitry Andric 
matcher()85106c3fb27SDimitry Andric   static Matcher matcher() {
85206c3fb27SDimitry Andric     auto Target =
85306c3fb27SDimitry Andric         unaryOperator(
85406c3fb27SDimitry Andric             hasOperatorName("*"),
85506c3fb27SDimitry Andric             has(expr(ignoringParenImpCasts(
8565f757f3fSDimitry Andric                 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
85706c3fb27SDimitry Andric             .bind(OperatorTag);
85806c3fb27SDimitry Andric 
85906c3fb27SDimitry Andric     return expr(isInUnspecifiedLvalueContext(Target));
86006c3fb27SDimitry Andric   }
86106c3fb27SDimitry Andric 
getClaimedVarUseSites() const86206c3fb27SDimitry Andric   DeclUseList getClaimedVarUseSites() const override {
86306c3fb27SDimitry Andric     return {BaseDeclRefExpr};
86406c3fb27SDimitry Andric   }
86506c3fb27SDimitry Andric 
getBaseStmt() const86606c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const final { return Op; }
86706c3fb27SDimitry Andric 
86806c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
86906c3fb27SDimitry Andric };
87006c3fb27SDimitry Andric 
87106c3fb27SDimitry Andric // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
87206c3fb27SDimitry Andric // Context (see `isInUnspecifiedPointerContext`).
87306c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator.
87406c3fb27SDimitry Andric class UPCAddressofArraySubscriptGadget : public FixableGadget {
87506c3fb27SDimitry Andric private:
87606c3fb27SDimitry Andric   static constexpr const char *const UPCAddressofArraySubscriptTag =
87706c3fb27SDimitry Andric       "AddressofArraySubscriptUnderUPC";
87806c3fb27SDimitry Andric   const UnaryOperator *Node; // the `&DRE[any]` node
87906c3fb27SDimitry Andric 
88006c3fb27SDimitry Andric public:
UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult & Result)88106c3fb27SDimitry Andric   UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
88206c3fb27SDimitry Andric       : FixableGadget(Kind::ULCArraySubscript),
88306c3fb27SDimitry Andric         Node(Result.Nodes.getNodeAs<UnaryOperator>(
88406c3fb27SDimitry Andric             UPCAddressofArraySubscriptTag)) {
88506c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
88606c3fb27SDimitry Andric   }
88706c3fb27SDimitry Andric 
classof(const Gadget * G)88806c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
88906c3fb27SDimitry Andric     return G->getKind() == Kind::UPCAddressofArraySubscript;
89006c3fb27SDimitry Andric   }
89106c3fb27SDimitry Andric 
matcher()89206c3fb27SDimitry Andric   static Matcher matcher() {
89306c3fb27SDimitry Andric     return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
89406c3fb27SDimitry Andric         unaryOperator(hasOperatorName("&"),
89506c3fb27SDimitry Andric                       hasUnaryOperand(arraySubscriptExpr(
8965f757f3fSDimitry Andric                           hasBase(ignoringParenImpCasts(declRefExpr(
8975f757f3fSDimitry Andric                                                   toSupportedVariable()))))))
89806c3fb27SDimitry Andric             .bind(UPCAddressofArraySubscriptTag)))));
89906c3fb27SDimitry Andric   }
90006c3fb27SDimitry Andric 
90106c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &) const override;
90206c3fb27SDimitry Andric 
getBaseStmt() const90306c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
90406c3fb27SDimitry Andric 
getClaimedVarUseSites() const90506c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
90606c3fb27SDimitry Andric     const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
90706c3fb27SDimitry Andric     const auto *DRE =
90806c3fb27SDimitry Andric         cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreImpCasts());
90906c3fb27SDimitry Andric     return {DRE};
91006c3fb27SDimitry Andric   }
91106c3fb27SDimitry Andric };
912bdd1243dSDimitry Andric } // namespace
913bdd1243dSDimitry Andric 
914bdd1243dSDimitry Andric namespace {
915bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect
91606c3fb27SDimitry Andric // declarations to its uses and make sure we've covered all uses with our
91706c3fb27SDimitry Andric // analysis before we try to fix the declaration.
918bdd1243dSDimitry Andric class DeclUseTracker {
919bdd1243dSDimitry Andric   using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
920bdd1243dSDimitry Andric   using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
921bdd1243dSDimitry Andric 
922bdd1243dSDimitry Andric   // Allocate on the heap for easier move.
923bdd1243dSDimitry Andric   std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
924bdd1243dSDimitry Andric   DefMapTy Defs{};
925bdd1243dSDimitry Andric 
926bdd1243dSDimitry Andric public:
927bdd1243dSDimitry Andric   DeclUseTracker() = default;
928bdd1243dSDimitry Andric   DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
92906c3fb27SDimitry Andric   DeclUseTracker &operator=(const DeclUseTracker &) = delete;
930bdd1243dSDimitry Andric   DeclUseTracker(DeclUseTracker &&) = default;
931bdd1243dSDimitry Andric   DeclUseTracker &operator=(DeclUseTracker &&) = default;
932bdd1243dSDimitry Andric 
933bdd1243dSDimitry Andric   // Start tracking a freshly discovered DRE.
discoverUse(const DeclRefExpr * DRE)934bdd1243dSDimitry Andric   void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
935bdd1243dSDimitry Andric 
936bdd1243dSDimitry Andric   // Stop tracking the DRE as it's been fully figured out.
claimUse(const DeclRefExpr * DRE)937bdd1243dSDimitry Andric   void claimUse(const DeclRefExpr *DRE) {
938bdd1243dSDimitry Andric     assert(Uses->count(DRE) &&
939bdd1243dSDimitry Andric            "DRE not found or claimed by multiple matchers!");
940bdd1243dSDimitry Andric     Uses->erase(DRE);
941bdd1243dSDimitry Andric   }
942bdd1243dSDimitry Andric 
943bdd1243dSDimitry Andric   // A variable is unclaimed if at least one use is unclaimed.
hasUnclaimedUses(const VarDecl * VD) const944bdd1243dSDimitry Andric   bool hasUnclaimedUses(const VarDecl *VD) const {
945bdd1243dSDimitry Andric     // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
946bdd1243dSDimitry Andric     return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
947bdd1243dSDimitry Andric       return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
948bdd1243dSDimitry Andric     });
949bdd1243dSDimitry Andric   }
950bdd1243dSDimitry Andric 
getUnclaimedUses(const VarDecl * VD) const9515f757f3fSDimitry Andric   UseSetTy getUnclaimedUses(const VarDecl *VD) const {
9525f757f3fSDimitry Andric     UseSetTy ReturnSet;
9535f757f3fSDimitry Andric     for (auto use : *Uses) {
9545f757f3fSDimitry Andric       if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
9555f757f3fSDimitry Andric         ReturnSet.insert(use);
9565f757f3fSDimitry Andric       }
9575f757f3fSDimitry Andric     }
9585f757f3fSDimitry Andric     return ReturnSet;
9595f757f3fSDimitry Andric   }
9605f757f3fSDimitry Andric 
discoverDecl(const DeclStmt * DS)961bdd1243dSDimitry Andric   void discoverDecl(const DeclStmt *DS) {
962bdd1243dSDimitry Andric     for (const Decl *D : DS->decls()) {
963bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(D)) {
964bdd1243dSDimitry Andric         // FIXME: Assertion temporarily disabled due to a bug in
965bdd1243dSDimitry Andric         // ASTMatcher internal behavior in presence of GNU
966bdd1243dSDimitry Andric         // statement-expressions. We need to properly investigate this
967bdd1243dSDimitry Andric         // because it can screw up our algorithm in other ways.
968bdd1243dSDimitry Andric         // assert(Defs.count(VD) == 0 && "Definition already discovered!");
969bdd1243dSDimitry Andric         Defs[VD] = DS;
970bdd1243dSDimitry Andric       }
971bdd1243dSDimitry Andric     }
972bdd1243dSDimitry Andric   }
973bdd1243dSDimitry Andric 
lookupDecl(const VarDecl * VD) const974bdd1243dSDimitry Andric   const DeclStmt *lookupDecl(const VarDecl *VD) const {
9755f757f3fSDimitry Andric     return Defs.lookup(VD);
976bdd1243dSDimitry Andric   }
977bdd1243dSDimitry Andric };
978bdd1243dSDimitry Andric } // namespace
979bdd1243dSDimitry Andric 
980bdd1243dSDimitry Andric namespace {
981bdd1243dSDimitry Andric // Strategy is a map from variables to the way we plan to emit fixes for
982bdd1243dSDimitry Andric // these variables. It is figured out gradually by trying different fixes
983bdd1243dSDimitry Andric // for different variables depending on gadgets in which these variables
984bdd1243dSDimitry Andric // participate.
985bdd1243dSDimitry Andric class Strategy {
986bdd1243dSDimitry Andric public:
987bdd1243dSDimitry Andric   enum class Kind {
988bdd1243dSDimitry Andric     Wontfix,  // We don't plan to emit a fixit for this variable.
989bdd1243dSDimitry Andric     Span,     // We recommend replacing the variable with std::span.
990bdd1243dSDimitry Andric     Iterator, // We recommend replacing the variable with std::span::iterator.
991bdd1243dSDimitry Andric     Array,    // We recommend replacing the variable with std::array.
992bdd1243dSDimitry Andric     Vector    // We recommend replacing the variable with std::vector.
993bdd1243dSDimitry Andric   };
994bdd1243dSDimitry Andric 
995bdd1243dSDimitry Andric private:
996bdd1243dSDimitry Andric   using MapTy = llvm::DenseMap<const VarDecl *, Kind>;
997bdd1243dSDimitry Andric 
998bdd1243dSDimitry Andric   MapTy Map;
999bdd1243dSDimitry Andric 
1000bdd1243dSDimitry Andric public:
1001bdd1243dSDimitry Andric   Strategy() = default;
1002bdd1243dSDimitry Andric   Strategy(const Strategy &) = delete; // Let's avoid copies.
100306c3fb27SDimitry Andric   Strategy &operator=(const Strategy &) = delete;
1004bdd1243dSDimitry Andric   Strategy(Strategy &&) = default;
100506c3fb27SDimitry Andric   Strategy &operator=(Strategy &&) = default;
1006bdd1243dSDimitry Andric 
set(const VarDecl * VD,Kind K)100706c3fb27SDimitry Andric   void set(const VarDecl *VD, Kind K) { Map[VD] = K; }
1008bdd1243dSDimitry Andric 
lookup(const VarDecl * VD) const1009bdd1243dSDimitry Andric   Kind lookup(const VarDecl *VD) const {
1010bdd1243dSDimitry Andric     auto I = Map.find(VD);
1011bdd1243dSDimitry Andric     if (I == Map.end())
1012bdd1243dSDimitry Andric       return Kind::Wontfix;
1013bdd1243dSDimitry Andric 
1014bdd1243dSDimitry Andric     return I->second;
1015bdd1243dSDimitry Andric   }
1016bdd1243dSDimitry Andric };
1017bdd1243dSDimitry Andric } // namespace
1018bdd1243dSDimitry Andric 
101906c3fb27SDimitry Andric 
102006c3fb27SDimitry Andric // Representing a pointer type expression of the form `++Ptr` in an Unspecified
102106c3fb27SDimitry Andric // Pointer Context (UPC):
102206c3fb27SDimitry Andric class UPCPreIncrementGadget : public FixableGadget {
102306c3fb27SDimitry Andric private:
102406c3fb27SDimitry Andric   static constexpr const char *const UPCPreIncrementTag =
102506c3fb27SDimitry Andric     "PointerPreIncrementUnderUPC";
102606c3fb27SDimitry Andric   const UnaryOperator *Node; // the `++Ptr` node
102706c3fb27SDimitry Andric 
102806c3fb27SDimitry Andric public:
UPCPreIncrementGadget(const MatchFinder::MatchResult & Result)102906c3fb27SDimitry Andric   UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
103006c3fb27SDimitry Andric     : FixableGadget(Kind::UPCPreIncrement),
103106c3fb27SDimitry Andric       Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
103206c3fb27SDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
103306c3fb27SDimitry Andric   }
103406c3fb27SDimitry Andric 
classof(const Gadget * G)103506c3fb27SDimitry Andric   static bool classof(const Gadget *G) {
103606c3fb27SDimitry Andric     return G->getKind() == Kind::UPCPreIncrement;
103706c3fb27SDimitry Andric   }
103806c3fb27SDimitry Andric 
matcher()103906c3fb27SDimitry Andric   static Matcher matcher() {
104006c3fb27SDimitry Andric     // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
104106c3fb27SDimitry Andric     // Although currently we can only provide fix-its when `Ptr` is a DRE, we
104206c3fb27SDimitry Andric     // can have the matcher be general, so long as `getClaimedVarUseSites` does
104306c3fb27SDimitry Andric     // things right.
104406c3fb27SDimitry Andric     return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
104506c3fb27SDimitry Andric 								    unaryOperator(isPreInc(),
10465f757f3fSDimitry Andric 										  hasUnaryOperand(declRefExpr(
10475f757f3fSDimitry Andric                                                     toSupportedVariable()))
104806c3fb27SDimitry Andric 										  ).bind(UPCPreIncrementTag)))));
104906c3fb27SDimitry Andric   }
105006c3fb27SDimitry Andric 
105106c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
105206c3fb27SDimitry Andric 
getBaseStmt() const105306c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
105406c3fb27SDimitry Andric 
getClaimedVarUseSites() const105506c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
105606c3fb27SDimitry Andric     return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
105706c3fb27SDimitry Andric   }
105806c3fb27SDimitry Andric };
105906c3fb27SDimitry Andric 
10605f757f3fSDimitry Andric // Representing a pointer type expression of the form `Ptr += n` in an
10615f757f3fSDimitry Andric // Unspecified Untyped Context (UUC):
10625f757f3fSDimitry Andric class UUCAddAssignGadget : public FixableGadget {
10635f757f3fSDimitry Andric private:
10645f757f3fSDimitry Andric   static constexpr const char *const UUCAddAssignTag =
10655f757f3fSDimitry Andric       "PointerAddAssignUnderUUC";
10665f757f3fSDimitry Andric   static constexpr const char *const OffsetTag = "Offset";
10675f757f3fSDimitry Andric 
10685f757f3fSDimitry Andric   const BinaryOperator *Node; // the `Ptr += n` node
10695f757f3fSDimitry Andric   const Expr *Offset = nullptr;
10705f757f3fSDimitry Andric 
10715f757f3fSDimitry Andric public:
UUCAddAssignGadget(const MatchFinder::MatchResult & Result)10725f757f3fSDimitry Andric   UUCAddAssignGadget(const MatchFinder::MatchResult &Result)
10735f757f3fSDimitry Andric       : FixableGadget(Kind::UUCAddAssign),
10745f757f3fSDimitry Andric         Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
10755f757f3fSDimitry Andric         Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
10765f757f3fSDimitry Andric     assert(Node != nullptr && "Expecting a non-null matching result");
10775f757f3fSDimitry Andric   }
10785f757f3fSDimitry Andric 
classof(const Gadget * G)10795f757f3fSDimitry Andric   static bool classof(const Gadget *G) {
10805f757f3fSDimitry Andric     return G->getKind() == Kind::UUCAddAssign;
10815f757f3fSDimitry Andric   }
10825f757f3fSDimitry Andric 
matcher()10835f757f3fSDimitry Andric   static Matcher matcher() {
10845f757f3fSDimitry Andric     return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
10855f757f3fSDimitry Andric         binaryOperator(hasOperatorName("+="),
10865f757f3fSDimitry Andric                        hasLHS(declRefExpr(toSupportedVariable())),
10875f757f3fSDimitry Andric                        hasRHS(expr().bind(OffsetTag)))
10885f757f3fSDimitry Andric             .bind(UUCAddAssignTag)))));
10895f757f3fSDimitry Andric   }
10905f757f3fSDimitry Andric 
10915f757f3fSDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &S) const override;
10925f757f3fSDimitry Andric 
getBaseStmt() const10935f757f3fSDimitry Andric   virtual const Stmt *getBaseStmt() const override { return Node; }
10945f757f3fSDimitry Andric 
getClaimedVarUseSites() const10955f757f3fSDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const override {
10965f757f3fSDimitry Andric     return {dyn_cast<DeclRefExpr>(Node->getLHS())};
10975f757f3fSDimitry Andric   }
10985f757f3fSDimitry Andric };
10995f757f3fSDimitry Andric 
110006c3fb27SDimitry Andric // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
110106c3fb27SDimitry Andric // ptr)`:
110206c3fb27SDimitry Andric class DerefSimplePtrArithFixableGadget : public FixableGadget {
110306c3fb27SDimitry Andric   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
110406c3fb27SDimitry Andric   static constexpr const char *const DerefOpTag = "DerefOp";
110506c3fb27SDimitry Andric   static constexpr const char *const AddOpTag = "AddOp";
110606c3fb27SDimitry Andric   static constexpr const char *const OffsetTag = "Offset";
110706c3fb27SDimitry Andric 
110806c3fb27SDimitry Andric   const DeclRefExpr *BaseDeclRefExpr = nullptr;
110906c3fb27SDimitry Andric   const UnaryOperator *DerefOp = nullptr;
111006c3fb27SDimitry Andric   const BinaryOperator *AddOp = nullptr;
111106c3fb27SDimitry Andric   const IntegerLiteral *Offset = nullptr;
111206c3fb27SDimitry Andric 
111306c3fb27SDimitry Andric public:
DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult & Result)111406c3fb27SDimitry Andric   DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)
111506c3fb27SDimitry Andric       : FixableGadget(Kind::DerefSimplePtrArithFixable),
111606c3fb27SDimitry Andric         BaseDeclRefExpr(
111706c3fb27SDimitry Andric             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
111806c3fb27SDimitry Andric         DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
111906c3fb27SDimitry Andric         AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
112006c3fb27SDimitry Andric         Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
112106c3fb27SDimitry Andric 
matcher()112206c3fb27SDimitry Andric   static Matcher matcher() {
112306c3fb27SDimitry Andric     // clang-format off
112406c3fb27SDimitry Andric     auto ThePtr = expr(hasPointerType(),
11255f757f3fSDimitry Andric                        ignoringImpCasts(declRefExpr(toSupportedVariable()).
11265f757f3fSDimitry Andric                                         bind(BaseDeclRefExprTag)));
112706c3fb27SDimitry Andric     auto PlusOverPtrAndInteger = expr(anyOf(
112806c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
112906c3fb27SDimitry Andric                          hasRHS(integerLiteral().bind(OffsetTag)))
113006c3fb27SDimitry Andric                          .bind(AddOpTag),
113106c3fb27SDimitry Andric           binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
113206c3fb27SDimitry Andric                          hasLHS(integerLiteral().bind(OffsetTag)))
113306c3fb27SDimitry Andric                          .bind(AddOpTag)));
113406c3fb27SDimitry Andric     return isInUnspecifiedLvalueContext(unaryOperator(
113506c3fb27SDimitry Andric         hasOperatorName("*"),
113606c3fb27SDimitry Andric         hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
113706c3fb27SDimitry Andric         .bind(DerefOpTag));
113806c3fb27SDimitry Andric     // clang-format on
113906c3fb27SDimitry Andric   }
114006c3fb27SDimitry Andric 
114106c3fb27SDimitry Andric   virtual std::optional<FixItList> getFixits(const Strategy &s) const final;
114206c3fb27SDimitry Andric 
114306c3fb27SDimitry Andric   // TODO remove this method from FixableGadget interface
getBaseStmt() const114406c3fb27SDimitry Andric   virtual const Stmt *getBaseStmt() const final { return nullptr; }
114506c3fb27SDimitry Andric 
getClaimedVarUseSites() const114606c3fb27SDimitry Andric   virtual DeclUseList getClaimedVarUseSites() const final {
114706c3fb27SDimitry Andric     return {BaseDeclRefExpr};
114806c3fb27SDimitry Andric   }
114906c3fb27SDimitry Andric };
115006c3fb27SDimitry Andric 
1151bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits.
115206c3fb27SDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
findGadgets(const Decl * D,const UnsafeBufferUsageHandler & Handler,bool EmitSuggestions)115306c3fb27SDimitry Andric findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
115406c3fb27SDimitry Andric             bool EmitSuggestions) {
1155bdd1243dSDimitry Andric 
1156bdd1243dSDimitry Andric   struct GadgetFinderCallback : MatchFinder::MatchCallback {
1157bdd1243dSDimitry Andric     FixableGadgetList FixableGadgets;
1158bdd1243dSDimitry Andric     WarningGadgetList WarningGadgets;
1159bdd1243dSDimitry Andric     DeclUseTracker Tracker;
1160bdd1243dSDimitry Andric 
1161bdd1243dSDimitry Andric     void run(const MatchFinder::MatchResult &Result) override {
1162bdd1243dSDimitry Andric       // In debug mode, assert that we've found exactly one gadget.
1163bdd1243dSDimitry Andric       // This helps us avoid conflicts in .bind() tags.
1164bdd1243dSDimitry Andric #if NDEBUG
1165bdd1243dSDimitry Andric #define NEXT return
1166bdd1243dSDimitry Andric #else
1167bdd1243dSDimitry Andric       [[maybe_unused]] int numFound = 0;
1168bdd1243dSDimitry Andric #define NEXT ++numFound
1169bdd1243dSDimitry Andric #endif
1170bdd1243dSDimitry Andric 
1171bdd1243dSDimitry Andric       if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
1172bdd1243dSDimitry Andric         Tracker.discoverUse(DRE);
1173bdd1243dSDimitry Andric         NEXT;
1174bdd1243dSDimitry Andric       }
1175bdd1243dSDimitry Andric 
1176bdd1243dSDimitry Andric       if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
1177bdd1243dSDimitry Andric         Tracker.discoverDecl(DS);
1178bdd1243dSDimitry Andric         NEXT;
1179bdd1243dSDimitry Andric       }
1180bdd1243dSDimitry Andric 
1181bdd1243dSDimitry Andric       // Figure out which matcher we've found, and call the appropriate
1182bdd1243dSDimitry Andric       // subclass constructor.
1183bdd1243dSDimitry Andric       // FIXME: Can we do this more logarithmically?
1184bdd1243dSDimitry Andric #define FIXABLE_GADGET(name)                                                   \
1185bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1186bdd1243dSDimitry Andric     FixableGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1187bdd1243dSDimitry Andric     NEXT;                                                                      \
1188bdd1243dSDimitry Andric   }
1189bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1190bdd1243dSDimitry Andric #define WARNING_GADGET(name)                                                   \
1191bdd1243dSDimitry Andric   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
1192bdd1243dSDimitry Andric     WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
1193bdd1243dSDimitry Andric     NEXT;                                                                      \
1194bdd1243dSDimitry Andric   }
1195bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1196bdd1243dSDimitry Andric 
1197bdd1243dSDimitry Andric       assert(numFound >= 1 && "Gadgets not found in match result!");
1198bdd1243dSDimitry Andric       assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
1199bdd1243dSDimitry Andric     }
1200bdd1243dSDimitry Andric   };
1201bdd1243dSDimitry Andric 
1202bdd1243dSDimitry Andric   MatchFinder M;
1203bdd1243dSDimitry Andric   GadgetFinderCallback CB;
1204bdd1243dSDimitry Andric 
1205bdd1243dSDimitry Andric   // clang-format off
1206bdd1243dSDimitry Andric   M.addMatcher(
120706c3fb27SDimitry Andric       stmt(
120806c3fb27SDimitry Andric         forEachDescendantEvaluatedStmt(stmt(anyOf(
1209bdd1243dSDimitry Andric           // Add Gadget::matcher() for every gadget in the registry.
121006c3fb27SDimitry Andric #define WARNING_GADGET(x)                                                      \
121106c3fb27SDimitry Andric           allOf(x ## Gadget::matcher().bind(#x),                               \
121206c3fb27SDimitry Andric                 notInSafeBufferOptOut(&Handler)),
121306c3fb27SDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
121406c3fb27SDimitry Andric             // Avoid a hanging comma.
121506c3fb27SDimitry Andric             unless(stmt())
121606c3fb27SDimitry Andric         )))
121706c3fb27SDimitry Andric     ),
121806c3fb27SDimitry Andric     &CB
121906c3fb27SDimitry Andric   );
122006c3fb27SDimitry Andric   // clang-format on
122106c3fb27SDimitry Andric 
122206c3fb27SDimitry Andric   if (EmitSuggestions) {
122306c3fb27SDimitry Andric     // clang-format off
122406c3fb27SDimitry Andric     M.addMatcher(
122506c3fb27SDimitry Andric         stmt(
122606c3fb27SDimitry Andric           forEachDescendantStmt(stmt(eachOf(
122706c3fb27SDimitry Andric #define FIXABLE_GADGET(x)                                                      \
1228bdd1243dSDimitry Andric             x ## Gadget::matcher().bind(#x),
1229bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1230bdd1243dSDimitry Andric             // In parallel, match all DeclRefExprs so that to find out
1231bdd1243dSDimitry Andric             // whether there are any uncovered by gadgets.
1232bdd1243dSDimitry Andric             declRefExpr(anyOf(hasPointerType(), hasArrayType()),
12335f757f3fSDimitry Andric                         to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
1234bdd1243dSDimitry Andric             // Also match DeclStmts because we'll need them when fixing
1235bdd1243dSDimitry Andric             // their underlying VarDecls that otherwise don't have
1236bdd1243dSDimitry Andric             // any backreferences to DeclStmts.
1237bdd1243dSDimitry Andric             declStmt().bind("any_ds")
123806c3fb27SDimitry Andric           )))
123906c3fb27SDimitry Andric       ),
1240bdd1243dSDimitry Andric       &CB
1241bdd1243dSDimitry Andric     );
1242bdd1243dSDimitry Andric     // clang-format on
124306c3fb27SDimitry Andric   }
1244bdd1243dSDimitry Andric 
1245bdd1243dSDimitry Andric   M.match(*D->getBody(), D->getASTContext());
124606c3fb27SDimitry Andric   return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
124706c3fb27SDimitry Andric           std::move(CB.Tracker)};
1248bdd1243dSDimitry Andric }
1249bdd1243dSDimitry Andric 
125006c3fb27SDimitry Andric // Compares AST nodes by source locations.
125106c3fb27SDimitry Andric template <typename NodeTy> struct CompareNode {
operator ()CompareNode125206c3fb27SDimitry Andric   bool operator()(const NodeTy *N1, const NodeTy *N2) const {
125306c3fb27SDimitry Andric     return N1->getBeginLoc().getRawEncoding() <
125406c3fb27SDimitry Andric            N2->getBeginLoc().getRawEncoding();
125506c3fb27SDimitry Andric   }
125606c3fb27SDimitry Andric };
125706c3fb27SDimitry Andric 
1258bdd1243dSDimitry Andric struct WarningGadgetSets {
125906c3fb27SDimitry Andric   std::map<const VarDecl *, std::set<const WarningGadget *>,
126006c3fb27SDimitry Andric            // To keep keys sorted by their locations in the map so that the
126106c3fb27SDimitry Andric            // order is deterministic:
126206c3fb27SDimitry Andric            CompareNode<VarDecl>>
126306c3fb27SDimitry Andric       byVar;
1264bdd1243dSDimitry Andric   // These Gadgets are not related to pointer variables (e. g. temporaries).
126506c3fb27SDimitry Andric   llvm::SmallVector<const WarningGadget *, 16> noVar;
1266bdd1243dSDimitry Andric };
1267bdd1243dSDimitry Andric 
1268bdd1243dSDimitry Andric static WarningGadgetSets
groupWarningGadgetsByVar(const WarningGadgetList & AllUnsafeOperations)126906c3fb27SDimitry Andric groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
1270bdd1243dSDimitry Andric   WarningGadgetSets result;
1271bdd1243dSDimitry Andric   // If some gadgets cover more than one
1272bdd1243dSDimitry Andric   // variable, they'll appear more than once in the map.
1273bdd1243dSDimitry Andric   for (auto &G : AllUnsafeOperations) {
1274bdd1243dSDimitry Andric     DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
1275bdd1243dSDimitry Andric 
1276bdd1243dSDimitry Andric     bool AssociatedWithVarDecl = false;
1277bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
1278bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
127906c3fb27SDimitry Andric         result.byVar[VD].insert(G.get());
1280bdd1243dSDimitry Andric         AssociatedWithVarDecl = true;
1281bdd1243dSDimitry Andric       }
1282bdd1243dSDimitry Andric     }
1283bdd1243dSDimitry Andric 
1284bdd1243dSDimitry Andric     if (!AssociatedWithVarDecl) {
128506c3fb27SDimitry Andric       result.noVar.push_back(G.get());
1286bdd1243dSDimitry Andric       continue;
1287bdd1243dSDimitry Andric     }
1288bdd1243dSDimitry Andric   }
1289bdd1243dSDimitry Andric   return result;
1290bdd1243dSDimitry Andric }
1291bdd1243dSDimitry Andric 
1292bdd1243dSDimitry Andric struct FixableGadgetSets {
12935f757f3fSDimitry Andric   std::map<const VarDecl *, std::set<const FixableGadget *>,
12945f757f3fSDimitry Andric            // To keep keys sorted by their locations in the map so that the
12955f757f3fSDimitry Andric            // order is deterministic:
12965f757f3fSDimitry Andric            CompareNode<VarDecl>>
12975f757f3fSDimitry Andric       byVar;
1298bdd1243dSDimitry Andric };
1299bdd1243dSDimitry Andric 
1300bdd1243dSDimitry Andric static FixableGadgetSets
groupFixablesByVar(FixableGadgetList && AllFixableOperations)1301bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
1302bdd1243dSDimitry Andric   FixableGadgetSets FixablesForUnsafeVars;
1303bdd1243dSDimitry Andric   for (auto &F : AllFixableOperations) {
1304bdd1243dSDimitry Andric     DeclUseList DREs = F->getClaimedVarUseSites();
1305bdd1243dSDimitry Andric 
1306bdd1243dSDimitry Andric     for (const DeclRefExpr *DRE : DREs) {
1307bdd1243dSDimitry Andric       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
130806c3fb27SDimitry Andric         FixablesForUnsafeVars.byVar[VD].insert(F.get());
1309bdd1243dSDimitry Andric       }
1310bdd1243dSDimitry Andric     }
1311bdd1243dSDimitry Andric   }
1312bdd1243dSDimitry Andric   return FixablesForUnsafeVars;
1313bdd1243dSDimitry Andric }
1314bdd1243dSDimitry Andric 
anyConflict(const SmallVectorImpl<FixItHint> & FixIts,const SourceManager & SM)131506c3fb27SDimitry Andric bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts,
131606c3fb27SDimitry Andric                                   const SourceManager &SM) {
131706c3fb27SDimitry Andric   // A simple interval overlap detection algorithm.  Sorts all ranges by their
131806c3fb27SDimitry Andric   // begin location then finds the first overlap in one pass.
131906c3fb27SDimitry Andric   std::vector<const FixItHint *> All; // a copy of `FixIts`
132006c3fb27SDimitry Andric 
132106c3fb27SDimitry Andric   for (const FixItHint &H : FixIts)
132206c3fb27SDimitry Andric     All.push_back(&H);
132306c3fb27SDimitry Andric   std::sort(All.begin(), All.end(),
132406c3fb27SDimitry Andric             [&SM](const FixItHint *H1, const FixItHint *H2) {
132506c3fb27SDimitry Andric               return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
132606c3fb27SDimitry Andric                                                   H2->RemoveRange.getBegin());
132706c3fb27SDimitry Andric             });
132806c3fb27SDimitry Andric 
132906c3fb27SDimitry Andric   const FixItHint *CurrHint = nullptr;
133006c3fb27SDimitry Andric 
133106c3fb27SDimitry Andric   for (const FixItHint *Hint : All) {
133206c3fb27SDimitry Andric     if (!CurrHint ||
133306c3fb27SDimitry Andric         SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
133406c3fb27SDimitry Andric                                      Hint->RemoveRange.getBegin())) {
133506c3fb27SDimitry Andric       // Either to initialize `CurrHint` or `CurrHint` does not
133606c3fb27SDimitry Andric       // overlap with `Hint`:
133706c3fb27SDimitry Andric       CurrHint = Hint;
133806c3fb27SDimitry Andric     } else
133906c3fb27SDimitry Andric       // In case `Hint` overlaps the `CurrHint`, we found at least one
134006c3fb27SDimitry Andric       // conflict:
134106c3fb27SDimitry Andric       return true;
134206c3fb27SDimitry Andric   }
134306c3fb27SDimitry Andric   return false;
134406c3fb27SDimitry Andric }
134506c3fb27SDimitry Andric 
134606c3fb27SDimitry Andric std::optional<FixItList>
getFixits(const Strategy & S) const134706c3fb27SDimitry Andric PointerAssignmentGadget::getFixits(const Strategy &S) const {
134806c3fb27SDimitry Andric   const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
134906c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
135006c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
135106c3fb27SDimitry Andric     case Strategy::Kind::Span:
135206c3fb27SDimitry Andric       if (S.lookup(RightVD) == Strategy::Kind::Span)
135306c3fb27SDimitry Andric         return FixItList{};
135406c3fb27SDimitry Andric       return std::nullopt;
135506c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
135606c3fb27SDimitry Andric       return std::nullopt;
135706c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
135806c3fb27SDimitry Andric     case Strategy::Kind::Array:
135906c3fb27SDimitry Andric     case Strategy::Kind::Vector:
136006c3fb27SDimitry Andric       llvm_unreachable("unsupported strategies for FixableGadgets");
136106c3fb27SDimitry Andric   }
136206c3fb27SDimitry Andric   return std::nullopt;
136306c3fb27SDimitry Andric }
136406c3fb27SDimitry Andric 
136506c3fb27SDimitry Andric std::optional<FixItList>
getFixits(const Strategy & S) const136606c3fb27SDimitry Andric PointerInitGadget::getFixits(const Strategy &S) const {
136706c3fb27SDimitry Andric   const auto *LeftVD = PtrInitLHS;
136806c3fb27SDimitry Andric   const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
136906c3fb27SDimitry Andric   switch (S.lookup(LeftVD)) {
137006c3fb27SDimitry Andric     case Strategy::Kind::Span:
137106c3fb27SDimitry Andric       if (S.lookup(RightVD) == Strategy::Kind::Span)
137206c3fb27SDimitry Andric         return FixItList{};
137306c3fb27SDimitry Andric       return std::nullopt;
137406c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
137506c3fb27SDimitry Andric       return std::nullopt;
137606c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
137706c3fb27SDimitry Andric     case Strategy::Kind::Array:
137806c3fb27SDimitry Andric     case Strategy::Kind::Vector:
137906c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
138006c3fb27SDimitry Andric   }
138106c3fb27SDimitry Andric   return std::nullopt;
138206c3fb27SDimitry Andric }
138306c3fb27SDimitry Andric 
isNonNegativeIntegerExpr(const Expr * Expr,const VarDecl * VD,const ASTContext & Ctx)13845f757f3fSDimitry Andric static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
13855f757f3fSDimitry Andric                                      const ASTContext &Ctx) {
13865f757f3fSDimitry Andric   if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
13875f757f3fSDimitry Andric     if (ConstVal->isNegative())
13885f757f3fSDimitry Andric       return false;
13895f757f3fSDimitry Andric   } else if (!Expr->getType()->isUnsignedIntegerType())
13905f757f3fSDimitry Andric     return false;
13915f757f3fSDimitry Andric   return true;
13925f757f3fSDimitry Andric }
13935f757f3fSDimitry Andric 
139406c3fb27SDimitry Andric std::optional<FixItList>
getFixits(const Strategy & S) const139506c3fb27SDimitry Andric ULCArraySubscriptGadget::getFixits(const Strategy &S) const {
139606c3fb27SDimitry Andric   if (const auto *DRE =
139706c3fb27SDimitry Andric           dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
139806c3fb27SDimitry Andric     if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
139906c3fb27SDimitry Andric       switch (S.lookup(VD)) {
140006c3fb27SDimitry Andric       case Strategy::Kind::Span: {
14015f757f3fSDimitry Andric 
140206c3fb27SDimitry Andric         // If the index has a negative constant value, we give up as no valid
140306c3fb27SDimitry Andric         // fix-it can be generated:
140406c3fb27SDimitry Andric         const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
140506c3fb27SDimitry Andric             VD->getASTContext();
14065f757f3fSDimitry Andric         if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
140706c3fb27SDimitry Andric           return std::nullopt;
140806c3fb27SDimitry Andric         // no-op is a good fix-it, otherwise
140906c3fb27SDimitry Andric         return FixItList{};
141006c3fb27SDimitry Andric       }
141106c3fb27SDimitry Andric       case Strategy::Kind::Wontfix:
141206c3fb27SDimitry Andric       case Strategy::Kind::Iterator:
141306c3fb27SDimitry Andric       case Strategy::Kind::Array:
141406c3fb27SDimitry Andric       case Strategy::Kind::Vector:
141506c3fb27SDimitry Andric         llvm_unreachable("unsupported strategies for FixableGadgets");
141606c3fb27SDimitry Andric       }
141706c3fb27SDimitry Andric     }
141806c3fb27SDimitry Andric   return std::nullopt;
141906c3fb27SDimitry Andric }
142006c3fb27SDimitry Andric 
142106c3fb27SDimitry Andric static std::optional<FixItList> // forward declaration
142206c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
142306c3fb27SDimitry Andric 
142406c3fb27SDimitry Andric std::optional<FixItList>
getFixits(const Strategy & S) const142506c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget::getFixits(const Strategy &S) const {
142606c3fb27SDimitry Andric   auto DREs = getClaimedVarUseSites();
142706c3fb27SDimitry Andric   const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
142806c3fb27SDimitry Andric 
142906c3fb27SDimitry Andric   switch (S.lookup(VD)) {
143006c3fb27SDimitry Andric   case Strategy::Kind::Span:
143106c3fb27SDimitry Andric     return fixUPCAddressofArraySubscriptWithSpan(Node);
143206c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
143306c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
143406c3fb27SDimitry Andric   case Strategy::Kind::Array:
143506c3fb27SDimitry Andric   case Strategy::Kind::Vector:
143606c3fb27SDimitry Andric     llvm_unreachable("unsupported strategies for FixableGadgets");
143706c3fb27SDimitry Andric   }
143806c3fb27SDimitry Andric   return std::nullopt; // something went wrong, no fix-it
143906c3fb27SDimitry Andric }
144006c3fb27SDimitry Andric 
144106c3fb27SDimitry Andric // FIXME: this function should be customizable through format
getEndOfLine()144206c3fb27SDimitry Andric static StringRef getEndOfLine() {
144306c3fb27SDimitry Andric   static const char *const EOL = "\n";
144406c3fb27SDimitry Andric   return EOL;
144506c3fb27SDimitry Andric }
144606c3fb27SDimitry Andric 
144706c3fb27SDimitry Andric // Returns the text indicating that the user needs to provide input there:
getUserFillPlaceHolder(StringRef HintTextToUser="placeholder")144806c3fb27SDimitry Andric std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
144906c3fb27SDimitry Andric   std::string s = std::string("<# ");
145006c3fb27SDimitry Andric   s += HintTextToUser;
145106c3fb27SDimitry Andric   s += " #>";
145206c3fb27SDimitry Andric   return s;
145306c3fb27SDimitry Andric }
145406c3fb27SDimitry Andric 
145506c3fb27SDimitry Andric // Return the text representation of the given `APInt Val`:
getAPIntText(APInt Val)145606c3fb27SDimitry Andric static std::string getAPIntText(APInt Val) {
145706c3fb27SDimitry Andric   SmallVector<char> Txt;
145806c3fb27SDimitry Andric   Val.toString(Txt, 10, true);
145906c3fb27SDimitry Andric   // APInt::toString does not add '\0' to the end of the string for us:
146006c3fb27SDimitry Andric   Txt.push_back('\0');
146106c3fb27SDimitry Andric   return Txt.data();
146206c3fb27SDimitry Andric }
146306c3fb27SDimitry Andric 
146406c3fb27SDimitry Andric // Return the source location of the last character of the AST `Node`.
146506c3fb27SDimitry Andric template <typename NodeTy>
146606c3fb27SDimitry Andric static std::optional<SourceLocation>
getEndCharLoc(const NodeTy * Node,const SourceManager & SM,const LangOptions & LangOpts)146706c3fb27SDimitry Andric getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
146806c3fb27SDimitry Andric               const LangOptions &LangOpts) {
146906c3fb27SDimitry Andric   unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
147006c3fb27SDimitry Andric   SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
147106c3fb27SDimitry Andric 
147206c3fb27SDimitry Andric   if (Loc.isValid())
147306c3fb27SDimitry Andric     return Loc;
147406c3fb27SDimitry Andric 
147506c3fb27SDimitry Andric   return std::nullopt;
147606c3fb27SDimitry Andric }
147706c3fb27SDimitry Andric 
147806c3fb27SDimitry Andric // Return the source location just past the last character of the AST `Node`.
147906c3fb27SDimitry Andric template <typename NodeTy>
getPastLoc(const NodeTy * Node,const SourceManager & SM,const LangOptions & LangOpts)148006c3fb27SDimitry Andric static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
148106c3fb27SDimitry Andric                                                 const SourceManager &SM,
148206c3fb27SDimitry Andric                                                 const LangOptions &LangOpts) {
148306c3fb27SDimitry Andric   SourceLocation Loc =
148406c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
148506c3fb27SDimitry Andric   if (Loc.isValid())
148606c3fb27SDimitry Andric     return Loc;
148706c3fb27SDimitry Andric   return std::nullopt;
148806c3fb27SDimitry Andric }
148906c3fb27SDimitry Andric 
149006c3fb27SDimitry Andric // Return text representation of an `Expr`.
getExprText(const Expr * E,const SourceManager & SM,const LangOptions & LangOpts)149106c3fb27SDimitry Andric static std::optional<StringRef> getExprText(const Expr *E,
149206c3fb27SDimitry Andric                                             const SourceManager &SM,
149306c3fb27SDimitry Andric                                             const LangOptions &LangOpts) {
149406c3fb27SDimitry Andric   std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
149506c3fb27SDimitry Andric 
149606c3fb27SDimitry Andric   if (LastCharLoc)
149706c3fb27SDimitry Andric     return Lexer::getSourceText(
149806c3fb27SDimitry Andric         CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
149906c3fb27SDimitry Andric         LangOpts);
150006c3fb27SDimitry Andric 
150106c3fb27SDimitry Andric   return std::nullopt;
150206c3fb27SDimitry Andric }
150306c3fb27SDimitry Andric 
150406c3fb27SDimitry Andric // Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
getRangeText(SourceRange SR,const SourceManager & SM,const LangOptions & LangOpts)150506c3fb27SDimitry Andric static std::optional<StringRef> getRangeText(SourceRange SR,
150606c3fb27SDimitry Andric                                              const SourceManager &SM,
150706c3fb27SDimitry Andric                                              const LangOptions &LangOpts) {
150806c3fb27SDimitry Andric   bool Invalid = false;
15095f757f3fSDimitry Andric   CharSourceRange CSR = CharSourceRange::getCharRange(SR);
151006c3fb27SDimitry Andric   StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
151106c3fb27SDimitry Andric 
151206c3fb27SDimitry Andric   if (!Invalid)
151306c3fb27SDimitry Andric     return Text;
151406c3fb27SDimitry Andric   return std::nullopt;
151506c3fb27SDimitry Andric }
151606c3fb27SDimitry Andric 
15175f757f3fSDimitry Andric // Returns the begin location of the identifier of the given variable
15185f757f3fSDimitry Andric // declaration.
getVarDeclIdentifierLoc(const VarDecl * VD)15195f757f3fSDimitry Andric static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) {
15205f757f3fSDimitry Andric   // According to the implementation of `VarDecl`, `VD->getLocation()` actually
15215f757f3fSDimitry Andric   // returns the begin location of the identifier of the declaration:
15225f757f3fSDimitry Andric   return VD->getLocation();
15235f757f3fSDimitry Andric }
15245f757f3fSDimitry Andric 
15255f757f3fSDimitry Andric // Returns the literal text of the identifier of the given variable declaration.
15265f757f3fSDimitry Andric static std::optional<StringRef>
getVarDeclIdentifierText(const VarDecl * VD,const SourceManager & SM,const LangOptions & LangOpts)15275f757f3fSDimitry Andric getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM,
15285f757f3fSDimitry Andric                          const LangOptions &LangOpts) {
15295f757f3fSDimitry Andric   SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
15305f757f3fSDimitry Andric   SourceLocation ParmIdentEndLoc =
15315f757f3fSDimitry Andric       Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
15325f757f3fSDimitry Andric 
15335f757f3fSDimitry Andric   if (ParmIdentEndLoc.isMacroID() &&
15345f757f3fSDimitry Andric       !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
15355f757f3fSDimitry Andric     return std::nullopt;
15365f757f3fSDimitry Andric   return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
15375f757f3fSDimitry Andric }
15385f757f3fSDimitry Andric 
15395f757f3fSDimitry Andric // We cannot fix a variable declaration if it has some other specifiers than the
15405f757f3fSDimitry Andric // type specifier.  Because the source ranges of those specifiers could overlap
15415f757f3fSDimitry Andric // with the source range that is being replaced using fix-its.  Especially when
15425f757f3fSDimitry Andric // we often cannot obtain accurate source ranges of cv-qualified type
15435f757f3fSDimitry Andric // specifiers.
15445f757f3fSDimitry Andric // FIXME: also deal with type attributes
hasUnsupportedSpecifiers(const VarDecl * VD,const SourceManager & SM)15455f757f3fSDimitry Andric static bool hasUnsupportedSpecifiers(const VarDecl *VD,
15465f757f3fSDimitry Andric                                      const SourceManager &SM) {
15475f757f3fSDimitry Andric   // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
15485f757f3fSDimitry Andric   // source range of `VD`:
15495f757f3fSDimitry Andric   bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
15505f757f3fSDimitry Andric     return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
15515f757f3fSDimitry Andric                                           VD->getBeginLoc())) &&
15525f757f3fSDimitry Andric            !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
15535f757f3fSDimitry Andric                                           At->getRange().getBegin()));
15545f757f3fSDimitry Andric   });
15555f757f3fSDimitry Andric   return VD->isInlineSpecified() || VD->isConstexpr() ||
15565f757f3fSDimitry Andric          VD->hasConstantInitialization() || !VD->hasLocalStorage() ||
15575f757f3fSDimitry Andric          AttrRangeOverlapping;
15585f757f3fSDimitry Andric }
15595f757f3fSDimitry Andric 
15605f757f3fSDimitry Andric // Returns the `SourceRange` of `D`.  The reason why this function exists is
15615f757f3fSDimitry Andric // that `D->getSourceRange()` may return a range where the end location is the
15625f757f3fSDimitry Andric // starting location of the last token.  The end location of the source range
15635f757f3fSDimitry Andric // returned by this function is the last location of the last token.
getSourceRangeToTokenEnd(const Decl * D,const SourceManager & SM,const LangOptions & LangOpts)15645f757f3fSDimitry Andric static SourceRange getSourceRangeToTokenEnd(const Decl *D,
15655f757f3fSDimitry Andric                                             const SourceManager &SM,
15665f757f3fSDimitry Andric                                             const LangOptions &LangOpts) {
15675f757f3fSDimitry Andric   SourceLocation Begin = D->getBeginLoc();
15685f757f3fSDimitry Andric   SourceLocation
15695f757f3fSDimitry Andric     End = // `D->getEndLoc` should always return the starting location of the
15705f757f3fSDimitry Andric     // last token, so we should get the end of the token
15715f757f3fSDimitry Andric     Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
15725f757f3fSDimitry Andric 
15735f757f3fSDimitry Andric   return SourceRange(Begin, End);
15745f757f3fSDimitry Andric }
15755f757f3fSDimitry Andric 
157606c3fb27SDimitry Andric // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
157706c3fb27SDimitry Andric // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does not
157806c3fb27SDimitry Andric // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
157906c3fb27SDimitry Andric // :( ), `Qualifiers` of the pointee type is returned separately through the
158006c3fb27SDimitry Andric // output parameter `QualifiersToAppend`.
158106c3fb27SDimitry Andric static std::optional<std::string>
getPointeeTypeText(const VarDecl * VD,const SourceManager & SM,const LangOptions & LangOpts,std::optional<Qualifiers> * QualifiersToAppend)15825f757f3fSDimitry Andric getPointeeTypeText(const VarDecl *VD, const SourceManager &SM,
158306c3fb27SDimitry Andric                    const LangOptions &LangOpts,
158406c3fb27SDimitry Andric                    std::optional<Qualifiers> *QualifiersToAppend) {
158506c3fb27SDimitry Andric   QualType Ty = VD->getType();
158606c3fb27SDimitry Andric   QualType PteTy;
158706c3fb27SDimitry Andric 
158806c3fb27SDimitry Andric   assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
158906c3fb27SDimitry Andric          "Expecting a VarDecl of type of pointer to object type");
159006c3fb27SDimitry Andric   PteTy = Ty->getPointeeType();
15915f757f3fSDimitry Andric 
15925f757f3fSDimitry Andric   TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
15935f757f3fSDimitry Andric   TypeLoc PteTyLoc;
15945f757f3fSDimitry Andric 
15955f757f3fSDimitry Andric   // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
15965f757f3fSDimitry Andric   // the `TypeLoc` of the pointee type:
15975f757f3fSDimitry Andric   switch (TyLoc.getTypeLocClass()) {
15985f757f3fSDimitry Andric   case TypeLoc::ConstantArray:
15995f757f3fSDimitry Andric   case TypeLoc::IncompleteArray:
16005f757f3fSDimitry Andric   case TypeLoc::VariableArray:
16015f757f3fSDimitry Andric   case TypeLoc::DependentSizedArray:
16025f757f3fSDimitry Andric   case TypeLoc::Decayed:
16035f757f3fSDimitry Andric     assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
16045f757f3fSDimitry Andric                                    "pointer type unless it decays.");
16055f757f3fSDimitry Andric     PteTyLoc = TyLoc.getNextTypeLoc();
16065f757f3fSDimitry Andric     break;
16075f757f3fSDimitry Andric   case TypeLoc::Pointer:
16085f757f3fSDimitry Andric     PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
16095f757f3fSDimitry Andric     break;
16105f757f3fSDimitry Andric   default:
16115f757f3fSDimitry Andric     return std::nullopt;
16125f757f3fSDimitry Andric   }
16135f757f3fSDimitry Andric   if (PteTyLoc.isNull())
16145f757f3fSDimitry Andric     // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
16155f757f3fSDimitry Andric     // when the pointer type is `auto`.
161606c3fb27SDimitry Andric     return std::nullopt;
161706c3fb27SDimitry Andric 
16185f757f3fSDimitry Andric   SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD);
161906c3fb27SDimitry Andric 
16205f757f3fSDimitry Andric   if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
162106c3fb27SDimitry Andric     // We are expecting these locations to be valid. But in some cases, they are
162206c3fb27SDimitry Andric     // not all valid. It is a Clang bug to me and we are not responsible for
162306c3fb27SDimitry Andric     // fixing it.  So we will just give up for now when it happens.
162406c3fb27SDimitry Andric     return std::nullopt;
162506c3fb27SDimitry Andric   }
162606c3fb27SDimitry Andric 
162706c3fb27SDimitry Andric   // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
162806c3fb27SDimitry Andric   SourceLocation PteEndOfTokenLoc =
162906c3fb27SDimitry Andric       Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
163006c3fb27SDimitry Andric 
16315f757f3fSDimitry Andric   if (!PteEndOfTokenLoc.isValid())
16325f757f3fSDimitry Andric     // Sometimes we cannot get the end location of the pointee type, e.g., when
16335f757f3fSDimitry Andric     // there are macros involved.
16345f757f3fSDimitry Andric     return std::nullopt;
16355f757f3fSDimitry Andric   if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
163606c3fb27SDimitry Andric     // We only deal with the cases where the source text of the pointee type
163706c3fb27SDimitry Andric     // appears on the left-hand side of the variable identifier completely,
163806c3fb27SDimitry Andric     // including the following forms:
163906c3fb27SDimitry Andric     // `T ident`,
164006c3fb27SDimitry Andric     // `T ident[]`, where `T` is any type.
164106c3fb27SDimitry Andric     // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
164206c3fb27SDimitry Andric     return std::nullopt;
164306c3fb27SDimitry Andric   }
164406c3fb27SDimitry Andric   if (PteTy.hasQualifiers()) {
164506c3fb27SDimitry Andric     // TypeLoc does not provide source ranges for qualifiers (it says it's
164606c3fb27SDimitry Andric     // intentional but seems fishy to me), so we cannot get the full text
164706c3fb27SDimitry Andric     // `PteTy` via source ranges.
164806c3fb27SDimitry Andric     *QualifiersToAppend = PteTy.getQualifiers();
164906c3fb27SDimitry Andric   }
165006c3fb27SDimitry Andric   return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
165106c3fb27SDimitry Andric       ->str();
165206c3fb27SDimitry Andric }
165306c3fb27SDimitry Andric 
165406c3fb27SDimitry Andric // Returns the text of the name (with qualifiers) of a `FunctionDecl`.
getFunNameText(const FunctionDecl * FD,const SourceManager & SM,const LangOptions & LangOpts)165506c3fb27SDimitry Andric static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
165606c3fb27SDimitry Andric                                                const SourceManager &SM,
165706c3fb27SDimitry Andric                                                const LangOptions &LangOpts) {
165806c3fb27SDimitry Andric   SourceLocation BeginLoc = FD->getQualifier()
165906c3fb27SDimitry Andric                                 ? FD->getQualifierLoc().getBeginLoc()
166006c3fb27SDimitry Andric                                 : FD->getNameInfo().getBeginLoc();
166106c3fb27SDimitry Andric   // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
166206c3fb27SDimitry Andric   // last token:
166306c3fb27SDimitry Andric   SourceLocation EndLoc = Lexer::getLocForEndOfToken(
166406c3fb27SDimitry Andric       FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
166506c3fb27SDimitry Andric   SourceRange NameRange{BeginLoc, EndLoc};
166606c3fb27SDimitry Andric 
166706c3fb27SDimitry Andric   return getRangeText(NameRange, SM, LangOpts);
166806c3fb27SDimitry Andric }
166906c3fb27SDimitry Andric 
16705f757f3fSDimitry Andric // Returns the text representing a `std::span` type where the element type is
16715f757f3fSDimitry Andric // represented by `EltTyText`.
16725f757f3fSDimitry Andric //
16735f757f3fSDimitry Andric // Note the optional parameter `Qualifiers`: one needs to pass qualifiers
16745f757f3fSDimitry Andric // explicitly if the element type needs to be qualified.
16755f757f3fSDimitry Andric static std::string
getSpanTypeText(StringRef EltTyText,std::optional<Qualifiers> Quals=std::nullopt)16765f757f3fSDimitry Andric getSpanTypeText(StringRef EltTyText,
16775f757f3fSDimitry Andric                 std::optional<Qualifiers> Quals = std::nullopt) {
16785f757f3fSDimitry Andric   const char *const SpanOpen = "std::span<";
16795f757f3fSDimitry Andric 
16805f757f3fSDimitry Andric   if (Quals)
16815f757f3fSDimitry Andric     return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
16825f757f3fSDimitry Andric   return SpanOpen + EltTyText.str() + '>';
16835f757f3fSDimitry Andric }
16845f757f3fSDimitry Andric 
168506c3fb27SDimitry Andric std::optional<FixItList>
getFixits(const Strategy & s) const168606c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget::getFixits(const Strategy &s) const {
168706c3fb27SDimitry Andric   const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
168806c3fb27SDimitry Andric 
168906c3fb27SDimitry Andric   if (VD && s.lookup(VD) == Strategy::Kind::Span) {
169006c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
169106c3fb27SDimitry Andric     // std::span can't represent elements before its begin()
169206c3fb27SDimitry Andric     if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
169306c3fb27SDimitry Andric       if (ConstVal->isNegative())
169406c3fb27SDimitry Andric         return std::nullopt;
169506c3fb27SDimitry Andric 
169606c3fb27SDimitry Andric     // note that the expr may (oddly) has multiple layers of parens
169706c3fb27SDimitry Andric     // example:
169806c3fb27SDimitry Andric     //   *((..(pointer + 123)..))
169906c3fb27SDimitry Andric     // goal:
170006c3fb27SDimitry Andric     //   pointer[123]
170106c3fb27SDimitry Andric     // Fix-It:
170206c3fb27SDimitry Andric     //   remove '*('
170306c3fb27SDimitry Andric     //   replace ' + ' with '['
170406c3fb27SDimitry Andric     //   replace ')' with ']'
170506c3fb27SDimitry Andric 
170606c3fb27SDimitry Andric     // example:
170706c3fb27SDimitry Andric     //   *((..(123 + pointer)..))
170806c3fb27SDimitry Andric     // goal:
170906c3fb27SDimitry Andric     //   123[pointer]
171006c3fb27SDimitry Andric     // Fix-It:
171106c3fb27SDimitry Andric     //   remove '*('
171206c3fb27SDimitry Andric     //   replace ' + ' with '['
171306c3fb27SDimitry Andric     //   replace ')' with ']'
171406c3fb27SDimitry Andric 
171506c3fb27SDimitry Andric     const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
171606c3fb27SDimitry Andric     const SourceManager &SM = Ctx.getSourceManager();
171706c3fb27SDimitry Andric     const LangOptions &LangOpts = Ctx.getLangOpts();
171806c3fb27SDimitry Andric     CharSourceRange StarWithTrailWhitespace =
171906c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
172006c3fb27SDimitry Andric                                              LHS->getBeginLoc());
172106c3fb27SDimitry Andric 
172206c3fb27SDimitry Andric     std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
172306c3fb27SDimitry Andric     if (!LHSLocation)
172406c3fb27SDimitry Andric       return std::nullopt;
172506c3fb27SDimitry Andric 
172606c3fb27SDimitry Andric     CharSourceRange PlusWithSurroundingWhitespace =
172706c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
172806c3fb27SDimitry Andric 
172906c3fb27SDimitry Andric     std::optional<SourceLocation> AddOpLocation =
173006c3fb27SDimitry Andric         getPastLoc(AddOp, SM, LangOpts);
173106c3fb27SDimitry Andric     std::optional<SourceLocation> DerefOpLocation =
173206c3fb27SDimitry Andric         getPastLoc(DerefOp, SM, LangOpts);
173306c3fb27SDimitry Andric 
173406c3fb27SDimitry Andric     if (!AddOpLocation || !DerefOpLocation)
173506c3fb27SDimitry Andric       return std::nullopt;
173606c3fb27SDimitry Andric 
173706c3fb27SDimitry Andric     CharSourceRange ClosingParenWithPrecWhitespace =
173806c3fb27SDimitry Andric         clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
173906c3fb27SDimitry Andric 
174006c3fb27SDimitry Andric     return FixItList{
174106c3fb27SDimitry Andric         {FixItHint::CreateRemoval(StarWithTrailWhitespace),
174206c3fb27SDimitry Andric          FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
174306c3fb27SDimitry Andric          FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
174406c3fb27SDimitry Andric   }
174506c3fb27SDimitry Andric   return std::nullopt; // something wrong or unsupported, give up
174606c3fb27SDimitry Andric }
174706c3fb27SDimitry Andric 
174806c3fb27SDimitry Andric std::optional<FixItList>
getFixits(const Strategy & S) const174906c3fb27SDimitry Andric PointerDereferenceGadget::getFixits(const Strategy &S) const {
175006c3fb27SDimitry Andric   const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
175106c3fb27SDimitry Andric   switch (S.lookup(VD)) {
175206c3fb27SDimitry Andric   case Strategy::Kind::Span: {
175306c3fb27SDimitry Andric     ASTContext &Ctx = VD->getASTContext();
175406c3fb27SDimitry Andric     SourceManager &SM = Ctx.getSourceManager();
175506c3fb27SDimitry Andric     // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
175606c3fb27SDimitry Andric     // Deletes the *operand
175706c3fb27SDimitry Andric     CharSourceRange derefRange = clang::CharSourceRange::getCharRange(
175806c3fb27SDimitry Andric         Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
175906c3fb27SDimitry Andric     // Inserts the [0]
17605f757f3fSDimitry Andric     if (auto LocPastOperand =
17615f757f3fSDimitry Andric             getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
176206c3fb27SDimitry Andric       return FixItList{{FixItHint::CreateRemoval(derefRange),
17635f757f3fSDimitry Andric 			FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
176406c3fb27SDimitry Andric     }
17655f757f3fSDimitry Andric     break;
176606c3fb27SDimitry Andric   }
176706c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
176806c3fb27SDimitry Andric   case Strategy::Kind::Array:
176906c3fb27SDimitry Andric   case Strategy::Kind::Vector:
177006c3fb27SDimitry Andric     llvm_unreachable("Strategy not implemented yet!");
177106c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
177206c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
177306c3fb27SDimitry Andric   }
177406c3fb27SDimitry Andric 
177506c3fb27SDimitry Andric   return std::nullopt;
177606c3fb27SDimitry Andric }
177706c3fb27SDimitry Andric 
177806c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form UPC(DRE) with
177906c3fb27SDimitry Andric // `DRE.data()`
getFixits(const Strategy & S) const178006c3fb27SDimitry Andric std::optional<FixItList> UPCStandalonePointerGadget::getFixits(const Strategy &S)
178106c3fb27SDimitry Andric       const {
178206c3fb27SDimitry Andric   const auto VD = cast<VarDecl>(Node->getDecl());
178306c3fb27SDimitry Andric   switch (S.lookup(VD)) {
178406c3fb27SDimitry Andric     case Strategy::Kind::Span: {
178506c3fb27SDimitry Andric       ASTContext &Ctx = VD->getASTContext();
178606c3fb27SDimitry Andric       SourceManager &SM = Ctx.getSourceManager();
178706c3fb27SDimitry Andric       // Inserts the .data() after the DRE
178806c3fb27SDimitry Andric       std::optional<SourceLocation> EndOfOperand =
178906c3fb27SDimitry Andric           getPastLoc(Node, SM, Ctx.getLangOpts());
179006c3fb27SDimitry Andric 
179106c3fb27SDimitry Andric       if (EndOfOperand)
179206c3fb27SDimitry Andric         return FixItList{{FixItHint::CreateInsertion(
179306c3fb27SDimitry Andric             *EndOfOperand, ".data()")}};
17945f757f3fSDimitry Andric       // FIXME: Points inside a macro expansion.
17955f757f3fSDimitry Andric       break;
179606c3fb27SDimitry Andric     }
179706c3fb27SDimitry Andric     case Strategy::Kind::Wontfix:
179806c3fb27SDimitry Andric     case Strategy::Kind::Iterator:
179906c3fb27SDimitry Andric     case Strategy::Kind::Array:
180006c3fb27SDimitry Andric     case Strategy::Kind::Vector:
180106c3fb27SDimitry Andric       llvm_unreachable("unsupported strategies for FixableGadgets");
180206c3fb27SDimitry Andric   }
180306c3fb27SDimitry Andric 
180406c3fb27SDimitry Andric   return std::nullopt;
180506c3fb27SDimitry Andric }
180606c3fb27SDimitry Andric 
180706c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form `&DRE[e]` with
180806c3fb27SDimitry Andric // `&DRE.data()[e]`:
180906c3fb27SDimitry Andric static std::optional<FixItList>
fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator * Node)181006c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) {
181106c3fb27SDimitry Andric   const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
181206c3fb27SDimitry Andric   const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
181306c3fb27SDimitry Andric   // FIXME: this `getASTContext` call is costly, we should pass the
181406c3fb27SDimitry Andric   // ASTContext in:
181506c3fb27SDimitry Andric   const ASTContext &Ctx = DRE->getDecl()->getASTContext();
181606c3fb27SDimitry Andric   const Expr *Idx = ArraySub->getIdx();
181706c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
181806c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
181906c3fb27SDimitry Andric   std::stringstream SS;
182006c3fb27SDimitry Andric   bool IdxIsLitZero = false;
182106c3fb27SDimitry Andric 
182206c3fb27SDimitry Andric   if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
182306c3fb27SDimitry Andric     if ((*ICE).isZero())
182406c3fb27SDimitry Andric       IdxIsLitZero = true;
182506c3fb27SDimitry Andric   std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
182606c3fb27SDimitry Andric   if (!DreString)
182706c3fb27SDimitry Andric     return std::nullopt;
182806c3fb27SDimitry Andric 
182906c3fb27SDimitry Andric   if (IdxIsLitZero) {
183006c3fb27SDimitry Andric     // If the index is literal zero, we produce the most concise fix-it:
183106c3fb27SDimitry Andric     SS << (*DreString).str() << ".data()";
183206c3fb27SDimitry Andric   } else {
183306c3fb27SDimitry Andric     std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
183406c3fb27SDimitry Andric     if (!IndexString)
183506c3fb27SDimitry Andric       return std::nullopt;
183606c3fb27SDimitry Andric 
183706c3fb27SDimitry Andric     SS << "&" << (*DreString).str() << ".data()"
183806c3fb27SDimitry Andric        << "[" << (*IndexString).str() << "]";
183906c3fb27SDimitry Andric   }
184006c3fb27SDimitry Andric   return FixItList{
184106c3fb27SDimitry Andric       FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
184206c3fb27SDimitry Andric }
184306c3fb27SDimitry Andric 
18445f757f3fSDimitry Andric std::optional<FixItList>
getFixits(const Strategy & S) const18455f757f3fSDimitry Andric UUCAddAssignGadget::getFixits(const Strategy &S) const {
18465f757f3fSDimitry Andric   DeclUseList DREs = getClaimedVarUseSites();
18475f757f3fSDimitry Andric 
18485f757f3fSDimitry Andric   if (DREs.size() != 1)
18495f757f3fSDimitry Andric     return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
18505f757f3fSDimitry Andric                          // give up
18515f757f3fSDimitry Andric   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
18525f757f3fSDimitry Andric     if (S.lookup(VD) == Strategy::Kind::Span) {
18535f757f3fSDimitry Andric       FixItList Fixes;
18545f757f3fSDimitry Andric 
18555f757f3fSDimitry Andric       const Stmt *AddAssignNode = getBaseStmt();
18565f757f3fSDimitry Andric       StringRef varName = VD->getName();
18575f757f3fSDimitry Andric       const ASTContext &Ctx = VD->getASTContext();
18585f757f3fSDimitry Andric 
18595f757f3fSDimitry Andric       if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
18605f757f3fSDimitry Andric         return std::nullopt;
18615f757f3fSDimitry Andric 
18625f757f3fSDimitry Andric       // To transform UUC(p += n) to UUC(p = p.subspan(..)):
18635f757f3fSDimitry Andric       bool NotParenExpr =
18645f757f3fSDimitry Andric           (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
18655f757f3fSDimitry Andric       std::string SS = varName.str() + " = " + varName.str() + ".subspan";
18665f757f3fSDimitry Andric       if (NotParenExpr)
18675f757f3fSDimitry Andric         SS += "(";
18685f757f3fSDimitry Andric 
18695f757f3fSDimitry Andric       std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
18705f757f3fSDimitry Andric           AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
18715f757f3fSDimitry Andric       if (!AddAssignLocation)
18725f757f3fSDimitry Andric         return std::nullopt;
18735f757f3fSDimitry Andric 
18745f757f3fSDimitry Andric       Fixes.push_back(FixItHint::CreateReplacement(
18755f757f3fSDimitry Andric           SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
18765f757f3fSDimitry Andric           SS));
18775f757f3fSDimitry Andric       if (NotParenExpr)
18785f757f3fSDimitry Andric         Fixes.push_back(FixItHint::CreateInsertion(
18795f757f3fSDimitry Andric             Offset->getEndLoc().getLocWithOffset(1), ")"));
18805f757f3fSDimitry Andric       return Fixes;
18815f757f3fSDimitry Andric     }
18825f757f3fSDimitry Andric   }
18835f757f3fSDimitry Andric   return std::nullopt; // Not in the cases that we can handle for now, give up.
18845f757f3fSDimitry Andric }
188506c3fb27SDimitry Andric 
getFixits(const Strategy & S) const188606c3fb27SDimitry Andric std::optional<FixItList> UPCPreIncrementGadget::getFixits(const Strategy &S) const {
188706c3fb27SDimitry Andric   DeclUseList DREs = getClaimedVarUseSites();
188806c3fb27SDimitry Andric 
188906c3fb27SDimitry Andric   if (DREs.size() != 1)
189006c3fb27SDimitry Andric     return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
189106c3fb27SDimitry Andric                          // give up
189206c3fb27SDimitry Andric   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
189306c3fb27SDimitry Andric     if (S.lookup(VD) == Strategy::Kind::Span) {
189406c3fb27SDimitry Andric       FixItList Fixes;
189506c3fb27SDimitry Andric       std::stringstream SS;
189606c3fb27SDimitry Andric       const Stmt *PreIncNode = getBaseStmt();
189706c3fb27SDimitry Andric       StringRef varName = VD->getName();
189806c3fb27SDimitry Andric       const ASTContext &Ctx = VD->getASTContext();
189906c3fb27SDimitry Andric 
190006c3fb27SDimitry Andric       // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
190106c3fb27SDimitry Andric       SS << "(" << varName.data() << " = " << varName.data()
190206c3fb27SDimitry Andric          << ".subspan(1)).data()";
190306c3fb27SDimitry Andric       std::optional<SourceLocation> PreIncLocation =
190406c3fb27SDimitry Andric           getEndCharLoc(PreIncNode, Ctx.getSourceManager(), Ctx.getLangOpts());
190506c3fb27SDimitry Andric       if (!PreIncLocation)
190606c3fb27SDimitry Andric         return std::nullopt;
190706c3fb27SDimitry Andric 
190806c3fb27SDimitry Andric       Fixes.push_back(FixItHint::CreateReplacement(
190906c3fb27SDimitry Andric           SourceRange(PreIncNode->getBeginLoc(), *PreIncLocation), SS.str()));
191006c3fb27SDimitry Andric       return Fixes;
191106c3fb27SDimitry Andric     }
191206c3fb27SDimitry Andric   }
191306c3fb27SDimitry Andric   return std::nullopt; // Not in the cases that we can handle for now, give up.
191406c3fb27SDimitry Andric }
191506c3fb27SDimitry Andric 
191606c3fb27SDimitry Andric 
191706c3fb27SDimitry Andric // For a non-null initializer `Init` of `T *` type, this function returns
191806c3fb27SDimitry Andric // `FixItHint`s producing a list initializer `{Init,  S}` as a part of a fix-it
191906c3fb27SDimitry Andric // to output stream.
192006c3fb27SDimitry Andric // In many cases, this function cannot figure out the actual extent `S`.  It
192106c3fb27SDimitry Andric // then will use a place holder to replace `S` to ask users to fill `S` in.  The
192206c3fb27SDimitry Andric // initializer shall be used to initialize a variable of type `std::span<T>`.
192306c3fb27SDimitry Andric //
192406c3fb27SDimitry Andric // FIXME: Support multi-level pointers
192506c3fb27SDimitry Andric //
192606c3fb27SDimitry Andric // Parameters:
192706c3fb27SDimitry Andric //   `Init` a pointer to the initializer expression
192806c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
192906c3fb27SDimitry Andric static FixItList
FixVarInitializerWithSpan(const Expr * Init,ASTContext & Ctx,const StringRef UserFillPlaceHolder)19305f757f3fSDimitry Andric FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
193106c3fb27SDimitry Andric                                  const StringRef UserFillPlaceHolder) {
193206c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
193306c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
193406c3fb27SDimitry Andric 
193506c3fb27SDimitry Andric   // If `Init` has a constant value that is (or equivalent to) a
193606c3fb27SDimitry Andric   // NULL pointer, we use the default constructor to initialize the span
193706c3fb27SDimitry Andric   // object, i.e., a `std:span` variable declaration with no initializer.
193806c3fb27SDimitry Andric   // So the fix-it is just to remove the initializer.
193906c3fb27SDimitry Andric   if (Init->isNullPointerConstant(Ctx,
194006c3fb27SDimitry Andric           // FIXME: Why does this function not ask for `const ASTContext
194106c3fb27SDimitry Andric           // &`? It should. Maybe worth an NFC patch later.
194206c3fb27SDimitry Andric           Expr::NullPointerConstantValueDependence::
194306c3fb27SDimitry Andric               NPC_ValueDependentIsNotNull)) {
194406c3fb27SDimitry Andric     std::optional<SourceLocation> InitLocation =
194506c3fb27SDimitry Andric         getEndCharLoc(Init, SM, LangOpts);
194606c3fb27SDimitry Andric     if (!InitLocation)
194706c3fb27SDimitry Andric       return {};
194806c3fb27SDimitry Andric 
194906c3fb27SDimitry Andric     SourceRange SR(Init->getBeginLoc(), *InitLocation);
195006c3fb27SDimitry Andric 
195106c3fb27SDimitry Andric     return {FixItHint::CreateRemoval(SR)};
195206c3fb27SDimitry Andric   }
195306c3fb27SDimitry Andric 
195406c3fb27SDimitry Andric   FixItList FixIts{};
195506c3fb27SDimitry Andric   std::string ExtentText = UserFillPlaceHolder.data();
195606c3fb27SDimitry Andric   StringRef One = "1";
195706c3fb27SDimitry Andric 
195806c3fb27SDimitry Andric   // Insert `{` before `Init`:
195906c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
196006c3fb27SDimitry Andric   // Try to get the data extent. Break into different cases:
196106c3fb27SDimitry Andric   if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
196206c3fb27SDimitry Andric     // In cases `Init` is `new T[n]` and there is no explicit cast over
196306c3fb27SDimitry Andric     // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
196406c3fb27SDimitry Andric     // of `T`. So the extent is `n` unless `n` has side effects.  Similar but
196506c3fb27SDimitry Andric     // simpler for the case where `Init` is `new T`.
196606c3fb27SDimitry Andric     if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
196706c3fb27SDimitry Andric       if (!Ext->HasSideEffects(Ctx)) {
196806c3fb27SDimitry Andric         std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
196906c3fb27SDimitry Andric         if (!ExtentString)
197006c3fb27SDimitry Andric           return {};
197106c3fb27SDimitry Andric         ExtentText = *ExtentString;
197206c3fb27SDimitry Andric       }
197306c3fb27SDimitry Andric     } else if (!CxxNew->isArray())
197406c3fb27SDimitry Andric       // Although the initializer is not allocating a buffer, the pointer
197506c3fb27SDimitry Andric       // variable could still be used in buffer access operations.
197606c3fb27SDimitry Andric       ExtentText = One;
197706c3fb27SDimitry Andric   } else if (const auto *CArrTy = Ctx.getAsConstantArrayType(
197806c3fb27SDimitry Andric                  Init->IgnoreImpCasts()->getType())) {
197906c3fb27SDimitry Andric     // In cases `Init` is of an array type after stripping off implicit casts,
198006c3fb27SDimitry Andric     // the extent is the array size.  Note that if the array size is not a
198106c3fb27SDimitry Andric     // constant, we cannot use it as the extent.
198206c3fb27SDimitry Andric     ExtentText = getAPIntText(CArrTy->getSize());
198306c3fb27SDimitry Andric   } else {
198406c3fb27SDimitry Andric     // In cases `Init` is of the form `&Var` after stripping of implicit
198506c3fb27SDimitry Andric     // casts, where `&` is the built-in operator, the extent is 1.
198606c3fb27SDimitry Andric     if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
198706c3fb27SDimitry Andric       if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
198806c3fb27SDimitry Andric           isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
198906c3fb27SDimitry Andric         ExtentText = One;
199006c3fb27SDimitry Andric     // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
199106c3fb27SDimitry Andric     // and explicit casting, etc. etc.
199206c3fb27SDimitry Andric   }
199306c3fb27SDimitry Andric 
199406c3fb27SDimitry Andric   SmallString<32> StrBuffer{};
199506c3fb27SDimitry Andric   std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
199606c3fb27SDimitry Andric 
199706c3fb27SDimitry Andric   if (!LocPassInit)
199806c3fb27SDimitry Andric     return {};
199906c3fb27SDimitry Andric 
200006c3fb27SDimitry Andric   StrBuffer.append(", ");
200106c3fb27SDimitry Andric   StrBuffer.append(ExtentText);
200206c3fb27SDimitry Andric   StrBuffer.append("}");
200306c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
200406c3fb27SDimitry Andric   return FixIts;
200506c3fb27SDimitry Andric }
200606c3fb27SDimitry Andric 
20075f757f3fSDimitry Andric #ifndef NDEBUG
20085f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg)  \
20095f757f3fSDimitry Andric Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), "failed to produce fixit for declaration '" + (D)->getNameAsString() + "'" + (Msg))
20105f757f3fSDimitry Andric #else
20115f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg)
20125f757f3fSDimitry Andric #endif
20135f757f3fSDimitry Andric 
20145f757f3fSDimitry Andric // For the given variable declaration with a pointer-to-T type, returns the text
20155f757f3fSDimitry Andric // `std::span<T>`.  If it is unable to generate the text, returns
20165f757f3fSDimitry Andric // `std::nullopt`.
createSpanTypeForVarDecl(const VarDecl * VD,const ASTContext & Ctx)20175f757f3fSDimitry Andric static std::optional<std::string> createSpanTypeForVarDecl(const VarDecl *VD,
20185f757f3fSDimitry Andric                                                            const ASTContext &Ctx) {
20195f757f3fSDimitry Andric   assert(VD->getType()->isPointerType());
20205f757f3fSDimitry Andric 
20215f757f3fSDimitry Andric   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
20225f757f3fSDimitry Andric   std::optional<std::string> PteTyText = getPointeeTypeText(
20235f757f3fSDimitry Andric       VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
20245f757f3fSDimitry Andric 
20255f757f3fSDimitry Andric   if (!PteTyText)
20265f757f3fSDimitry Andric     return std::nullopt;
20275f757f3fSDimitry Andric 
20285f757f3fSDimitry Andric   std::string SpanTyText = "std::span<";
20295f757f3fSDimitry Andric 
20305f757f3fSDimitry Andric   SpanTyText.append(*PteTyText);
20315f757f3fSDimitry Andric   // Append qualifiers to span element type if any:
20325f757f3fSDimitry Andric   if (PteTyQualifiers) {
20335f757f3fSDimitry Andric     SpanTyText.append(" ");
20345f757f3fSDimitry Andric     SpanTyText.append(PteTyQualifiers->getAsString());
20355f757f3fSDimitry Andric   }
20365f757f3fSDimitry Andric   SpanTyText.append(">");
20375f757f3fSDimitry Andric   return SpanTyText;
20385f757f3fSDimitry Andric }
20395f757f3fSDimitry Andric 
204006c3fb27SDimitry Andric // For a `VarDecl` of the form `T  * var (= Init)?`, this
20415f757f3fSDimitry Andric // function generates fix-its that
20425f757f3fSDimitry Andric //  1) replace `T * var` with `std::span<T> var`; and
20435f757f3fSDimitry Andric //  2) change `Init` accordingly to a span constructor, if it exists.
204406c3fb27SDimitry Andric //
204506c3fb27SDimitry Andric // FIXME: support Multi-level pointers
204606c3fb27SDimitry Andric //
204706c3fb27SDimitry Andric // Parameters:
204806c3fb27SDimitry Andric //   `D` a pointer the variable declaration node
204906c3fb27SDimitry Andric //   `Ctx` a reference to the ASTContext
20505f757f3fSDimitry Andric //   `UserFillPlaceHolder` the user-input placeholder text
205106c3fb27SDimitry Andric // Returns:
20525f757f3fSDimitry Andric //    the non-empty fix-it list, if fix-its are successfuly generated; empty
20535f757f3fSDimitry Andric //    list otherwise.
fixLocalVarDeclWithSpan(const VarDecl * D,ASTContext & Ctx,const StringRef UserFillPlaceHolder,UnsafeBufferUsageHandler & Handler)20545f757f3fSDimitry Andric static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
20555f757f3fSDimitry Andric 					 const StringRef UserFillPlaceHolder,
20565f757f3fSDimitry Andric 					 UnsafeBufferUsageHandler &Handler) {
20575f757f3fSDimitry Andric   if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager()))
20585f757f3fSDimitry Andric     return {};
205906c3fb27SDimitry Andric 
206006c3fb27SDimitry Andric   FixItList FixIts{};
20615f757f3fSDimitry Andric   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
206206c3fb27SDimitry Andric 
20635f757f3fSDimitry Andric   if (!SpanTyText) {
20645f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
20655f757f3fSDimitry Andric     return {};
20665f757f3fSDimitry Andric   }
20675f757f3fSDimitry Andric 
20685f757f3fSDimitry Andric   // Will hold the text for `std::span<T> Ident`:
20695f757f3fSDimitry Andric   std::stringstream SS;
20705f757f3fSDimitry Andric 
20715f757f3fSDimitry Andric   SS << *SpanTyText;
20725f757f3fSDimitry Andric   // Append qualifiers to the type of `D`, if any:
20735f757f3fSDimitry Andric   if (D->getType().hasQualifiers())
20745f757f3fSDimitry Andric     SS << " " << D->getType().getQualifiers().getAsString();
20755f757f3fSDimitry Andric 
20765f757f3fSDimitry Andric   // The end of the range of the original source that will be replaced
20775f757f3fSDimitry Andric   // by `std::span<T> ident`:
20785f757f3fSDimitry Andric   SourceLocation EndLocForReplacement = D->getEndLoc();
20795f757f3fSDimitry Andric   std::optional<StringRef> IdentText =
20805f757f3fSDimitry Andric       getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
20815f757f3fSDimitry Andric 
20825f757f3fSDimitry Andric   if (!IdentText) {
20835f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
20845f757f3fSDimitry Andric     return {};
20855f757f3fSDimitry Andric   }
20865f757f3fSDimitry Andric   // Fix the initializer if it exists:
208706c3fb27SDimitry Andric   if (const Expr *Init = D->getInit()) {
208806c3fb27SDimitry Andric     FixItList InitFixIts =
20895f757f3fSDimitry Andric         FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
209006c3fb27SDimitry Andric     if (InitFixIts.empty())
209106c3fb27SDimitry Andric       return {};
209206c3fb27SDimitry Andric     FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts.begin()),
209306c3fb27SDimitry Andric                   std::make_move_iterator(InitFixIts.end()));
20945f757f3fSDimitry Andric     // If the declaration has the form `T *ident = init`, we want to replace
20955f757f3fSDimitry Andric     // `T *ident = ` with `std::span<T> ident`:
20965f757f3fSDimitry Andric     EndLocForReplacement = Init->getBeginLoc().getLocWithOffset(-1);
20975f757f3fSDimitry Andric   }
20985f757f3fSDimitry Andric   SS << " " << IdentText->str();
20995f757f3fSDimitry Andric   if (!EndLocForReplacement.isValid()) {
21005f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
210106c3fb27SDimitry Andric     return {};
21025f757f3fSDimitry Andric   }
210306c3fb27SDimitry Andric   FixIts.push_back(FixItHint::CreateReplacement(
21045f757f3fSDimitry Andric       SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
210506c3fb27SDimitry Andric   return FixIts;
210606c3fb27SDimitry Andric }
210706c3fb27SDimitry Andric 
hasConflictingOverload(const FunctionDecl * FD)210806c3fb27SDimitry Andric static bool hasConflictingOverload(const FunctionDecl *FD) {
210906c3fb27SDimitry Andric   return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
211006c3fb27SDimitry Andric }
211106c3fb27SDimitry Andric 
21125f757f3fSDimitry Andric // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
21135f757f3fSDimitry Andric // types, this function produces fix-its to make the change self-contained.  Let
21145f757f3fSDimitry Andric // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
21155f757f3fSDimitry Andric // entity defined by the `FunctionDecl` after the change to the parameters.
21165f757f3fSDimitry Andric // Fix-its produced by this function are
211706c3fb27SDimitry Andric //   1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
211806c3fb27SDimitry Andric //   of 'F';
211906c3fb27SDimitry Andric //   2. Create a declaration of "NewF" next to each declaration of `F`;
212006c3fb27SDimitry Andric //   3. Create a definition of "F" (as its' original definition is now belongs
212106c3fb27SDimitry Andric //      to "NewF") next to its original definition.  The body of the creating
212206c3fb27SDimitry Andric //      definition calls to "NewF".
212306c3fb27SDimitry Andric //
212406c3fb27SDimitry Andric // Example:
212506c3fb27SDimitry Andric //
212606c3fb27SDimitry Andric // void f(int *p);  // original declaration
212706c3fb27SDimitry Andric // void f(int *p) { // original definition
212806c3fb27SDimitry Andric //    p[5];
212906c3fb27SDimitry Andric // }
213006c3fb27SDimitry Andric //
213106c3fb27SDimitry Andric // To change the parameter `p` to be of `std::span<int>` type, we
213206c3fb27SDimitry Andric // also add overloads:
213306c3fb27SDimitry Andric //
213406c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
213506c3fb27SDimitry Andric // void f(std::span<int> p);                      // added overload decl
213606c3fb27SDimitry Andric // void f(std::span<int> p) {     // original def where param is changed
213706c3fb27SDimitry Andric //    p[5];
213806c3fb27SDimitry Andric // }
213906c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p) {  // added def
214006c3fb27SDimitry Andric //   return f(std::span(p, <# size #>));
214106c3fb27SDimitry Andric // }
214206c3fb27SDimitry Andric //
214306c3fb27SDimitry Andric static std::optional<FixItList>
createOverloadsForFixedParams(const Strategy & S,const FunctionDecl * FD,const ASTContext & Ctx,UnsafeBufferUsageHandler & Handler)21445f757f3fSDimitry Andric createOverloadsForFixedParams(const Strategy &S, const FunctionDecl *FD,
21455f757f3fSDimitry Andric                               const ASTContext &Ctx,
214606c3fb27SDimitry Andric                               UnsafeBufferUsageHandler &Handler) {
214706c3fb27SDimitry Andric   // FIXME: need to make this conflict checking better:
214806c3fb27SDimitry Andric   if (hasConflictingOverload(FD))
214906c3fb27SDimitry Andric     return std::nullopt;
215006c3fb27SDimitry Andric 
215106c3fb27SDimitry Andric   const SourceManager &SM = Ctx.getSourceManager();
215206c3fb27SDimitry Andric   const LangOptions &LangOpts = Ctx.getLangOpts();
21535f757f3fSDimitry Andric   const unsigned NumParms = FD->getNumParams();
21545f757f3fSDimitry Andric   std::vector<std::string> NewTysTexts(NumParms);
21555f757f3fSDimitry Andric   std::vector<bool> ParmsMask(NumParms, false);
21565f757f3fSDimitry Andric   bool AtLeastOneParmToFix = false;
21575f757f3fSDimitry Andric 
21585f757f3fSDimitry Andric   for (unsigned i = 0; i < NumParms; i++) {
21595f757f3fSDimitry Andric     const ParmVarDecl *PVD = FD->getParamDecl(i);
21605f757f3fSDimitry Andric 
21615f757f3fSDimitry Andric     if (S.lookup(PVD) == Strategy::Kind::Wontfix)
21625f757f3fSDimitry Andric       continue;
21635f757f3fSDimitry Andric     if (S.lookup(PVD) != Strategy::Kind::Span)
21645f757f3fSDimitry Andric       // Not supported, not suppose to happen:
21655f757f3fSDimitry Andric       return std::nullopt;
21665f757f3fSDimitry Andric 
21675f757f3fSDimitry Andric     std::optional<Qualifiers> PteTyQuals = std::nullopt;
21685f757f3fSDimitry Andric     std::optional<std::string> PteTyText =
21695f757f3fSDimitry Andric         getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
21705f757f3fSDimitry Andric 
21715f757f3fSDimitry Andric     if (!PteTyText)
21725f757f3fSDimitry Andric       // something wrong in obtaining the text of the pointee type, give up
21735f757f3fSDimitry Andric       return std::nullopt;
21745f757f3fSDimitry Andric     // FIXME: whether we should create std::span type depends on the Strategy.
21755f757f3fSDimitry Andric     NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
21765f757f3fSDimitry Andric     ParmsMask[i] = true;
21775f757f3fSDimitry Andric     AtLeastOneParmToFix = true;
21785f757f3fSDimitry Andric   }
21795f757f3fSDimitry Andric   if (!AtLeastOneParmToFix)
21805f757f3fSDimitry Andric     // No need to create function overloads:
21815f757f3fSDimitry Andric     return {};
218206c3fb27SDimitry Andric   // FIXME Respect indentation of the original code.
218306c3fb27SDimitry Andric 
218406c3fb27SDimitry Andric   // A lambda that creates the text representation of a function declaration
21855f757f3fSDimitry Andric   // with the new type signatures:
218606c3fb27SDimitry Andric   const auto NewOverloadSignatureCreator =
21875f757f3fSDimitry Andric       [&SM, &LangOpts, &NewTysTexts,
21885f757f3fSDimitry Andric        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
218906c3fb27SDimitry Andric     std::stringstream SS;
219006c3fb27SDimitry Andric 
219106c3fb27SDimitry Andric     SS << ";";
219206c3fb27SDimitry Andric     SS << getEndOfLine().str();
219306c3fb27SDimitry Andric     // Append: ret-type func-name "("
219406c3fb27SDimitry Andric     if (auto Prefix = getRangeText(
219506c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
219606c3fb27SDimitry Andric             SM, LangOpts))
219706c3fb27SDimitry Andric       SS << Prefix->str();
219806c3fb27SDimitry Andric     else
219906c3fb27SDimitry Andric       return std::nullopt; // give up
220006c3fb27SDimitry Andric     // Append: parameter-type-list
220106c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
220206c3fb27SDimitry Andric 
220306c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
220406c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
220506c3fb27SDimitry Andric 
220606c3fb27SDimitry Andric       if (Parm->isImplicit())
220706c3fb27SDimitry Andric         continue;
22085f757f3fSDimitry Andric       if (ParmsMask[i]) {
22095f757f3fSDimitry Andric         // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
22105f757f3fSDimitry Andric         // new type:
22115f757f3fSDimitry Andric         SS << NewTysTexts[i];
221206c3fb27SDimitry Andric         // print parameter name if provided:
221306c3fb27SDimitry Andric         if (IdentifierInfo *II = Parm->getIdentifier())
22145f757f3fSDimitry Andric           SS << ' ' << II->getName().str();
22155f757f3fSDimitry Andric       } else if (auto ParmTypeText = getRangeText(
22165f757f3fSDimitry Andric                      getSourceRangeToTokenEnd(Parm, SM, LangOpts),
22175f757f3fSDimitry Andric                      SM, LangOpts)) {
221806c3fb27SDimitry Andric         // print the whole `Parm` without modification:
221906c3fb27SDimitry Andric         SS << ParmTypeText->str();
222006c3fb27SDimitry Andric       } else
222106c3fb27SDimitry Andric         return std::nullopt; // something wrong, give up
222206c3fb27SDimitry Andric       if (i != NumParms - 1)
222306c3fb27SDimitry Andric         SS << ", ";
222406c3fb27SDimitry Andric     }
222506c3fb27SDimitry Andric     SS << ")";
222606c3fb27SDimitry Andric     return SS.str();
222706c3fb27SDimitry Andric   };
222806c3fb27SDimitry Andric 
222906c3fb27SDimitry Andric   // A lambda that creates the text representation of a function definition with
223006c3fb27SDimitry Andric   // the original signature:
223106c3fb27SDimitry Andric   const auto OldOverloadDefCreator =
22325f757f3fSDimitry Andric       [&Handler, &SM, &LangOpts, &NewTysTexts,
22335f757f3fSDimitry Andric        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
223406c3fb27SDimitry Andric     std::stringstream SS;
223506c3fb27SDimitry Andric 
223606c3fb27SDimitry Andric     SS << getEndOfLine().str();
223706c3fb27SDimitry Andric     // Append: attr-name ret-type func-name "(" param-list ")" "{"
223806c3fb27SDimitry Andric     if (auto FDPrefix = getRangeText(
223906c3fb27SDimitry Andric             SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
224006c3fb27SDimitry Andric             LangOpts))
224106c3fb27SDimitry Andric       SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
224206c3fb27SDimitry Andric          << FDPrefix->str() << "{";
224306c3fb27SDimitry Andric     else
224406c3fb27SDimitry Andric       return std::nullopt;
224506c3fb27SDimitry Andric     // Append: "return" func-name "("
224606c3fb27SDimitry Andric     if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
224706c3fb27SDimitry Andric       SS << "return " << FunQualName->str() << "(";
224806c3fb27SDimitry Andric     else
224906c3fb27SDimitry Andric       return std::nullopt;
225006c3fb27SDimitry Andric 
225106c3fb27SDimitry Andric     // Append: arg-list
225206c3fb27SDimitry Andric     const unsigned NumParms = FD->getNumParams();
225306c3fb27SDimitry Andric     for (unsigned i = 0; i < NumParms; i++) {
225406c3fb27SDimitry Andric       const ParmVarDecl *Parm = FD->getParamDecl(i);
225506c3fb27SDimitry Andric 
225606c3fb27SDimitry Andric       if (Parm->isImplicit())
225706c3fb27SDimitry Andric         continue;
225806c3fb27SDimitry Andric       // FIXME: If a parameter has no name, it is unused in the
225906c3fb27SDimitry Andric       // definition. So we could just leave it as it is.
226006c3fb27SDimitry Andric       if (!Parm->getIdentifier())
226106c3fb27SDimitry Andric         // If a parameter of a function definition has no name:
226206c3fb27SDimitry Andric         return std::nullopt;
22635f757f3fSDimitry Andric       if (ParmsMask[i])
226406c3fb27SDimitry Andric         // This is our spanified paramter!
22655f757f3fSDimitry Andric         SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
22665f757f3fSDimitry Andric            << ", " << getUserFillPlaceHolder("size") << ")";
226706c3fb27SDimitry Andric       else
226806c3fb27SDimitry Andric         SS << Parm->getIdentifier()->getName().str();
226906c3fb27SDimitry Andric       if (i != NumParms - 1)
227006c3fb27SDimitry Andric         SS << ", ";
227106c3fb27SDimitry Andric     }
227206c3fb27SDimitry Andric     // finish call and the body
227306c3fb27SDimitry Andric     SS << ");}" << getEndOfLine().str();
227406c3fb27SDimitry Andric     // FIXME: 80-char line formatting?
227506c3fb27SDimitry Andric     return SS.str();
227606c3fb27SDimitry Andric   };
227706c3fb27SDimitry Andric 
227806c3fb27SDimitry Andric   FixItList FixIts{};
227906c3fb27SDimitry Andric   for (FunctionDecl *FReDecl : FD->redecls()) {
228006c3fb27SDimitry Andric     std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
228106c3fb27SDimitry Andric 
228206c3fb27SDimitry Andric     if (!Loc)
228306c3fb27SDimitry Andric       return {};
228406c3fb27SDimitry Andric     if (FReDecl->isThisDeclarationADefinition()) {
228506c3fb27SDimitry Andric       assert(FReDecl == FD && "inconsistent function definition");
228606c3fb27SDimitry Andric       // Inserts a definition with the old signature to the end of
228706c3fb27SDimitry Andric       // `FReDecl`:
22885f757f3fSDimitry Andric       if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
228906c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
229006c3fb27SDimitry Andric       else
229106c3fb27SDimitry Andric         return {}; // give up
229206c3fb27SDimitry Andric     } else {
229306c3fb27SDimitry Andric       // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
229406c3fb27SDimitry Andric       if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
229506c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(
229606c3fb27SDimitry Andric             FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
229706c3fb27SDimitry Andric                                         FReDecl->getBeginLoc(), " ")));
229806c3fb27SDimitry Andric       }
229906c3fb27SDimitry Andric       // Inserts a declaration with the new signature to the end of `FReDecl`:
23005f757f3fSDimitry Andric       if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
230106c3fb27SDimitry Andric         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
230206c3fb27SDimitry Andric       else
230306c3fb27SDimitry Andric         return {};
230406c3fb27SDimitry Andric     }
230506c3fb27SDimitry Andric   }
230606c3fb27SDimitry Andric   return FixIts;
230706c3fb27SDimitry Andric }
230806c3fb27SDimitry Andric 
23095f757f3fSDimitry Andric // To fix a `ParmVarDecl` to be of `std::span` type.
fixParamWithSpan(const ParmVarDecl * PVD,const ASTContext & Ctx,UnsafeBufferUsageHandler & Handler)231006c3fb27SDimitry Andric static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
231106c3fb27SDimitry Andric                                   UnsafeBufferUsageHandler &Handler) {
23125f757f3fSDimitry Andric   if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) {
23135f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
23145f757f3fSDimitry Andric     return {};
23155f757f3fSDimitry Andric   }
23165f757f3fSDimitry Andric   if (PVD->hasDefaultArg()) {
231706c3fb27SDimitry Andric     // FIXME: generate fix-its for default values:
23185f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
231906c3fb27SDimitry Andric     return {};
23205f757f3fSDimitry Andric   }
232106c3fb27SDimitry Andric 
232206c3fb27SDimitry Andric   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
232306c3fb27SDimitry Andric   std::optional<std::string> PteTyText = getPointeeTypeText(
232406c3fb27SDimitry Andric       PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
232506c3fb27SDimitry Andric 
23265f757f3fSDimitry Andric   if (!PteTyText) {
23275f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
232806c3fb27SDimitry Andric     return {};
23295f757f3fSDimitry Andric   }
233006c3fb27SDimitry Andric 
233106c3fb27SDimitry Andric   std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
233206c3fb27SDimitry Andric 
23335f757f3fSDimitry Andric   if (!PVDNameText) {
23345f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
233506c3fb27SDimitry Andric     return {};
23365f757f3fSDimitry Andric   }
233706c3fb27SDimitry Andric 
233806c3fb27SDimitry Andric   std::stringstream SS;
23395f757f3fSDimitry Andric   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
234006c3fb27SDimitry Andric 
234106c3fb27SDimitry Andric   if (PteTyQualifiers)
23425f757f3fSDimitry Andric     // Append qualifiers if they exist:
23435f757f3fSDimitry Andric     SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
23445f757f3fSDimitry Andric   else
23455f757f3fSDimitry Andric     SS << getSpanTypeText(*PteTyText);
234606c3fb27SDimitry Andric   // Append qualifiers to the type of the parameter:
234706c3fb27SDimitry Andric   if (PVD->getType().hasQualifiers())
23485f757f3fSDimitry Andric     SS << ' ' << PVD->getType().getQualifiers().getAsString();
234906c3fb27SDimitry Andric   // Append parameter's name:
23505f757f3fSDimitry Andric   SS << ' ' << PVDNameText->str();
23515f757f3fSDimitry Andric   // Add replacement fix-it:
23525f757f3fSDimitry Andric   return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
235306c3fb27SDimitry Andric }
235406c3fb27SDimitry Andric 
fixVariableWithSpan(const VarDecl * VD,const DeclUseTracker & Tracker,ASTContext & Ctx,UnsafeBufferUsageHandler & Handler)235506c3fb27SDimitry Andric static FixItList fixVariableWithSpan(const VarDecl *VD,
235606c3fb27SDimitry Andric                                      const DeclUseTracker &Tracker,
235706c3fb27SDimitry Andric                                      ASTContext &Ctx,
235806c3fb27SDimitry Andric                                      UnsafeBufferUsageHandler &Handler) {
235906c3fb27SDimitry Andric   const DeclStmt *DS = Tracker.lookupDecl(VD);
23605f757f3fSDimitry Andric   if (!DS) {
23615f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : variables declared this way not implemented yet");
23625f757f3fSDimitry Andric     return {};
23635f757f3fSDimitry Andric   }
236406c3fb27SDimitry Andric   if (!DS->isSingleDecl()) {
236506c3fb27SDimitry Andric     // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
23665f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
236706c3fb27SDimitry Andric     return {};
236806c3fb27SDimitry Andric   }
236906c3fb27SDimitry Andric   // Currently DS is an unused variable but we'll need it when
237006c3fb27SDimitry Andric   // non-single decls are implemented, where the pointee type name
237106c3fb27SDimitry Andric   // and the '*' are spread around the place.
237206c3fb27SDimitry Andric   (void)DS;
237306c3fb27SDimitry Andric 
237406c3fb27SDimitry Andric   // FIXME: handle cases where DS has multiple declarations
23755f757f3fSDimitry Andric   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
237606c3fb27SDimitry Andric }
237706c3fb27SDimitry Andric 
237806c3fb27SDimitry Andric // TODO: we should be consistent to use `std::nullopt` to represent no-fix due
237906c3fb27SDimitry Andric // to any unexpected problem.
238006c3fb27SDimitry Andric static FixItList
fixVariable(const VarDecl * VD,Strategy::Kind K,const Decl * D,const DeclUseTracker & Tracker,ASTContext & Ctx,UnsafeBufferUsageHandler & Handler)238106c3fb27SDimitry Andric fixVariable(const VarDecl *VD, Strategy::Kind K,
238206c3fb27SDimitry Andric             /* The function decl under analysis */ const Decl *D,
238306c3fb27SDimitry Andric             const DeclUseTracker &Tracker, ASTContext &Ctx,
238406c3fb27SDimitry Andric             UnsafeBufferUsageHandler &Handler) {
238506c3fb27SDimitry Andric   if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
238606c3fb27SDimitry Andric     auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
23875f757f3fSDimitry Andric     if (!FD || FD != D) {
238806c3fb27SDimitry Andric       // `FD != D` means that `PVD` belongs to a function that is not being
238906c3fb27SDimitry Andric       // analyzed currently.  Thus `FD` may not be complete.
23905f757f3fSDimitry Andric       DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
239106c3fb27SDimitry Andric       return {};
23925f757f3fSDimitry Andric     }
239306c3fb27SDimitry Andric 
239406c3fb27SDimitry Andric     // TODO If function has a try block we can't change params unless we check
239506c3fb27SDimitry Andric     // also its catch block for their use.
239606c3fb27SDimitry Andric     // FIXME We might support static class methods, some select methods,
239706c3fb27SDimitry Andric     // operators and possibly lamdas.
239806c3fb27SDimitry Andric     if (FD->isMain() || FD->isConstexpr() ||
239906c3fb27SDimitry Andric         FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
240006c3fb27SDimitry Andric         FD->isVariadic() ||
240106c3fb27SDimitry Andric         // also covers call-operator of lamdas
240206c3fb27SDimitry Andric         isa<CXXMethodDecl>(FD) ||
240306c3fb27SDimitry Andric         // skip when the function body is a try-block
240406c3fb27SDimitry Andric         (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
24055f757f3fSDimitry Andric         FD->isOverloadedOperator()) {
24065f757f3fSDimitry Andric       DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
240706c3fb27SDimitry Andric       return {}; // TODO test all these cases
240806c3fb27SDimitry Andric     }
24095f757f3fSDimitry Andric   }
241006c3fb27SDimitry Andric 
241106c3fb27SDimitry Andric   switch (K) {
241206c3fb27SDimitry Andric   case Strategy::Kind::Span: {
241306c3fb27SDimitry Andric     if (VD->getType()->isPointerType()) {
241406c3fb27SDimitry Andric       if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
241506c3fb27SDimitry Andric         return fixParamWithSpan(PVD, Ctx, Handler);
241606c3fb27SDimitry Andric 
241706c3fb27SDimitry Andric       if (VD->isLocalVarDecl())
241806c3fb27SDimitry Andric         return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
241906c3fb27SDimitry Andric     }
24205f757f3fSDimitry Andric     DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
242106c3fb27SDimitry Andric     return {};
242206c3fb27SDimitry Andric   }
242306c3fb27SDimitry Andric   case Strategy::Kind::Iterator:
242406c3fb27SDimitry Andric   case Strategy::Kind::Array:
242506c3fb27SDimitry Andric   case Strategy::Kind::Vector:
242606c3fb27SDimitry Andric     llvm_unreachable("Strategy not implemented yet!");
242706c3fb27SDimitry Andric   case Strategy::Kind::Wontfix:
242806c3fb27SDimitry Andric     llvm_unreachable("Invalid strategy!");
242906c3fb27SDimitry Andric   }
243006c3fb27SDimitry Andric   llvm_unreachable("Unknown strategy!");
243106c3fb27SDimitry Andric }
243206c3fb27SDimitry Andric 
243306c3fb27SDimitry Andric // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
243406c3fb27SDimitry Andric // `RemoveRange` of 'h' overlaps with a macro use.
overlapWithMacro(const FixItList & FixIts)243506c3fb27SDimitry Andric static bool overlapWithMacro(const FixItList &FixIts) {
243606c3fb27SDimitry Andric   // FIXME: For now we only check if the range (or the first token) is (part of)
243706c3fb27SDimitry Andric   // a macro expansion.  Ideally, we want to check for all tokens in the range.
243806c3fb27SDimitry Andric   return llvm::any_of(FixIts, [](const FixItHint &Hint) {
243906c3fb27SDimitry Andric     auto Range = Hint.RemoveRange;
244006c3fb27SDimitry Andric     if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
244106c3fb27SDimitry Andric       // If the range (or the first token) is (part of) a macro expansion:
244206c3fb27SDimitry Andric       return true;
244306c3fb27SDimitry Andric     return false;
244406c3fb27SDimitry Andric   });
244506c3fb27SDimitry Andric }
244606c3fb27SDimitry Andric 
24475f757f3fSDimitry Andric // Returns true iff `VD` is a parameter of the declaration `D`:
isParameterOf(const VarDecl * VD,const Decl * D)24485f757f3fSDimitry Andric static bool isParameterOf(const VarDecl *VD, const Decl *D) {
24495f757f3fSDimitry Andric   return isa<ParmVarDecl>(VD) &&
24505f757f3fSDimitry Andric          VD->getDeclContext() == dyn_cast<DeclContext>(D);
245106c3fb27SDimitry Andric }
245206c3fb27SDimitry Andric 
24535f757f3fSDimitry Andric // Erases variables in `FixItsForVariable`, if such a variable has an unfixable
24545f757f3fSDimitry Andric // group mate.  A variable `v` is unfixable iff `FixItsForVariable` does not
24555f757f3fSDimitry Andric // contain `v`.
eraseVarsForUnfixableGroupMates(std::map<const VarDecl *,FixItList> & FixItsForVariable,const VariableGroupsManager & VarGrpMgr)24565f757f3fSDimitry Andric static void eraseVarsForUnfixableGroupMates(
24575f757f3fSDimitry Andric     std::map<const VarDecl *, FixItList> &FixItsForVariable,
24585f757f3fSDimitry Andric     const VariableGroupsManager &VarGrpMgr) {
24595f757f3fSDimitry Andric   // Variables will be removed from `FixItsForVariable`:
24605f757f3fSDimitry Andric   SmallVector<const VarDecl *, 8> ToErase;
24615f757f3fSDimitry Andric 
24625f757f3fSDimitry Andric   for (const auto &[VD, Ignore] : FixItsForVariable) {
24635f757f3fSDimitry Andric     VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
24645f757f3fSDimitry Andric     if (llvm::any_of(Grp,
24655f757f3fSDimitry Andric                      [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
24665f757f3fSDimitry Andric                        return !FixItsForVariable.count(GrpMember);
24675f757f3fSDimitry Andric                      })) {
24685f757f3fSDimitry Andric       // At least one group member cannot be fixed, so we have to erase the
24695f757f3fSDimitry Andric       // whole group:
24705f757f3fSDimitry Andric       for (const VarDecl *Member : Grp)
24715f757f3fSDimitry Andric         ToErase.push_back(Member);
24725f757f3fSDimitry Andric     }
24735f757f3fSDimitry Andric   }
24745f757f3fSDimitry Andric   for (auto *VarToErase : ToErase)
24755f757f3fSDimitry Andric     FixItsForVariable.erase(VarToErase);
24765f757f3fSDimitry Andric }
24775f757f3fSDimitry Andric 
24785f757f3fSDimitry Andric // Returns the fix-its that create bounds-safe function overloads for the
24795f757f3fSDimitry Andric // function `D`, if `D`'s parameters will be changed to safe-types through
24805f757f3fSDimitry Andric // fix-its in `FixItsForVariable`.
24815f757f3fSDimitry Andric //
24825f757f3fSDimitry Andric // NOTE: In case `D`'s parameters will be changed but bounds-safe function
24835f757f3fSDimitry Andric // overloads cannot created, the whole group that contains the parameters will
24845f757f3fSDimitry Andric // be erased from `FixItsForVariable`.
createFunctionOverloadsForParms(std::map<const VarDecl *,FixItList> & FixItsForVariable,const VariableGroupsManager & VarGrpMgr,const FunctionDecl * FD,const Strategy & S,ASTContext & Ctx,UnsafeBufferUsageHandler & Handler)24855f757f3fSDimitry Andric static FixItList createFunctionOverloadsForParms(
24865f757f3fSDimitry Andric     std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
24875f757f3fSDimitry Andric     const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
24885f757f3fSDimitry Andric     const Strategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler) {
24895f757f3fSDimitry Andric   FixItList FixItsSharedByParms{};
24905f757f3fSDimitry Andric 
24915f757f3fSDimitry Andric   std::optional<FixItList> OverloadFixes =
24925f757f3fSDimitry Andric       createOverloadsForFixedParams(S, FD, Ctx, Handler);
24935f757f3fSDimitry Andric 
24945f757f3fSDimitry Andric   if (OverloadFixes) {
24955f757f3fSDimitry Andric     FixItsSharedByParms.append(*OverloadFixes);
24965f757f3fSDimitry Andric   } else {
24975f757f3fSDimitry Andric     // Something wrong in generating `OverloadFixes`, need to remove the
24985f757f3fSDimitry Andric     // whole group, where parameters are in, from `FixItsForVariable` (Note
24995f757f3fSDimitry Andric     // that all parameters should be in the same group):
25005f757f3fSDimitry Andric     for (auto *Member : VarGrpMgr.getGroupOfParms())
25015f757f3fSDimitry Andric       FixItsForVariable.erase(Member);
25025f757f3fSDimitry Andric   }
25035f757f3fSDimitry Andric   return FixItsSharedByParms;
25045f757f3fSDimitry Andric }
25055f757f3fSDimitry Andric 
25065f757f3fSDimitry Andric // Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
2507bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList>
getFixIts(FixableGadgetSets & FixablesForAllVars,const Strategy & S,ASTContext & Ctx,const Decl * D,const DeclUseTracker & Tracker,UnsafeBufferUsageHandler & Handler,const VariableGroupsManager & VarGrpMgr)250806c3fb27SDimitry Andric getFixIts(FixableGadgetSets &FixablesForAllVars, const Strategy &S,
250906c3fb27SDimitry Andric 	  ASTContext &Ctx,
251006c3fb27SDimitry Andric           /* The function decl under analysis */ const Decl *D,
251106c3fb27SDimitry Andric           const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
25125f757f3fSDimitry Andric           const VariableGroupsManager &VarGrpMgr) {
25135f757f3fSDimitry Andric   // `FixItsForVariable` will map each variable to a set of fix-its directly
25145f757f3fSDimitry Andric   // associated to the variable itself.  Fix-its of distinct variables in
25155f757f3fSDimitry Andric   // `FixItsForVariable` are disjoint.
2516bdd1243dSDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariable;
25175f757f3fSDimitry Andric 
25185f757f3fSDimitry Andric   // Populate `FixItsForVariable` with fix-its directly associated with each
25195f757f3fSDimitry Andric   // variable.  Fix-its directly associated to a variable 'v' are the ones
25205f757f3fSDimitry Andric   // produced by the `FixableGadget`s whose claimed variable is 'v'.
252106c3fb27SDimitry Andric   for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
252206c3fb27SDimitry Andric     FixItsForVariable[VD] =
252306c3fb27SDimitry Andric         fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
252406c3fb27SDimitry Andric     // If we fail to produce Fix-It for the declaration we have to skip the
252506c3fb27SDimitry Andric     // variable entirely.
252606c3fb27SDimitry Andric     if (FixItsForVariable[VD].empty()) {
252706c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
252806c3fb27SDimitry Andric       continue;
252906c3fb27SDimitry Andric     }
2530bdd1243dSDimitry Andric     for (const auto &F : Fixables) {
253106c3fb27SDimitry Andric       std::optional<FixItList> Fixits = F->getFixits(S);
253206c3fb27SDimitry Andric 
25335f757f3fSDimitry Andric       if (Fixits) {
2534bdd1243dSDimitry Andric         FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
25355f757f3fSDimitry Andric                                      Fixits->begin(), Fixits->end());
25365f757f3fSDimitry Andric         continue;
25375f757f3fSDimitry Andric       }
25385f757f3fSDimitry Andric #ifndef NDEBUG
25395f757f3fSDimitry Andric       Handler.addDebugNoteForVar(
25405f757f3fSDimitry Andric           VD, F->getBaseStmt()->getBeginLoc(),
25415f757f3fSDimitry Andric           ("gadget '" + F->getDebugName() + "' refused to produce a fix")
25425f757f3fSDimitry Andric               .str());
25435f757f3fSDimitry Andric #endif
254406c3fb27SDimitry Andric       FixItsForVariable.erase(VD);
25455f757f3fSDimitry Andric       break;
25465f757f3fSDimitry Andric     }
25475f757f3fSDimitry Andric   }
25485f757f3fSDimitry Andric 
25495f757f3fSDimitry Andric   // `FixItsForVariable` now contains only variables that can be
25505f757f3fSDimitry Andric   // fixed. A variable can be fixed if its' declaration and all Fixables
25515f757f3fSDimitry Andric   // associated to it can all be fixed.
25525f757f3fSDimitry Andric 
25535f757f3fSDimitry Andric   // To further remove from `FixItsForVariable` variables whose group mates
25545f757f3fSDimitry Andric   // cannot be fixed...
25555f757f3fSDimitry Andric   eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
25565f757f3fSDimitry Andric   // Now `FixItsForVariable` gets further reduced: a variable is in
25575f757f3fSDimitry Andric   // `FixItsForVariable` iff it can be fixed and all its group mates can be
25585f757f3fSDimitry Andric   // fixed.
25595f757f3fSDimitry Andric 
25605f757f3fSDimitry Andric   // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
25615f757f3fSDimitry Andric   // That is,  when fixing multiple parameters in one step,  these fix-its will
25625f757f3fSDimitry Andric   // be applied only once (instead of being applied per parameter).
25635f757f3fSDimitry Andric   FixItList FixItsSharedByParms{};
25645f757f3fSDimitry Andric 
25655f757f3fSDimitry Andric   if (auto *FD = dyn_cast<FunctionDecl>(D))
25665f757f3fSDimitry Andric     FixItsSharedByParms = createFunctionOverloadsForParms(
25675f757f3fSDimitry Andric         FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
25685f757f3fSDimitry Andric 
25695f757f3fSDimitry Andric   // The map that maps each variable `v` to fix-its for the whole group where
25705f757f3fSDimitry Andric   // `v` is in:
25715f757f3fSDimitry Andric   std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
25725f757f3fSDimitry Andric       FixItsForVariable};
25735f757f3fSDimitry Andric 
25745f757f3fSDimitry Andric   for (auto &[Var, Ignore] : FixItsForVariable) {
25755f757f3fSDimitry Andric     bool AnyParm = false;
25765f757f3fSDimitry Andric     const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
25775f757f3fSDimitry Andric 
25785f757f3fSDimitry Andric     for (const VarDecl *GrpMate : VarGroupForVD) {
25795f757f3fSDimitry Andric       if (Var == GrpMate)
258006c3fb27SDimitry Andric         continue;
25815f757f3fSDimitry Andric       if (FixItsForVariable.count(GrpMate))
25825f757f3fSDimitry Andric         FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
258306c3fb27SDimitry Andric     }
25845f757f3fSDimitry Andric     if (AnyParm) {
25855f757f3fSDimitry Andric       // This assertion should never fail.  Otherwise we have a bug.
25865f757f3fSDimitry Andric       assert(!FixItsSharedByParms.empty() &&
25875f757f3fSDimitry Andric              "Should not try to fix a parameter that does not belong to a "
25885f757f3fSDimitry Andric              "FunctionDecl");
25895f757f3fSDimitry Andric       FinalFixItsForVariable[Var].append(FixItsSharedByParms);
25905f757f3fSDimitry Andric     }
25915f757f3fSDimitry Andric   }
25925f757f3fSDimitry Andric   // Fix-its that will be applied in one step shall NOT:
25935f757f3fSDimitry Andric   // 1. overlap with macros or/and templates; or
25945f757f3fSDimitry Andric   // 2. conflict with each other.
25955f757f3fSDimitry Andric   // Otherwise, the fix-its will be dropped.
25965f757f3fSDimitry Andric   for (auto Iter = FinalFixItsForVariable.begin();
25975f757f3fSDimitry Andric        Iter != FinalFixItsForVariable.end();)
25985f757f3fSDimitry Andric     if (overlapWithMacro(Iter->second) ||
25995f757f3fSDimitry Andric         clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
26005f757f3fSDimitry Andric       Iter = FinalFixItsForVariable.erase(Iter);
26015f757f3fSDimitry Andric     } else
26025f757f3fSDimitry Andric       Iter++;
26035f757f3fSDimitry Andric   return FinalFixItsForVariable;
260406c3fb27SDimitry Andric }
260506c3fb27SDimitry Andric 
26065f757f3fSDimitry Andric template <typename VarDeclIterTy>
2607bdd1243dSDimitry Andric static Strategy
getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars)26085f757f3fSDimitry Andric getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
2609bdd1243dSDimitry Andric   Strategy S;
2610bdd1243dSDimitry Andric   for (const VarDecl *VD : UnsafeVars) {
2611bdd1243dSDimitry Andric     S.set(VD, Strategy::Kind::Span);
2612bdd1243dSDimitry Andric   }
2613bdd1243dSDimitry Andric   return S;
2614bdd1243dSDimitry Andric }
2615bdd1243dSDimitry Andric 
26165f757f3fSDimitry Andric //  Manages variable groups:
26175f757f3fSDimitry Andric class VariableGroupsManagerImpl : public VariableGroupsManager {
26185f757f3fSDimitry Andric   const std::vector<VarGrpTy> Groups;
26195f757f3fSDimitry Andric   const std::map<const VarDecl *, unsigned> &VarGrpMap;
26205f757f3fSDimitry Andric   const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
26215f757f3fSDimitry Andric 
26225f757f3fSDimitry Andric public:
VariableGroupsManagerImpl(const std::vector<VarGrpTy> & Groups,const std::map<const VarDecl *,unsigned> & VarGrpMap,const llvm::SetVector<const VarDecl * > & GrpsUnionForParms)26235f757f3fSDimitry Andric   VariableGroupsManagerImpl(
26245f757f3fSDimitry Andric       const std::vector<VarGrpTy> &Groups,
26255f757f3fSDimitry Andric       const std::map<const VarDecl *, unsigned> &VarGrpMap,
26265f757f3fSDimitry Andric       const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
26275f757f3fSDimitry Andric       : Groups(Groups), VarGrpMap(VarGrpMap),
26285f757f3fSDimitry Andric         GrpsUnionForParms(GrpsUnionForParms) {}
26295f757f3fSDimitry Andric 
getGroupOfVar(const VarDecl * Var,bool * HasParm) const26305f757f3fSDimitry Andric   VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
26315f757f3fSDimitry Andric     if (GrpsUnionForParms.contains(Var)) {
26325f757f3fSDimitry Andric       if (HasParm)
26335f757f3fSDimitry Andric         *HasParm = true;
26345f757f3fSDimitry Andric       return GrpsUnionForParms.getArrayRef();
26355f757f3fSDimitry Andric     }
26365f757f3fSDimitry Andric     if (HasParm)
26375f757f3fSDimitry Andric       *HasParm = false;
26385f757f3fSDimitry Andric 
26395f757f3fSDimitry Andric     auto It = VarGrpMap.find(Var);
26405f757f3fSDimitry Andric 
26415f757f3fSDimitry Andric     if (It == VarGrpMap.end())
26425f757f3fSDimitry Andric       return std::nullopt;
26435f757f3fSDimitry Andric     return Groups[It->second];
26445f757f3fSDimitry Andric   }
26455f757f3fSDimitry Andric 
getGroupOfParms() const26465f757f3fSDimitry Andric   VarGrpRef getGroupOfParms() const override {
26475f757f3fSDimitry Andric     return GrpsUnionForParms.getArrayRef();
26485f757f3fSDimitry Andric   }
26495f757f3fSDimitry Andric };
26505f757f3fSDimitry Andric 
checkUnsafeBufferUsage(const Decl * D,UnsafeBufferUsageHandler & Handler,bool EmitSuggestions)2651bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D,
265206c3fb27SDimitry Andric                                    UnsafeBufferUsageHandler &Handler,
265306c3fb27SDimitry Andric                                    bool EmitSuggestions) {
26545f757f3fSDimitry Andric #ifndef NDEBUG
26555f757f3fSDimitry Andric   Handler.clearDebugNotes();
26565f757f3fSDimitry Andric #endif
2657bdd1243dSDimitry Andric 
26585f757f3fSDimitry Andric   assert(D && D->getBody());
265906c3fb27SDimitry Andric   // We do not want to visit a Lambda expression defined inside a method independently.
266006c3fb27SDimitry Andric   // Instead, it should be visited along with the outer method.
26615f757f3fSDimitry Andric   // FIXME: do we want to do the same thing for `BlockDecl`s?
266206c3fb27SDimitry Andric   if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
266306c3fb27SDimitry Andric     if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
266406c3fb27SDimitry Andric       return;
2665bdd1243dSDimitry Andric   }
2666bdd1243dSDimitry Andric 
266706c3fb27SDimitry Andric   // Do not emit fixit suggestions for functions declared in an
266806c3fb27SDimitry Andric   // extern "C" block.
266906c3fb27SDimitry Andric   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
267006c3fb27SDimitry Andric       for (FunctionDecl *FReDecl : FD->redecls()) {
267106c3fb27SDimitry Andric       if (FReDecl->isExternC()) {
267206c3fb27SDimitry Andric         EmitSuggestions = false;
267306c3fb27SDimitry Andric         break;
267406c3fb27SDimitry Andric       }
267506c3fb27SDimitry Andric     }
267606c3fb27SDimitry Andric   }
267706c3fb27SDimitry Andric 
267806c3fb27SDimitry Andric   WarningGadgetSets UnsafeOps;
267906c3fb27SDimitry Andric   FixableGadgetSets FixablesForAllVars;
268006c3fb27SDimitry Andric 
268106c3fb27SDimitry Andric   auto [FixableGadgets, WarningGadgets, Tracker] =
268206c3fb27SDimitry Andric     findGadgets(D, Handler, EmitSuggestions);
268306c3fb27SDimitry Andric 
268406c3fb27SDimitry Andric   if (!EmitSuggestions) {
268506c3fb27SDimitry Andric     // Our job is very easy without suggestions. Just warn about
268606c3fb27SDimitry Andric     // every problematic operation and consider it done. No need to deal
268706c3fb27SDimitry Andric     // with fixable gadgets, no need to group operations by variable.
268806c3fb27SDimitry Andric     for (const auto &G : WarningGadgets) {
26891db9f3b2SDimitry Andric       Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false,
26901db9f3b2SDimitry Andric                                     D->getASTContext());
269106c3fb27SDimitry Andric     }
269206c3fb27SDimitry Andric 
269306c3fb27SDimitry Andric     // This return guarantees that most of the machine doesn't run when
269406c3fb27SDimitry Andric     // suggestions aren't requested.
269506c3fb27SDimitry Andric     assert(FixableGadgets.size() == 0 &&
269606c3fb27SDimitry Andric            "Fixable gadgets found but suggestions not requested!");
269706c3fb27SDimitry Andric     return;
269806c3fb27SDimitry Andric   }
269906c3fb27SDimitry Andric 
27005f757f3fSDimitry Andric   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
27015f757f3fSDimitry Andric   //  function under the analysis. No need to fix any Fixables.
27025f757f3fSDimitry Andric   if (!WarningGadgets.empty()) {
27035f757f3fSDimitry Andric     // Gadgets "claim" variables they're responsible for. Once this loop
27045f757f3fSDimitry Andric     // finishes, the tracker will only track DREs that weren't claimed by any
27055f757f3fSDimitry Andric     // gadgets, i.e. not understood by the analysis.
27065f757f3fSDimitry Andric     for (const auto &G : FixableGadgets) {
27075f757f3fSDimitry Andric       for (const auto *DRE : G->getClaimedVarUseSites()) {
27085f757f3fSDimitry Andric         Tracker.claimUse(DRE);
27095f757f3fSDimitry Andric       }
27105f757f3fSDimitry Andric     }
27115f757f3fSDimitry Andric   }
27125f757f3fSDimitry Andric 
27135f757f3fSDimitry Andric   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
27145f757f3fSDimitry Andric   // function under the analysis.  Thus, it early returns here as there is
27155f757f3fSDimitry Andric   // nothing needs to be fixed.
27165f757f3fSDimitry Andric   //
27175f757f3fSDimitry Andric   // Note this claim is based on the assumption that there is no unsafe
27185f757f3fSDimitry Andric   // variable whose declaration is invisible from the analyzing function.
27195f757f3fSDimitry Andric   // Otherwise, we need to consider if the uses of those unsafe varuables needs
27205f757f3fSDimitry Andric   // fix.
27215f757f3fSDimitry Andric   // So far, we are not fixing any global variables or class members. And,
27225f757f3fSDimitry Andric   // lambdas will be analyzed along with the enclosing function. So this early
27235f757f3fSDimitry Andric   // return is correct for now.
27245f757f3fSDimitry Andric   if (WarningGadgets.empty())
27255f757f3fSDimitry Andric     return;
27265f757f3fSDimitry Andric 
272706c3fb27SDimitry Andric   UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
272806c3fb27SDimitry Andric   FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
272906c3fb27SDimitry Andric 
273006c3fb27SDimitry Andric   std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
273106c3fb27SDimitry Andric 
2732bdd1243dSDimitry Andric   // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
273306c3fb27SDimitry Andric   for (auto it = FixablesForAllVars.byVar.cbegin();
273406c3fb27SDimitry Andric        it != FixablesForAllVars.byVar.cend();) {
273506c3fb27SDimitry Andric       // FIXME: need to deal with global variables later
27365f757f3fSDimitry Andric       if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
27375f757f3fSDimitry Andric #ifndef NDEBUG
27385f757f3fSDimitry Andric           Handler.addDebugNoteForVar(
27395f757f3fSDimitry Andric               it->first, it->first->getBeginLoc(),
27405f757f3fSDimitry Andric               ("failed to produce fixit for '" + it->first->getNameAsString() +
27415f757f3fSDimitry Andric                "' : neither local nor a parameter"));
27425f757f3fSDimitry Andric #endif
27435f757f3fSDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
27445f757f3fSDimitry Andric       } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
27455f757f3fSDimitry Andric #ifndef NDEBUG
27465f757f3fSDimitry Andric         Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
27475f757f3fSDimitry Andric                                    ("failed to produce fixit for '" +
27485f757f3fSDimitry Andric                                     it->first->getNameAsString() +
27495f757f3fSDimitry Andric                                     "' : has a reference type"));
27505f757f3fSDimitry Andric #endif
27515f757f3fSDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
27525f757f3fSDimitry Andric       } else if (Tracker.hasUnclaimedUses(it->first)) {
27535f757f3fSDimitry Andric #ifndef NDEBUG
27545f757f3fSDimitry Andric         auto AllUnclaimed = Tracker.getUnclaimedUses(it->first);
27555f757f3fSDimitry Andric         for (auto UnclaimedDRE : AllUnclaimed) {
27565f757f3fSDimitry Andric         std::string UnclaimedUseTrace =
27575f757f3fSDimitry Andric             getDREAncestorString(UnclaimedDRE, D->getASTContext());
27585f757f3fSDimitry Andric 
27595f757f3fSDimitry Andric         Handler.addDebugNoteForVar(
27605f757f3fSDimitry Andric             it->first, UnclaimedDRE->getBeginLoc(),
27615f757f3fSDimitry Andric             ("failed to produce fixit for '" + it->first->getNameAsString() +
27625f757f3fSDimitry Andric              "' : has an unclaimed use\nThe unclaimed DRE trace: " +
27635f757f3fSDimitry Andric              UnclaimedUseTrace));
27645f757f3fSDimitry Andric         }
27655f757f3fSDimitry Andric #endif
27665f757f3fSDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
27675f757f3fSDimitry Andric       } else if (it->first->isInitCapture()) {
27685f757f3fSDimitry Andric #ifndef NDEBUG
27695f757f3fSDimitry Andric         Handler.addDebugNoteForVar(
27705f757f3fSDimitry Andric             it->first, it->first->getBeginLoc(),
27715f757f3fSDimitry Andric                                    ("failed to produce fixit for '" + it->first->getNameAsString() +
27725f757f3fSDimitry Andric                                     "' : init capture"));
27735f757f3fSDimitry Andric #endif
277406c3fb27SDimitry Andric         it = FixablesForAllVars.byVar.erase(it);
2775bdd1243dSDimitry Andric       }else {
2776bdd1243dSDimitry Andric       ++it;
2777bdd1243dSDimitry Andric     }
2778bdd1243dSDimitry Andric   }
2779bdd1243dSDimitry Andric 
278006c3fb27SDimitry Andric   // Fixpoint iteration for pointer assignments
27815f757f3fSDimitry Andric   using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
278206c3fb27SDimitry Andric   DepMapTy DependenciesMap{};
278306c3fb27SDimitry Andric   DepMapTy PtrAssignmentGraph{};
278406c3fb27SDimitry Andric 
278506c3fb27SDimitry Andric   for (auto it : FixablesForAllVars.byVar) {
278606c3fb27SDimitry Andric     for (const FixableGadget *fixable : it.second) {
278706c3fb27SDimitry Andric       std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
278806c3fb27SDimitry Andric                                   fixable->getStrategyImplications();
278906c3fb27SDimitry Andric       if (ImplPair) {
27905f757f3fSDimitry Andric         std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
279106c3fb27SDimitry Andric         PtrAssignmentGraph[Impl.first].insert(Impl.second);
279206c3fb27SDimitry Andric       }
279306c3fb27SDimitry Andric     }
279406c3fb27SDimitry Andric   }
279506c3fb27SDimitry Andric 
279606c3fb27SDimitry Andric   /*
279706c3fb27SDimitry Andric    The following code does a BFS traversal of the `PtrAssignmentGraph`
279806c3fb27SDimitry Andric    considering all unsafe vars as starting nodes and constructs an undirected
279906c3fb27SDimitry Andric    graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
280006c3fb27SDimitry Andric    elimiates all variables that are unreachable from any unsafe var. In other
280106c3fb27SDimitry Andric    words, this removes all dependencies that don't include any unsafe variable
280206c3fb27SDimitry Andric    and consequently don't need any fixit generation.
280306c3fb27SDimitry Andric    Note: A careful reader would observe that the code traverses
280406c3fb27SDimitry Andric    `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
280506c3fb27SDimitry Andric    `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
280606c3fb27SDimitry Andric    achieve the same result but the one used here dramatically cuts the
280706c3fb27SDimitry Andric    amount of hoops the second part of the algorithm needs to jump, given that
280806c3fb27SDimitry Andric    a lot of these connections become "direct". The reader is advised not to
280906c3fb27SDimitry Andric    imagine how the graph is transformed because of using `Var` instead of
281006c3fb27SDimitry Andric    `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
281106c3fb27SDimitry Andric    and think about why it's equivalent later.
281206c3fb27SDimitry Andric    */
281306c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVarsDirected{};
281406c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
281506c3fb27SDimitry Andric     if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
281606c3fb27SDimitry Andric 
281706c3fb27SDimitry Andric       std::queue<const VarDecl*> QueueDirected{};
281806c3fb27SDimitry Andric       QueueDirected.push(Var);
281906c3fb27SDimitry Andric       while(!QueueDirected.empty()) {
282006c3fb27SDimitry Andric         const VarDecl* CurrentVar = QueueDirected.front();
282106c3fb27SDimitry Andric         QueueDirected.pop();
282206c3fb27SDimitry Andric         VisitedVarsDirected.insert(CurrentVar);
282306c3fb27SDimitry Andric         auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
282406c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
282506c3fb27SDimitry Andric           if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
282606c3fb27SDimitry Andric             QueueDirected.push(Adj);
282706c3fb27SDimitry Andric           }
282806c3fb27SDimitry Andric           DependenciesMap[Var].insert(Adj);
282906c3fb27SDimitry Andric           DependenciesMap[Adj].insert(Var);
283006c3fb27SDimitry Andric         }
283106c3fb27SDimitry Andric       }
283206c3fb27SDimitry Andric     }
283306c3fb27SDimitry Andric   }
283406c3fb27SDimitry Andric 
28355f757f3fSDimitry Andric   // `Groups` stores the set of Connected Components in the graph.
28365f757f3fSDimitry Andric   std::vector<VarGrpTy> Groups;
28375f757f3fSDimitry Andric   // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
28385f757f3fSDimitry Andric   // variables belong to.  Group indexes refer to the elements in `Groups`.
28395f757f3fSDimitry Andric   // `VarGrpMap` is complete in that every variable that needs fix is in it.
28405f757f3fSDimitry Andric   std::map<const VarDecl *, unsigned> VarGrpMap;
28415f757f3fSDimitry Andric   // The union group over the ones in "Groups" that contain parameters of `D`:
28425f757f3fSDimitry Andric   llvm::SetVector<const VarDecl *>
28435f757f3fSDimitry Andric       GrpsUnionForParms; // these variables need to be fixed in one step
28445f757f3fSDimitry Andric 
284506c3fb27SDimitry Andric   // Group Connected Components for Unsafe Vars
284606c3fb27SDimitry Andric   // (Dependencies based on pointer assignments)
284706c3fb27SDimitry Andric   std::set<const VarDecl *> VisitedVars{};
284806c3fb27SDimitry Andric   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
284906c3fb27SDimitry Andric     if (VisitedVars.find(Var) == VisitedVars.end()) {
28505f757f3fSDimitry Andric       VarGrpTy &VarGroup = Groups.emplace_back();
285106c3fb27SDimitry Andric       std::queue<const VarDecl*> Queue{};
28525f757f3fSDimitry Andric 
285306c3fb27SDimitry Andric       Queue.push(Var);
285406c3fb27SDimitry Andric       while(!Queue.empty()) {
285506c3fb27SDimitry Andric         const VarDecl* CurrentVar = Queue.front();
285606c3fb27SDimitry Andric         Queue.pop();
285706c3fb27SDimitry Andric         VisitedVars.insert(CurrentVar);
285806c3fb27SDimitry Andric         VarGroup.push_back(CurrentVar);
285906c3fb27SDimitry Andric         auto AdjacentNodes = DependenciesMap[CurrentVar];
286006c3fb27SDimitry Andric         for (const VarDecl *Adj : AdjacentNodes) {
286106c3fb27SDimitry Andric           if (VisitedVars.find(Adj) == VisitedVars.end()) {
286206c3fb27SDimitry Andric             Queue.push(Adj);
286306c3fb27SDimitry Andric           }
286406c3fb27SDimitry Andric         }
286506c3fb27SDimitry Andric       }
28665f757f3fSDimitry Andric 
28675f757f3fSDimitry Andric       bool HasParm = false;
28685f757f3fSDimitry Andric       unsigned GrpIdx = Groups.size() - 1;
28695f757f3fSDimitry Andric 
287006c3fb27SDimitry Andric       for (const VarDecl *V : VarGroup) {
28715f757f3fSDimitry Andric         VarGrpMap[V] = GrpIdx;
28725f757f3fSDimitry Andric         if (!HasParm && isParameterOf(V, D))
28735f757f3fSDimitry Andric           HasParm = true;
287406c3fb27SDimitry Andric       }
28755f757f3fSDimitry Andric       if (HasParm)
28765f757f3fSDimitry Andric         GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
287706c3fb27SDimitry Andric     }
287806c3fb27SDimitry Andric   }
287906c3fb27SDimitry Andric 
28805f757f3fSDimitry Andric   // Remove a `FixableGadget` if the associated variable is not in the graph
28815f757f3fSDimitry Andric   // computed above.  We do not want to generate fix-its for such variables,
28825f757f3fSDimitry Andric   // since they are neither warned nor reachable from a warned one.
28835f757f3fSDimitry Andric   //
28845f757f3fSDimitry Andric   // Note a variable is not warned if it is not directly used in any unsafe
28855f757f3fSDimitry Andric   // operation. A variable `v` is NOT reachable from an unsafe variable, if it
28865f757f3fSDimitry Andric   // does not exist another variable `u` such that `u` is warned and fixing `u`
28875f757f3fSDimitry Andric   // (transitively) implicates fixing `v`.
28885f757f3fSDimitry Andric   //
28895f757f3fSDimitry Andric   // For example,
28905f757f3fSDimitry Andric   // ```
28915f757f3fSDimitry Andric   // void f(int * p) {
28925f757f3fSDimitry Andric   //   int * a = p; *p = 0;
28935f757f3fSDimitry Andric   // }
28945f757f3fSDimitry Andric   // ```
28955f757f3fSDimitry Andric   // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
28965f757f3fSDimitry Andric   // warned nor reachable from a warned one.  If we add `a[5] = 0` to the end of
28975f757f3fSDimitry Andric   // the function above, `p` becomes reachable from a warned variable.
28985f757f3fSDimitry Andric   for (auto I = FixablesForAllVars.byVar.begin();
28995f757f3fSDimitry Andric        I != FixablesForAllVars.byVar.end();) {
29005f757f3fSDimitry Andric     // Note `VisitedVars` contain all the variables in the graph:
29015f757f3fSDimitry Andric     if (!VisitedVars.count((*I).first)) {
29025f757f3fSDimitry Andric       // no such var in graph:
29035f757f3fSDimitry Andric       I = FixablesForAllVars.byVar.erase(I);
29045f757f3fSDimitry Andric     } else
29055f757f3fSDimitry Andric       ++I;
29065f757f3fSDimitry Andric   }
290706c3fb27SDimitry Andric 
29085f757f3fSDimitry Andric   // We assign strategies to variables that are 1) in the graph and 2) can be
29095f757f3fSDimitry Andric   // fixed. Other variables have the default "Won't fix" strategy.
29105f757f3fSDimitry Andric   Strategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
29115f757f3fSDimitry Andric       VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
29125f757f3fSDimitry Andric         // If a warned variable has no "Fixable", it is considered unfixable:
29135f757f3fSDimitry Andric         return FixablesForAllVars.byVar.count(V);
29145f757f3fSDimitry Andric       }));
29155f757f3fSDimitry Andric   VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
29165f757f3fSDimitry Andric 
29175f757f3fSDimitry Andric   if (isa<NamedDecl>(D))
29185f757f3fSDimitry Andric     // The only case where `D` is not a `NamedDecl` is when `D` is a
29195f757f3fSDimitry Andric     // `BlockDecl`. Let's not fix variables in blocks for now
292006c3fb27SDimitry Andric     FixItsForVariableGroup =
292106c3fb27SDimitry Andric         getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
29225f757f3fSDimitry Andric                   Tracker, Handler, VarGrpMgr);
2923bdd1243dSDimitry Andric 
2924bdd1243dSDimitry Andric   for (const auto &G : UnsafeOps.noVar) {
29251db9f3b2SDimitry Andric     Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false,
29261db9f3b2SDimitry Andric                                   D->getASTContext());
2927bdd1243dSDimitry Andric   }
2928bdd1243dSDimitry Andric 
2929bdd1243dSDimitry Andric   for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
293006c3fb27SDimitry Andric     auto FixItsIt = FixItsForVariableGroup.find(VD);
29315f757f3fSDimitry Andric     Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
293206c3fb27SDimitry Andric                                       FixItsIt != FixItsForVariableGroup.end()
2933bdd1243dSDimitry Andric                                       ? std::move(FixItsIt->second)
29345f757f3fSDimitry Andric                                       : FixItList{},
29355f757f3fSDimitry Andric                                       D);
2936bdd1243dSDimitry Andric     for (const auto &G : WarningGadgets) {
29371db9f3b2SDimitry Andric       Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true,
29381db9f3b2SDimitry Andric                                     D->getASTContext());
2939bdd1243dSDimitry Andric     }
2940bdd1243dSDimitry Andric   }
2941bdd1243dSDimitry Andric }
2942