xref: /llvm-project/clang/lib/Analysis/UnsafeBufferUsage.cpp (revision 2a8c12b29f8dc777a62868512bed1a2dae1ef8b2)
13b7af279SArtem Dergachev //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===//
23b7af279SArtem Dergachev //
33b7af279SArtem Dergachev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43b7af279SArtem Dergachev // See https://llvm.org/LICENSE.txt for license information.
53b7af279SArtem Dergachev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63b7af279SArtem Dergachev //
73b7af279SArtem Dergachev //===----------------------------------------------------------------------===//
83b7af279SArtem Dergachev 
93b7af279SArtem Dergachev #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
106fce42f8Sjkorous-apple #include "clang/AST/ASTContext.h"
116a0f2e53Sziqingluo-90 #include "clang/AST/Decl.h"
12dde802b1SSirraide #include "clang/AST/DynamicRecursiveASTVisitor.h"
13a4323586Sziqingluo-90 #include "clang/AST/Expr.h"
14d7dd2c46Sziqingluo-90 #include "clang/AST/FormatString.h"
156fce42f8Sjkorous-apple #include "clang/AST/Stmt.h"
16a4323586Sziqingluo-90 #include "clang/AST/StmtVisitor.h"
17de88d7dbSziqingluo-90 #include "clang/AST/Type.h"
18214312efSJan Korous #include "clang/ASTMatchers/ASTMatchFinder.h"
196fce42f8Sjkorous-apple #include "clang/ASTMatchers/ASTMatchers.h"
20644ac2a0Sjkorous-apple #include "clang/Basic/SourceLocation.h"
21bdf4f2beSZiqing Luo #include "clang/Lex/Lexer.h"
22148dc8a2Sziqingluo-90 #include "clang/Lex/Preprocessor.h"
239816863dSZiqing Luo #include "llvm/ADT/APSInt.h"
243b7af279SArtem Dergachev #include "llvm/ADT/SmallVector.h"
25644ac2a0Sjkorous-apple #include "llvm/ADT/StringRef.h"
26644ac2a0Sjkorous-apple #include "llvm/Support/Casting.h"
27214312efSJan Korous #include <memory>
28a1580d7bSKazu Hirata #include <optional>
29171dfc54SRashmi Mudduluru #include <queue>
309816863dSZiqing Luo #include <sstream>
313b7af279SArtem Dergachev 
323b7af279SArtem Dergachev using namespace llvm;
333b7af279SArtem Dergachev using namespace clang;
343b7af279SArtem Dergachev using namespace ast_matchers;
353b7af279SArtem Dergachev 
36a4323586Sziqingluo-90 #ifndef NDEBUG
37a4323586Sziqingluo-90 namespace {
38a4323586Sziqingluo-90 class StmtDebugPrinter
39a4323586Sziqingluo-90     : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
40a4323586Sziqingluo-90 public:
41a4323586Sziqingluo-90   std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
42a4323586Sziqingluo-90 
43a4323586Sziqingluo-90   std::string VisitBinaryOperator(const BinaryOperator *BO) {
44a4323586Sziqingluo-90     return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
45a4323586Sziqingluo-90   }
46a4323586Sziqingluo-90 
47a4323586Sziqingluo-90   std::string VisitUnaryOperator(const UnaryOperator *UO) {
48a4323586Sziqingluo-90     return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
49a4323586Sziqingluo-90   }
50a4323586Sziqingluo-90 
51a4323586Sziqingluo-90   std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
52a4323586Sziqingluo-90     return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
53a4323586Sziqingluo-90   }
54a4323586Sziqingluo-90 };
55a4323586Sziqingluo-90 
56a4323586Sziqingluo-90 // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
57a4323586Sziqingluo-90 // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
58a4323586Sziqingluo-90 static std::string getDREAncestorString(const DeclRefExpr *DRE,
59a4323586Sziqingluo-90                                         ASTContext &Ctx) {
60a4323586Sziqingluo-90   std::stringstream SS;
61a4323586Sziqingluo-90   const Stmt *St = DRE;
62a4323586Sziqingluo-90   StmtDebugPrinter StmtPriner;
63a4323586Sziqingluo-90 
64a4323586Sziqingluo-90   do {
65a4323586Sziqingluo-90     SS << StmtPriner.Visit(St);
66a4323586Sziqingluo-90 
67a4323586Sziqingluo-90     DynTypedNodeList StParents = Ctx.getParents(*St);
68a4323586Sziqingluo-90 
69a4323586Sziqingluo-90     if (StParents.size() > 1)
70a4323586Sziqingluo-90       return "unavailable due to multiple parents";
71a4323586Sziqingluo-90     if (StParents.size() == 0)
72a4323586Sziqingluo-90       break;
73a4323586Sziqingluo-90     St = StParents.begin()->get<Stmt>();
74a4323586Sziqingluo-90     if (St)
75a4323586Sziqingluo-90       SS << " ==> ";
76a4323586Sziqingluo-90   } while (St);
77a4323586Sziqingluo-90   return SS.str();
78a4323586Sziqingluo-90 }
79a4323586Sziqingluo-90 } // namespace
80a4323586Sziqingluo-90 #endif /* NDEBUG */
81a4323586Sziqingluo-90 
827d0d34fbSziqingluo-90 namespace clang::ast_matchers {
837d0d34fbSziqingluo-90 // A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
847d0d34fbSziqingluo-90 // except for those belonging to a different callable of "n".
85dde802b1SSirraide class MatchDescendantVisitor : public DynamicRecursiveASTVisitor {
867d0d34fbSziqingluo-90 public:
877d0d34fbSziqingluo-90   // Creates an AST visitor that matches `Matcher` on all
887d0d34fbSziqingluo-90   // descendants of a given node "n" except for the ones
897d0d34fbSziqingluo-90   // belonging to a different callable of "n".
9042b1d08bSziqingluo-90   MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
917d0d34fbSziqingluo-90                          internal::ASTMatchFinder *Finder,
927d0d34fbSziqingluo-90                          internal::BoundNodesTreeBuilder *Builder,
939516419cSMalavikaSamak                          internal::ASTMatchFinder::BindKind Bind,
949516419cSMalavikaSamak                          const bool ignoreUnevaluatedContext)
957d0d34fbSziqingluo-90       : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
96dde802b1SSirraide         Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {
97dde802b1SSirraide     ShouldVisitTemplateInstantiations = true;
98dde802b1SSirraide     ShouldVisitImplicitCode = false; // TODO: let's ignore implicit code for now
99dde802b1SSirraide   }
1007d0d34fbSziqingluo-90 
1017d0d34fbSziqingluo-90   // Returns true if a match is found in a subtree of `DynNode`, which belongs
1027d0d34fbSziqingluo-90   // to the same callable of `DynNode`.
1037d0d34fbSziqingluo-90   bool findMatch(const DynTypedNode &DynNode) {
1047d0d34fbSziqingluo-90     Matches = false;
1057d0d34fbSziqingluo-90     if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
1067d0d34fbSziqingluo-90       TraverseStmt(const_cast<Stmt *>(StmtNode));
1077d0d34fbSziqingluo-90       *Builder = ResultBindings;
1087d0d34fbSziqingluo-90       return Matches;
1097d0d34fbSziqingluo-90     }
1107d0d34fbSziqingluo-90     return false;
1117d0d34fbSziqingluo-90   }
1127d0d34fbSziqingluo-90 
1137d0d34fbSziqingluo-90   // The following are overriding methods from the base visitor class.
1147d0d34fbSziqingluo-90   // They are public only to allow CRTP to work. They are *not *part
1157d0d34fbSziqingluo-90   // of the public API of this class.
1167d0d34fbSziqingluo-90 
1177d0d34fbSziqingluo-90   // For the matchers so far used in safe buffers, we only need to match
1187d0d34fbSziqingluo-90   // `Stmt`s.  To override more as needed.
1197d0d34fbSziqingluo-90 
120dde802b1SSirraide   bool TraverseDecl(Decl *Node) override {
1217d0d34fbSziqingluo-90     if (!Node)
1227d0d34fbSziqingluo-90       return true;
1237d0d34fbSziqingluo-90     if (!match(*Node))
1247d0d34fbSziqingluo-90       return false;
1257d0d34fbSziqingluo-90     // To skip callables:
1267d0d34fbSziqingluo-90     if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
1277d0d34fbSziqingluo-90       return true;
1287d0d34fbSziqingluo-90     // Traverse descendants
129dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseDecl(Node);
1307d0d34fbSziqingluo-90   }
1317d0d34fbSziqingluo-90 
132dde802b1SSirraide   bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override {
1339516419cSMalavikaSamak     // These are unevaluated, except the result expression.
1349516419cSMalavikaSamak     if (ignoreUnevaluatedContext)
1359516419cSMalavikaSamak       return TraverseStmt(Node->getResultExpr());
136dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseGenericSelectionExpr(Node);
1379516419cSMalavikaSamak   }
1389516419cSMalavikaSamak 
139dde802b1SSirraide   bool
140dde802b1SSirraide   TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override {
1419516419cSMalavikaSamak     // Unevaluated context.
1429516419cSMalavikaSamak     if (ignoreUnevaluatedContext)
1439516419cSMalavikaSamak       return true;
144dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(Node);
1459516419cSMalavikaSamak   }
1469516419cSMalavikaSamak 
147dde802b1SSirraide   bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) override {
1489516419cSMalavikaSamak     // Unevaluated context.
1499516419cSMalavikaSamak     if (ignoreUnevaluatedContext)
1509516419cSMalavikaSamak       return true;
151dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseTypeOfExprTypeLoc(Node);
1529516419cSMalavikaSamak   }
1539516419cSMalavikaSamak 
154dde802b1SSirraide   bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) override {
1559516419cSMalavikaSamak     // Unevaluated context.
1569516419cSMalavikaSamak     if (ignoreUnevaluatedContext)
1579516419cSMalavikaSamak       return true;
158dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseDecltypeTypeLoc(Node);
1599516419cSMalavikaSamak   }
1609516419cSMalavikaSamak 
161dde802b1SSirraide   bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override {
1629516419cSMalavikaSamak     // Unevaluated context.
1639516419cSMalavikaSamak     if (ignoreUnevaluatedContext)
1649516419cSMalavikaSamak       return true;
165dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseCXXNoexceptExpr(Node);
1669516419cSMalavikaSamak   }
1679516419cSMalavikaSamak 
168dde802b1SSirraide   bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override {
1699516419cSMalavikaSamak     // Unevaluated context.
1709516419cSMalavikaSamak     if (ignoreUnevaluatedContext)
1719516419cSMalavikaSamak       return true;
172dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(Node);
1739516419cSMalavikaSamak   }
1749516419cSMalavikaSamak 
175c60b055dSZequan Wu   bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) override {
176c60b055dSZequan Wu     if (!TraverseStmt(Node->getExpr()))
177c60b055dSZequan Wu       return false;
178c60b055dSZequan Wu     return DynamicRecursiveASTVisitor::TraverseCXXDefaultInitExpr(Node);
179c60b055dSZequan Wu   }
180c60b055dSZequan Wu 
181dde802b1SSirraide   bool TraverseStmt(Stmt *Node) override {
1827d0d34fbSziqingluo-90     if (!Node)
1837d0d34fbSziqingluo-90       return true;
1847d0d34fbSziqingluo-90     if (!match(*Node))
1857d0d34fbSziqingluo-90       return false;
186dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseStmt(Node);
1877d0d34fbSziqingluo-90   }
1887d0d34fbSziqingluo-90 
1897d0d34fbSziqingluo-90 private:
1907d0d34fbSziqingluo-90   // Sets 'Matched' to true if 'Matcher' matches 'Node'
1917d0d34fbSziqingluo-90   //
1927d0d34fbSziqingluo-90   // Returns 'true' if traversal should continue after this function
1937d0d34fbSziqingluo-90   // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
1947d0d34fbSziqingluo-90   template <typename T> bool match(const T &Node) {
1957d0d34fbSziqingluo-90     internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
1967d0d34fbSziqingluo-90 
19742b1d08bSziqingluo-90     if (Matcher->matches(DynTypedNode::create(Node), Finder,
1987d0d34fbSziqingluo-90                          &RecursiveBuilder)) {
1997d0d34fbSziqingluo-90       ResultBindings.addMatch(RecursiveBuilder);
2007d0d34fbSziqingluo-90       Matches = true;
2017d0d34fbSziqingluo-90       if (Bind != internal::ASTMatchFinder::BK_All)
2027d0d34fbSziqingluo-90         return false; // Abort as soon as a match is found.
2037d0d34fbSziqingluo-90     }
2047d0d34fbSziqingluo-90     return true;
2057d0d34fbSziqingluo-90   }
2067d0d34fbSziqingluo-90 
20742b1d08bSziqingluo-90   const internal::DynTypedMatcher *const Matcher;
2087d0d34fbSziqingluo-90   internal::ASTMatchFinder *const Finder;
2097d0d34fbSziqingluo-90   internal::BoundNodesTreeBuilder *const Builder;
2107d0d34fbSziqingluo-90   internal::BoundNodesTreeBuilder ResultBindings;
2117d0d34fbSziqingluo-90   const internal::ASTMatchFinder::BindKind Bind;
2127d0d34fbSziqingluo-90   bool Matches;
2139516419cSMalavikaSamak   bool ignoreUnevaluatedContext;
2147d0d34fbSziqingluo-90 };
2157d0d34fbSziqingluo-90 
216ca6ceeb0SZiqing Luo // Because we're dealing with raw pointers, let's define what we mean by that.
217ca6ceeb0SZiqing Luo static auto hasPointerType() {
218ca6ceeb0SZiqing Luo   return hasType(hasCanonicalType(pointerType()));
219ca6ceeb0SZiqing Luo }
220ca6ceeb0SZiqing Luo 
2213fa91021Sjkorous-apple static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); }
222ca6ceeb0SZiqing Luo 
2233fa91021Sjkorous-apple AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>,
2243fa91021Sjkorous-apple               innerMatcher) {
2255be422b4Sziqingluo-90   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
2265be422b4Sziqingluo-90 
2273fa91021Sjkorous-apple   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
2283fa91021Sjkorous-apple                                  true);
2299516419cSMalavikaSamak   return Visitor.findMatch(DynTypedNode::create(Node));
2309516419cSMalavikaSamak }
2319516419cSMalavikaSamak 
2323fa91021Sjkorous-apple AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>,
2333fa91021Sjkorous-apple               innerMatcher) {
2349516419cSMalavikaSamak   const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
2359516419cSMalavikaSamak 
2363fa91021Sjkorous-apple   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
2373fa91021Sjkorous-apple                                  false);
2387d0d34fbSziqingluo-90   return Visitor.findMatch(DynTypedNode::create(Node));
2397d0d34fbSziqingluo-90 }
240bdf4f2beSZiqing Luo 
241829bcb06SZiqing Luo // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
242148dc8a2Sziqingluo-90 AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *,
243148dc8a2Sziqingluo-90               Handler) {
244829bcb06SZiqing Luo   return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
245829bcb06SZiqing Luo }
246829bcb06SZiqing Luo 
2479816863dSZiqing Luo AST_MATCHER_P(Stmt, ignoreUnsafeBufferInContainer,
2489816863dSZiqing Luo               const UnsafeBufferUsageHandler *, Handler) {
2499816863dSZiqing Luo   return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());
2509816863dSZiqing Luo }
2519816863dSZiqing Luo 
252d7dd2c46Sziqingluo-90 AST_MATCHER_P(Stmt, ignoreUnsafeLibcCall, const UnsafeBufferUsageHandler *,
253d7dd2c46Sziqingluo-90               Handler) {
254090dc77aSZiqing Luo   if (Finder->getASTContext().getLangOpts().CPlusPlus)
255d7dd2c46Sziqingluo-90     return Handler->ignoreUnsafeBufferInLibcCall(Node.getBeginLoc());
256090dc77aSZiqing Luo   return true; /* Only warn about libc calls for C++ */
257d7dd2c46Sziqingluo-90 }
258d7dd2c46Sziqingluo-90 
259bdf4f2beSZiqing Luo AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
260bdf4f2beSZiqing Luo   return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
261bdf4f2beSZiqing Luo }
262bdf4f2beSZiqing Luo 
263762af11dSZiqing Luo // Matches a `UnaryOperator` whose operator is pre-increment:
264762af11dSZiqing Luo AST_MATCHER(UnaryOperator, isPreInc) {
265762af11dSZiqing Luo   return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
266762af11dSZiqing Luo }
267762af11dSZiqing Luo 
268bdf4f2beSZiqing Luo // Returns a matcher that matches any expression 'e' such that `innerMatcher`
269bdf4f2beSZiqing Luo // matches 'e' and 'e' is in an Unspecified Lvalue Context.
270cd265296SZiqing Luo static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) {
271cd265296SZiqing Luo   // clang-format off
272cd265296SZiqing Luo   return
273cd265296SZiqing Luo     expr(anyOf(
274cd265296SZiqing Luo       implicitCastExpr(
275cd265296SZiqing Luo         hasCastKind(CastKind::CK_LValueToRValue),
276cd265296SZiqing Luo         castSubExpr(innerMatcher)),
277cd265296SZiqing Luo       binaryOperator(
278cd265296SZiqing Luo         hasAnyOperatorName("="),
279cd265296SZiqing Luo         hasLHS(innerMatcher)
280cd265296SZiqing Luo       )
281cd265296SZiqing Luo     ));
2827c7b1914SArtem Dergachev   // clang-format on
283bdf4f2beSZiqing Luo }
284ca6ceeb0SZiqing Luo 
285ca6ceeb0SZiqing Luo // Returns a matcher that matches any expression `e` such that `InnerMatcher`
286ca6ceeb0SZiqing Luo // matches `e` and `e` is in an Unspecified Pointer Context (UPC).
287ca6ceeb0SZiqing Luo static internal::Matcher<Stmt>
288ca6ceeb0SZiqing Luo isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
289ca6ceeb0SZiqing Luo   // A UPC can be
290ca6ceeb0SZiqing Luo   // 1. an argument of a function call (except the callee has [[unsafe_...]]
291ca6ceeb0SZiqing Luo   //    attribute), or
29288f7f018SZiqing Luo   // 2. the operand of a pointer-to-(integer or bool) cast operation; or
293a046d187SMalavikaSamak   // 3. the operand of a comparator operation; or
29488f7f018SZiqing Luo   // 4. the operand of a pointer subtraction operation
29588f7f018SZiqing Luo   //    (i.e., computing the distance between two pointers); or ...
29688f7f018SZiqing Luo 
2974cd7616fSjkorous-apple   // clang-format off
2984cd7616fSjkorous-apple   auto CallArgMatcher = callExpr(
2994cd7616fSjkorous-apple     forEachArgumentWithParamType(
3004cd7616fSjkorous-apple       InnerMatcher,
3014cd7616fSjkorous-apple       isAnyPointer() /* array also decays to pointer type*/),
3024cd7616fSjkorous-apple     unless(callee(
3034cd7616fSjkorous-apple       functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
304a046d187SMalavikaSamak 
305ca6ceeb0SZiqing Luo   auto CastOperandMatcher =
30688f7f018SZiqing Luo       castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
30788f7f018SZiqing Luo 		     hasCastKind(CastKind::CK_PointerToBoolean)),
308ca6ceeb0SZiqing Luo 	       castSubExpr(allOf(hasPointerType(), InnerMatcher)));
309ca6ceeb0SZiqing Luo 
310a046d187SMalavikaSamak   auto CompOperandMatcher =
311a046d187SMalavikaSamak       binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
312a046d187SMalavikaSamak                      eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
313a046d187SMalavikaSamak                             hasRHS(allOf(hasPointerType(), InnerMatcher))));
314a046d187SMalavikaSamak 
31588f7f018SZiqing Luo   // A matcher that matches pointer subtractions:
31688f7f018SZiqing Luo   auto PtrSubtractionMatcher =
31788f7f018SZiqing Luo       binaryOperator(hasOperatorName("-"),
31888f7f018SZiqing Luo 		     // Note that here we need both LHS and RHS to be
31988f7f018SZiqing Luo 		     // pointer. Then the inner matcher can match any of
32088f7f018SZiqing Luo 		     // them:
32188f7f018SZiqing Luo 		     allOf(hasLHS(hasPointerType()),
32288f7f018SZiqing Luo 			   hasRHS(hasPointerType())),
32388f7f018SZiqing Luo 		     eachOf(hasLHS(InnerMatcher),
32488f7f018SZiqing Luo 			    hasRHS(InnerMatcher)));
3254cd7616fSjkorous-apple   // clang-format on
32688f7f018SZiqing Luo 
32788f7f018SZiqing Luo   return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
32888f7f018SZiqing Luo                     PtrSubtractionMatcher));
329ca6ceeb0SZiqing Luo   // FIXME: any more cases? (UPC excludes the RHS of an assignment.  For now we
330ca6ceeb0SZiqing Luo   // don't have to check that.)
331ca6ceeb0SZiqing Luo }
332171dfc54SRashmi Mudduluru 
333171dfc54SRashmi Mudduluru // Returns a matcher that matches any expression 'e' such that `innerMatcher`
334171dfc54SRashmi Mudduluru // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression
335171dfc54SRashmi Mudduluru // 'e' isn't evaluated to an RValue). For example, consider the following code:
336171dfc54SRashmi Mudduluru //    int *p = new int[4];
337171dfc54SRashmi Mudduluru //    int *q = new int[4];
338171dfc54SRashmi Mudduluru //    if ((p = q)) {}
339171dfc54SRashmi Mudduluru //    p = q;
340171dfc54SRashmi Mudduluru // The expression `p = q` in the conditional of the `if` statement
341171dfc54SRashmi Mudduluru // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
342171dfc54SRashmi Mudduluru // in the assignment statement is in an untyped context.
343171dfc54SRashmi Mudduluru static internal::Matcher<Stmt>
344171dfc54SRashmi Mudduluru isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
345171dfc54SRashmi Mudduluru   // An unspecified context can be
346171dfc54SRashmi Mudduluru   // 1. A compound statement,
347171dfc54SRashmi Mudduluru   // 2. The body of an if statement
348171dfc54SRashmi Mudduluru   // 3. Body of a loop
349171dfc54SRashmi Mudduluru   auto CompStmt = compoundStmt(forEach(InnerMatcher));
350171dfc54SRashmi Mudduluru   auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
351171dfc54SRashmi Mudduluru   auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
352171dfc54SRashmi Mudduluru   // FIXME: Handle loop bodies.
353171dfc54SRashmi Mudduluru   return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
354171dfc54SRashmi Mudduluru }
3559816863dSZiqing Luo 
3569816863dSZiqing Luo // Given a two-param std::span construct call, matches iff the call has the
3579816863dSZiqing Luo // following forms:
3589816863dSZiqing Luo //   1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
3599816863dSZiqing Luo //   2. `std::span<T>{new T, 1}`
3609816863dSZiqing Luo //   3. `std::span<T>{&var, 1}`
3619816863dSZiqing Luo //   4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size
3629816863dSZiqing Luo //   `n`
3639816863dSZiqing Luo //   5. `std::span<T>{any, 0}`
3640dcb0acfSMalavika Samak //   6. `std::span<T>{std::addressof(...), 1}`
3659816863dSZiqing Luo AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
3669816863dSZiqing Luo   assert(Node.getNumArgs() == 2 &&
3679816863dSZiqing Luo          "expecting a two-parameter std::span constructor");
3689816863dSZiqing Luo   const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit();
3699816863dSZiqing Luo   const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit();
3709816863dSZiqing Luo   auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) {
3719816863dSZiqing Luo     if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext()))
3729816863dSZiqing Luo       if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) {
3739816863dSZiqing Luo         return APSInt::compareValues(*E0CV, *E1CV) == 0;
3749816863dSZiqing Luo       }
3759816863dSZiqing Luo     return false;
3769816863dSZiqing Luo   };
3779816863dSZiqing Luo   auto AreSameDRE = [](const Expr *E0, const Expr *E1) {
3789816863dSZiqing Luo     if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
3799816863dSZiqing Luo       if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
3809816863dSZiqing Luo         return DRE0->getDecl() == DRE1->getDecl();
3819816863dSZiqing Luo       }
3829816863dSZiqing Luo     return false;
3839816863dSZiqing Luo   };
3849816863dSZiqing Luo   std::optional<APSInt> Arg1CV =
3859816863dSZiqing Luo       Arg1->getIntegerConstantExpr(Finder->getASTContext());
3869816863dSZiqing Luo 
3879816863dSZiqing Luo   if (Arg1CV && Arg1CV->isZero())
3889816863dSZiqing Luo     // Check form 5:
3899816863dSZiqing Luo     return true;
3909816863dSZiqing Luo   switch (Arg0->IgnoreImplicit()->getStmtClass()) {
3919816863dSZiqing Luo   case Stmt::CXXNewExprClass:
3929816863dSZiqing Luo     if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) {
3939816863dSZiqing Luo       // Check form 1:
3949816863dSZiqing Luo       return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
3959816863dSZiqing Luo              HaveEqualConstantValues(*Size, Arg1);
3969816863dSZiqing Luo     }
3979816863dSZiqing Luo     // TODO: what's placeholder type? avoid it for now.
3989816863dSZiqing Luo     if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) {
3999816863dSZiqing Luo       // Check form 2:
4009816863dSZiqing Luo       return Arg1CV && Arg1CV->isOne();
4019816863dSZiqing Luo     }
4029816863dSZiqing Luo     break;
4039816863dSZiqing Luo   case Stmt::UnaryOperatorClass:
4049816863dSZiqing Luo     if (cast<UnaryOperator>(Arg0)->getOpcode() ==
4059816863dSZiqing Luo         UnaryOperator::Opcode::UO_AddrOf)
4069816863dSZiqing Luo       // Check form 3:
4079816863dSZiqing Luo       return Arg1CV && Arg1CV->isOne();
4089816863dSZiqing Luo     break;
4090dcb0acfSMalavika Samak   case Stmt::CallExprClass:
4100dcb0acfSMalavika Samak     if (const auto *CE = dyn_cast<CallExpr>(Arg0)) {
4110dcb0acfSMalavika Samak       const auto FnDecl = CE->getDirectCallee();
4120dcb0acfSMalavika Samak       if (FnDecl && FnDecl->getNameAsString() == "addressof" &&
4130dcb0acfSMalavika Samak           FnDecl->isInStdNamespace()) {
4140dcb0acfSMalavika Samak         return Arg1CV && Arg1CV->isOne();
4150dcb0acfSMalavika Samak       }
4160dcb0acfSMalavika Samak     }
4170dcb0acfSMalavika Samak     break;
4189816863dSZiqing Luo   default:
4199816863dSZiqing Luo     break;
4209816863dSZiqing Luo   }
4219816863dSZiqing Luo 
4229816863dSZiqing Luo   QualType Arg0Ty = Arg0->IgnoreImplicit()->getType();
4239816863dSZiqing Luo 
4249aa3b53aSZiqing Luo   if (auto *ConstArrTy =
4259aa3b53aSZiqing Luo           Finder->getASTContext().getAsConstantArrayType(Arg0Ty)) {
4269aa3b53aSZiqing Luo     const APSInt ConstArrSize = APSInt(ConstArrTy->getSize());
4279816863dSZiqing Luo 
4289816863dSZiqing Luo     // Check form 4:
42928ddbd4aSChris B     return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;
4309816863dSZiqing Luo   }
4319816863dSZiqing Luo   return false;
4329816863dSZiqing Luo }
4339a1e6373Sjkorous-apple 
4349a1e6373Sjkorous-apple AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
4359a1e6373Sjkorous-apple   // FIXME: Proper solution:
4369a1e6373Sjkorous-apple   //  - refactor Sema::CheckArrayAccess
4379a1e6373Sjkorous-apple   //    - split safe/OOB/unknown decision logic from diagnostics emitting code
4389a1e6373Sjkorous-apple   //    -  e. g. "Try harder to find a NamedDecl to point at in the note."
4399a1e6373Sjkorous-apple   //    already duplicated
4409a1e6373Sjkorous-apple   //  - call both from Sema and from here
4419a1e6373Sjkorous-apple 
4429c50182bSMalavika Samak   uint64_t limit;
4439c50182bSMalavika Samak   if (const auto *CATy =
4449c50182bSMalavika Samak           dyn_cast<ConstantArrayType>(Node.getBase()
4459c50182bSMalavika Samak                                           ->IgnoreParenImpCasts()
4469c50182bSMalavika Samak                                           ->getType()
4479c50182bSMalavika Samak                                           ->getUnqualifiedDesugaredType())) {
4489c50182bSMalavika Samak     limit = CATy->getLimitedSize();
4499c50182bSMalavika Samak   } else if (const auto *SLiteral = dyn_cast<StringLiteral>(
4509c50182bSMalavika Samak                  Node.getBase()->IgnoreParenImpCasts())) {
4519c50182bSMalavika Samak     limit = SLiteral->getLength() + 1;
4529c50182bSMalavika Samak   } else {
4539a1e6373Sjkorous-apple     return false;
454da032a60SMalavika Samak   }
4559a1e6373Sjkorous-apple 
456*2a8c12b2SMalavika Samak   Expr::EvalResult EVResult;
457*2a8c12b2SMalavika Samak   const Expr *IndexExpr = Node.getIdx();
458*2a8c12b2SMalavika Samak   if (!IndexExpr->isValueDependent() &&
459*2a8c12b2SMalavika Samak       IndexExpr->EvaluateAsInt(EVResult, Finder->getASTContext())) {
460*2a8c12b2SMalavika Samak     llvm::APSInt ArrIdx = EVResult.Val.getInt();
461*2a8c12b2SMalavika Samak     // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
462*2a8c12b2SMalavika Samak     // bug
4639c50182bSMalavika Samak     if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
4649a1e6373Sjkorous-apple       return true;
4659a1e6373Sjkorous-apple   }
4669a1e6373Sjkorous-apple   return false;
4679a1e6373Sjkorous-apple }
4689a1e6373Sjkorous-apple 
469d7dd2c46Sziqingluo-90 AST_MATCHER_P(CallExpr, hasNumArgs, unsigned, Num) {
470d7dd2c46Sziqingluo-90   return Node.getNumArgs() == Num;
471d7dd2c46Sziqingluo-90 }
472d7dd2c46Sziqingluo-90 
473d7dd2c46Sziqingluo-90 namespace libc_func_matchers {
474d7dd2c46Sziqingluo-90 // Under `libc_func_matchers`, define a set of matchers that match unsafe
475d7dd2c46Sziqingluo-90 // functions in libc and unsafe calls to them.
476d7dd2c46Sziqingluo-90 
477d7dd2c46Sziqingluo-90 //  A tiny parser to strip off common prefix and suffix of libc function names
478d7dd2c46Sziqingluo-90 //  in real code.
479d7dd2c46Sziqingluo-90 //
480d7dd2c46Sziqingluo-90 //  Given a function name, `matchName` returns `CoreName` according to the
481d7dd2c46Sziqingluo-90 //  following grammar:
482d7dd2c46Sziqingluo-90 //
483d7dd2c46Sziqingluo-90 //  LibcName     := CoreName | CoreName + "_s"
484d7dd2c46Sziqingluo-90 //  MatchingName := "__builtin_" + LibcName              |
485d7dd2c46Sziqingluo-90 //                  "__builtin___" + LibcName + "_chk"   |
486d7dd2c46Sziqingluo-90 //                  "__asan_" + LibcName
487d7dd2c46Sziqingluo-90 //
488d7dd2c46Sziqingluo-90 struct LibcFunNamePrefixSuffixParser {
489d7dd2c46Sziqingluo-90   StringRef matchName(StringRef FunName, bool isBuiltin) {
490d7dd2c46Sziqingluo-90     // Try to match __builtin_:
491d7dd2c46Sziqingluo-90     if (isBuiltin && FunName.starts_with("__builtin_"))
492d7dd2c46Sziqingluo-90       // Then either it is __builtin_LibcName or __builtin___LibcName_chk or
493d7dd2c46Sziqingluo-90       // no match:
494d7dd2c46Sziqingluo-90       return matchLibcNameOrBuiltinChk(
495d7dd2c46Sziqingluo-90           FunName.drop_front(10 /* truncate "__builtin_" */));
496d7dd2c46Sziqingluo-90     // Try to match __asan_:
497d7dd2c46Sziqingluo-90     if (FunName.starts_with("__asan_"))
498d7dd2c46Sziqingluo-90       return matchLibcName(FunName.drop_front(7 /* truncate of "__asan_" */));
499d7dd2c46Sziqingluo-90     return matchLibcName(FunName);
500d7dd2c46Sziqingluo-90   }
501d7dd2c46Sziqingluo-90 
502d7dd2c46Sziqingluo-90   // Parameter `Name` is the substring after stripping off the prefix
503d7dd2c46Sziqingluo-90   // "__builtin_".
504d7dd2c46Sziqingluo-90   StringRef matchLibcNameOrBuiltinChk(StringRef Name) {
505d7dd2c46Sziqingluo-90     if (Name.starts_with("__") && Name.ends_with("_chk"))
506d7dd2c46Sziqingluo-90       return matchLibcName(
507d7dd2c46Sziqingluo-90           Name.drop_front(2).drop_back(4) /* truncate "__" and "_chk" */);
508d7dd2c46Sziqingluo-90     return matchLibcName(Name);
509d7dd2c46Sziqingluo-90   }
510d7dd2c46Sziqingluo-90 
511d7dd2c46Sziqingluo-90   StringRef matchLibcName(StringRef Name) {
512d7dd2c46Sziqingluo-90     if (Name.ends_with("_s"))
513d7dd2c46Sziqingluo-90       return Name.drop_back(2 /* truncate "_s" */);
514d7dd2c46Sziqingluo-90     return Name;
515d7dd2c46Sziqingluo-90   }
516d7dd2c46Sziqingluo-90 };
517d7dd2c46Sziqingluo-90 
518d7dd2c46Sziqingluo-90 // A pointer type expression is known to be null-terminated, if it has the
519d7dd2c46Sziqingluo-90 // form: E.c_str(), for any expression E of `std::string` type.
520d7dd2c46Sziqingluo-90 static bool isNullTermPointer(const Expr *Ptr) {
521d7dd2c46Sziqingluo-90   if (isa<StringLiteral>(Ptr->IgnoreParenImpCasts()))
522d7dd2c46Sziqingluo-90     return true;
523d7dd2c46Sziqingluo-90   if (isa<PredefinedExpr>(Ptr->IgnoreParenImpCasts()))
524d7dd2c46Sziqingluo-90     return true;
525d7dd2c46Sziqingluo-90   if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts())) {
526d7dd2c46Sziqingluo-90     const CXXMethodDecl *MD = MCE->getMethodDecl();
527d7dd2c46Sziqingluo-90     const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
528d7dd2c46Sziqingluo-90 
529d7dd2c46Sziqingluo-90     if (MD && RD && RD->isInStdNamespace())
530d7dd2c46Sziqingluo-90       if (MD->getName() == "c_str" && RD->getName() == "basic_string")
531d7dd2c46Sziqingluo-90         return true;
532d7dd2c46Sziqingluo-90   }
533d7dd2c46Sziqingluo-90   return false;
534d7dd2c46Sziqingluo-90 }
535d7dd2c46Sziqingluo-90 
536d7dd2c46Sziqingluo-90 // Return true iff at least one of following cases holds:
537d7dd2c46Sziqingluo-90 //  1. Format string is a literal and there is an unsafe pointer argument
538d7dd2c46Sziqingluo-90 //     corresponding to an `s` specifier;
539d7dd2c46Sziqingluo-90 //  2. Format string is not a literal and there is least an unsafe pointer
540d7dd2c46Sziqingluo-90 //     argument (including the formatter argument).
541d7dd2c46Sziqingluo-90 //
542d7dd2c46Sziqingluo-90 // `UnsafeArg` is the output argument that will be set only if this function
543d7dd2c46Sziqingluo-90 // returns true.
544d7dd2c46Sziqingluo-90 static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
545d7dd2c46Sziqingluo-90                                   const unsigned FmtArgIdx, ASTContext &Ctx,
546d7dd2c46Sziqingluo-90                                   bool isKprintf = false) {
547d7dd2c46Sziqingluo-90   class StringFormatStringHandler
548d7dd2c46Sziqingluo-90       : public analyze_format_string::FormatStringHandler {
549d7dd2c46Sziqingluo-90     const CallExpr *Call;
550d7dd2c46Sziqingluo-90     unsigned FmtArgIdx;
551d7dd2c46Sziqingluo-90     const Expr *&UnsafeArg;
552d7dd2c46Sziqingluo-90 
553d7dd2c46Sziqingluo-90   public:
554d7dd2c46Sziqingluo-90     StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
555d7dd2c46Sziqingluo-90                               const Expr *&UnsafeArg)
556d7dd2c46Sziqingluo-90         : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg) {}
557d7dd2c46Sziqingluo-90 
558d7dd2c46Sziqingluo-90     bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
559d7dd2c46Sziqingluo-90                                const char *startSpecifier,
560d7dd2c46Sziqingluo-90                                unsigned specifierLen,
561d7dd2c46Sziqingluo-90                                const TargetInfo &Target) override {
562d7dd2c46Sziqingluo-90       if (FS.getConversionSpecifier().getKind() ==
563d7dd2c46Sziqingluo-90           analyze_printf::PrintfConversionSpecifier::sArg) {
564d7dd2c46Sziqingluo-90         unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
565d7dd2c46Sziqingluo-90 
566d7dd2c46Sziqingluo-90         if (0 < ArgIdx && ArgIdx < Call->getNumArgs())
567d7dd2c46Sziqingluo-90           if (!isNullTermPointer(Call->getArg(ArgIdx))) {
568d7dd2c46Sziqingluo-90             UnsafeArg = Call->getArg(ArgIdx); // output
569d7dd2c46Sziqingluo-90             // returning false stops parsing immediately
570d7dd2c46Sziqingluo-90             return false;
571d7dd2c46Sziqingluo-90           }
572d7dd2c46Sziqingluo-90       }
573d7dd2c46Sziqingluo-90       return true; // continue parsing
574d7dd2c46Sziqingluo-90     }
575d7dd2c46Sziqingluo-90   };
576d7dd2c46Sziqingluo-90 
577d7dd2c46Sziqingluo-90   const Expr *Fmt = Call->getArg(FmtArgIdx);
578d7dd2c46Sziqingluo-90 
579d7dd2c46Sziqingluo-90   if (auto *SL = dyn_cast<StringLiteral>(Fmt->IgnoreParenImpCasts())) {
58048498ec7Sziqingluo-90     StringRef FmtStr;
58148498ec7Sziqingluo-90 
58248498ec7Sziqingluo-90     if (SL->getCharByteWidth() == 1)
58348498ec7Sziqingluo-90       FmtStr = SL->getString();
58448498ec7Sziqingluo-90     else if (auto EvaledFmtStr = SL->tryEvaluateString(Ctx))
58548498ec7Sziqingluo-90       FmtStr = *EvaledFmtStr;
58648498ec7Sziqingluo-90     else
58748498ec7Sziqingluo-90       goto CHECK_UNSAFE_PTR;
58848498ec7Sziqingluo-90 
589d7dd2c46Sziqingluo-90     StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg);
590d7dd2c46Sziqingluo-90 
591d7dd2c46Sziqingluo-90     return analyze_format_string::ParsePrintfString(
592d7dd2c46Sziqingluo-90         Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
593d7dd2c46Sziqingluo-90         Ctx.getTargetInfo(), isKprintf);
594d7dd2c46Sziqingluo-90   }
59548498ec7Sziqingluo-90 CHECK_UNSAFE_PTR:
596d7dd2c46Sziqingluo-90   // If format is not a string literal, we cannot analyze the format string.
597d7dd2c46Sziqingluo-90   // In this case, this call is considered unsafe if at least one argument
598d7dd2c46Sziqingluo-90   // (including the format argument) is unsafe pointer.
599d7dd2c46Sziqingluo-90   return llvm::any_of(
600d7dd2c46Sziqingluo-90       llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),
601d7dd2c46Sziqingluo-90       [&UnsafeArg](const Expr *Arg) -> bool {
602d7dd2c46Sziqingluo-90         if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
603d7dd2c46Sziqingluo-90           UnsafeArg = Arg;
604d7dd2c46Sziqingluo-90           return true;
605d7dd2c46Sziqingluo-90         }
606d7dd2c46Sziqingluo-90         return false;
607d7dd2c46Sziqingluo-90       });
608d7dd2c46Sziqingluo-90 }
609d7dd2c46Sziqingluo-90 
610d7dd2c46Sziqingluo-90 // Matches a FunctionDecl node such that
611d7dd2c46Sziqingluo-90 //  1. It's name, after stripping off predefined prefix and suffix, is
612d7dd2c46Sziqingluo-90 //     `CoreName`; and
613d7dd2c46Sziqingluo-90 //  2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
614d7dd2c46Sziqingluo-90 //     is a set of libc function names.
615d7dd2c46Sziqingluo-90 //
616d7dd2c46Sziqingluo-90 //  Note: For predefined prefix and suffix, see `LibcFunNamePrefixSuffixParser`.
617d7dd2c46Sziqingluo-90 //  The notation `CoreName[str/wcs]` means a new name obtained from replace
618d7dd2c46Sziqingluo-90 //  string "wcs" with "str" in `CoreName`.
619d7dd2c46Sziqingluo-90 AST_MATCHER(FunctionDecl, isPredefinedUnsafeLibcFunc) {
620d7dd2c46Sziqingluo-90   static std::unique_ptr<std::set<StringRef>> PredefinedNames = nullptr;
621d7dd2c46Sziqingluo-90   if (!PredefinedNames)
622d7dd2c46Sziqingluo-90     PredefinedNames =
623d7dd2c46Sziqingluo-90         std::make_unique<std::set<StringRef>, std::set<StringRef>>({
624d7dd2c46Sziqingluo-90             // numeric conversion:
625d7dd2c46Sziqingluo-90             "atof",
626d7dd2c46Sziqingluo-90             "atoi",
627d7dd2c46Sziqingluo-90             "atol",
628d7dd2c46Sziqingluo-90             "atoll",
629d7dd2c46Sziqingluo-90             "strtol",
630d7dd2c46Sziqingluo-90             "strtoll",
631d7dd2c46Sziqingluo-90             "strtoul",
632d7dd2c46Sziqingluo-90             "strtoull",
633d7dd2c46Sziqingluo-90             "strtof",
634d7dd2c46Sziqingluo-90             "strtod",
635d7dd2c46Sziqingluo-90             "strtold",
636d7dd2c46Sziqingluo-90             "strtoimax",
637d7dd2c46Sziqingluo-90             "strtoumax",
638d7dd2c46Sziqingluo-90             // "strfromf",  "strfromd", "strfroml", // C23?
639d7dd2c46Sziqingluo-90             // string manipulation:
640d7dd2c46Sziqingluo-90             "strcpy",
641d7dd2c46Sziqingluo-90             "strncpy",
642d7dd2c46Sziqingluo-90             "strlcpy",
643d7dd2c46Sziqingluo-90             "strcat",
644d7dd2c46Sziqingluo-90             "strncat",
645d7dd2c46Sziqingluo-90             "strlcat",
646d7dd2c46Sziqingluo-90             "strxfrm",
647d7dd2c46Sziqingluo-90             "strdup",
648d7dd2c46Sziqingluo-90             "strndup",
649d7dd2c46Sziqingluo-90             // string examination:
650d7dd2c46Sziqingluo-90             "strlen",
651d7dd2c46Sziqingluo-90             "strnlen",
652d7dd2c46Sziqingluo-90             "strcmp",
653d7dd2c46Sziqingluo-90             "strncmp",
654d7dd2c46Sziqingluo-90             "stricmp",
655d7dd2c46Sziqingluo-90             "strcasecmp",
656d7dd2c46Sziqingluo-90             "strcoll",
657d7dd2c46Sziqingluo-90             "strchr",
658d7dd2c46Sziqingluo-90             "strrchr",
659d7dd2c46Sziqingluo-90             "strspn",
660d7dd2c46Sziqingluo-90             "strcspn",
661d7dd2c46Sziqingluo-90             "strpbrk",
662d7dd2c46Sziqingluo-90             "strstr",
663d7dd2c46Sziqingluo-90             "strtok",
664d7dd2c46Sziqingluo-90             // "mem-" functions
665d7dd2c46Sziqingluo-90             "memchr",
666d7dd2c46Sziqingluo-90             "wmemchr",
667d7dd2c46Sziqingluo-90             "memcmp",
668d7dd2c46Sziqingluo-90             "wmemcmp",
669d7dd2c46Sziqingluo-90             "memcpy",
670d7dd2c46Sziqingluo-90             "memccpy",
671d7dd2c46Sziqingluo-90             "mempcpy",
672d7dd2c46Sziqingluo-90             "wmemcpy",
673d7dd2c46Sziqingluo-90             "memmove",
674d7dd2c46Sziqingluo-90             "wmemmove",
675d7dd2c46Sziqingluo-90             "memset",
676d7dd2c46Sziqingluo-90             "wmemset",
677d7dd2c46Sziqingluo-90             // IO:
678d7dd2c46Sziqingluo-90             "fread",
679d7dd2c46Sziqingluo-90             "fwrite",
680d7dd2c46Sziqingluo-90             "fgets",
681d7dd2c46Sziqingluo-90             "fgetws",
682d7dd2c46Sziqingluo-90             "gets",
683d7dd2c46Sziqingluo-90             "fputs",
684d7dd2c46Sziqingluo-90             "fputws",
685d7dd2c46Sziqingluo-90             "puts",
686d7dd2c46Sziqingluo-90             // others
687d7dd2c46Sziqingluo-90             "strerror_s",
688d7dd2c46Sziqingluo-90             "strerror_r",
689d7dd2c46Sziqingluo-90             "bcopy",
690d7dd2c46Sziqingluo-90             "bzero",
691d7dd2c46Sziqingluo-90             "bsearch",
692d7dd2c46Sziqingluo-90             "qsort",
693d7dd2c46Sziqingluo-90         });
694d7dd2c46Sziqingluo-90 
695d7dd2c46Sziqingluo-90   auto *II = Node.getIdentifier();
696d7dd2c46Sziqingluo-90 
697d7dd2c46Sziqingluo-90   if (!II)
698d7dd2c46Sziqingluo-90     return false;
699d7dd2c46Sziqingluo-90 
700d7dd2c46Sziqingluo-90   StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
701d7dd2c46Sziqingluo-90       II->getName(), Node.getBuiltinID());
702d7dd2c46Sziqingluo-90 
703d7dd2c46Sziqingluo-90   // Match predefined names:
704d7dd2c46Sziqingluo-90   if (PredefinedNames->find(Name) != PredefinedNames->end())
705d7dd2c46Sziqingluo-90     return true;
706d7dd2c46Sziqingluo-90 
707d7dd2c46Sziqingluo-90   std::string NameWCS = Name.str();
708d7dd2c46Sziqingluo-90   size_t WcsPos = NameWCS.find("wcs");
709d7dd2c46Sziqingluo-90 
710d7dd2c46Sziqingluo-90   while (WcsPos != std::string::npos) {
711d7dd2c46Sziqingluo-90     NameWCS[WcsPos++] = 's';
712d7dd2c46Sziqingluo-90     NameWCS[WcsPos++] = 't';
713d7dd2c46Sziqingluo-90     NameWCS[WcsPos++] = 'r';
714d7dd2c46Sziqingluo-90     WcsPos = NameWCS.find("wcs", WcsPos);
715d7dd2c46Sziqingluo-90   }
716d7dd2c46Sziqingluo-90   if (PredefinedNames->find(NameWCS) != PredefinedNames->end())
717d7dd2c46Sziqingluo-90     return true;
718d7dd2c46Sziqingluo-90   // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. They
719d7dd2c46Sziqingluo-90   // all should end with "scanf"):
720d7dd2c46Sziqingluo-90   return Name.ends_with("scanf");
721d7dd2c46Sziqingluo-90 }
722d7dd2c46Sziqingluo-90 
723d7dd2c46Sziqingluo-90 // Match a call to one of the `v*printf` functions taking `va_list`.  We cannot
724d7dd2c46Sziqingluo-90 // check safety for these functions so they should be changed to their
725d7dd2c46Sziqingluo-90 // non-va_list versions.
726d7dd2c46Sziqingluo-90 AST_MATCHER(FunctionDecl, isUnsafeVaListPrintfFunc) {
727d7dd2c46Sziqingluo-90   auto *II = Node.getIdentifier();
728d7dd2c46Sziqingluo-90 
729d7dd2c46Sziqingluo-90   if (!II)
730d7dd2c46Sziqingluo-90     return false;
731d7dd2c46Sziqingluo-90 
732d7dd2c46Sziqingluo-90   StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
733d7dd2c46Sziqingluo-90       II->getName(), Node.getBuiltinID());
734d7dd2c46Sziqingluo-90 
735d7dd2c46Sziqingluo-90   if (!Name.ends_with("printf"))
736d7dd2c46Sziqingluo-90     return false; // neither printf nor scanf
737d7dd2c46Sziqingluo-90   return Name.starts_with("v");
738d7dd2c46Sziqingluo-90 }
739d7dd2c46Sziqingluo-90 
740d7dd2c46Sziqingluo-90 // Matches a call to one of the `sprintf` functions as they are always unsafe
741d7dd2c46Sziqingluo-90 // and should be changed to `snprintf`.
742d7dd2c46Sziqingluo-90 AST_MATCHER(FunctionDecl, isUnsafeSprintfFunc) {
743d7dd2c46Sziqingluo-90   auto *II = Node.getIdentifier();
744d7dd2c46Sziqingluo-90 
745d7dd2c46Sziqingluo-90   if (!II)
746d7dd2c46Sziqingluo-90     return false;
747d7dd2c46Sziqingluo-90 
748d7dd2c46Sziqingluo-90   StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
749d7dd2c46Sziqingluo-90       II->getName(), Node.getBuiltinID());
750d7dd2c46Sziqingluo-90 
751d7dd2c46Sziqingluo-90   if (!Name.ends_with("printf") ||
752d7dd2c46Sziqingluo-90       // Let `isUnsafeVaListPrintfFunc` check for cases with va-list:
753d7dd2c46Sziqingluo-90       Name.starts_with("v"))
754d7dd2c46Sziqingluo-90     return false;
755d7dd2c46Sziqingluo-90 
756d7dd2c46Sziqingluo-90   StringRef Prefix = Name.drop_back(6);
757d7dd2c46Sziqingluo-90 
758d7dd2c46Sziqingluo-90   if (Prefix.ends_with("w"))
759d7dd2c46Sziqingluo-90     Prefix = Prefix.drop_back(1);
760d7dd2c46Sziqingluo-90   return Prefix == "s";
761d7dd2c46Sziqingluo-90 }
762d7dd2c46Sziqingluo-90 
763d7dd2c46Sziqingluo-90 // Match function declarations of `printf`, `fprintf`, `snprintf` and their wide
764d7dd2c46Sziqingluo-90 // character versions.  Calls to these functions can be safe if their arguments
765d7dd2c46Sziqingluo-90 // are carefully made safe.
766d7dd2c46Sziqingluo-90 AST_MATCHER(FunctionDecl, isNormalPrintfFunc) {
767d7dd2c46Sziqingluo-90   auto *II = Node.getIdentifier();
768d7dd2c46Sziqingluo-90 
769d7dd2c46Sziqingluo-90   if (!II)
770d7dd2c46Sziqingluo-90     return false;
771d7dd2c46Sziqingluo-90 
772d7dd2c46Sziqingluo-90   StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
773d7dd2c46Sziqingluo-90       II->getName(), Node.getBuiltinID());
774d7dd2c46Sziqingluo-90 
775d7dd2c46Sziqingluo-90   if (!Name.ends_with("printf") || Name.starts_with("v"))
776d7dd2c46Sziqingluo-90     return false;
777d7dd2c46Sziqingluo-90 
778d7dd2c46Sziqingluo-90   StringRef Prefix = Name.drop_back(6);
779d7dd2c46Sziqingluo-90 
780d7dd2c46Sziqingluo-90   if (Prefix.ends_with("w"))
781d7dd2c46Sziqingluo-90     Prefix = Prefix.drop_back(1);
782d7dd2c46Sziqingluo-90 
783d7dd2c46Sziqingluo-90   return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
784d7dd2c46Sziqingluo-90 }
785d7dd2c46Sziqingluo-90 
786d7dd2c46Sziqingluo-90 // This matcher requires that it is known that the callee `isNormalPrintf`.
787d7dd2c46Sziqingluo-90 // Then if the format string is a string literal, this matcher matches when at
788d7dd2c46Sziqingluo-90 // least one string argument is unsafe. If the format is not a string literal,
789d7dd2c46Sziqingluo-90 // this matcher matches when at least one pointer type argument is unsafe.
790d7dd2c46Sziqingluo-90 AST_MATCHER_P(CallExpr, hasUnsafePrintfStringArg,
791d7dd2c46Sziqingluo-90               clang::ast_matchers::internal::Matcher<Expr>,
792d7dd2c46Sziqingluo-90               UnsafeStringArgMatcher) {
793de88d7dbSziqingluo-90   // Determine what printf it is by examining formal parameters:
794de88d7dbSziqingluo-90   const FunctionDecl *FD = Node.getDirectCallee();
795de88d7dbSziqingluo-90 
796de88d7dbSziqingluo-90   assert(FD && "It should have been checked that FD is non-null.");
797de88d7dbSziqingluo-90 
798de88d7dbSziqingluo-90   unsigned NumParms = FD->getNumParams();
799de88d7dbSziqingluo-90 
800de88d7dbSziqingluo-90   if (NumParms < 1)
801de88d7dbSziqingluo-90     return false; // possibly some user-defined printf function
802de88d7dbSziqingluo-90 
803d7dd2c46Sziqingluo-90   ASTContext &Ctx = Finder->getASTContext();
804090dc77aSZiqing Luo   QualType FirstParmTy = FD->getParamDecl(0)->getType();
805d7dd2c46Sziqingluo-90 
806090dc77aSZiqing Luo   if (!FirstParmTy->isPointerType())
807de88d7dbSziqingluo-90     return false; // possibly some user-defined printf function
808d7dd2c46Sziqingluo-90 
809090dc77aSZiqing Luo   QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
810d7dd2c46Sziqingluo-90 
811de88d7dbSziqingluo-90   if (!Ctx.getFILEType()
812de88d7dbSziqingluo-90            .isNull() && //`FILE *` must be in the context if it is fprintf
813de88d7dbSziqingluo-90       FirstPteTy.getCanonicalType() == Ctx.getFILEType().getCanonicalType()) {
814d7dd2c46Sziqingluo-90     // It is a fprintf:
815d7dd2c46Sziqingluo-90     const Expr *UnsafeArg;
816d7dd2c46Sziqingluo-90 
817d7dd2c46Sziqingluo-90     if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 1, Ctx, false))
818d7dd2c46Sziqingluo-90       return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
819d7dd2c46Sziqingluo-90     return false;
820d7dd2c46Sziqingluo-90   }
821d7dd2c46Sziqingluo-90 
822de88d7dbSziqingluo-90   if (FirstPteTy.isConstQualified()) {
823de88d7dbSziqingluo-90     // If the first parameter is a `const char *`, it is a printf/kprintf:
824de88d7dbSziqingluo-90     bool isKprintf = false;
825de88d7dbSziqingluo-90     const Expr *UnsafeArg;
826d7dd2c46Sziqingluo-90 
827de88d7dbSziqingluo-90     if (auto *II = FD->getIdentifier())
828de88d7dbSziqingluo-90       isKprintf = II->getName() == "kprintf";
829de88d7dbSziqingluo-90     if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 0, Ctx, isKprintf))
830de88d7dbSziqingluo-90       return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
831de88d7dbSziqingluo-90     return false;
832de88d7dbSziqingluo-90   }
833de88d7dbSziqingluo-90 
834de88d7dbSziqingluo-90   if (NumParms > 2) {
835de88d7dbSziqingluo-90     QualType SecondParmTy = FD->getParamDecl(1)->getType();
836de88d7dbSziqingluo-90 
837de88d7dbSziqingluo-90     if (!FirstPteTy.isConstQualified() && SecondParmTy->isIntegerType()) {
838de88d7dbSziqingluo-90       // If the first parameter type is non-const qualified `char *` and the
839de88d7dbSziqingluo-90       // second is an integer, it is a snprintf:
840d7dd2c46Sziqingluo-90       const Expr *UnsafeArg;
841d7dd2c46Sziqingluo-90 
842d7dd2c46Sziqingluo-90       if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 2, Ctx, false))
843d7dd2c46Sziqingluo-90         return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
844d7dd2c46Sziqingluo-90       return false;
845d7dd2c46Sziqingluo-90     }
846de88d7dbSziqingluo-90   }
847de88d7dbSziqingluo-90   // We don't really recognize this "normal" printf, the only thing we
848d7dd2c46Sziqingluo-90   // can do is to require all pointers to be null-terminated:
849d7dd2c46Sziqingluo-90   for (auto Arg : Node.arguments())
850d7dd2c46Sziqingluo-90     if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg))
851d7dd2c46Sziqingluo-90       if (UnsafeStringArgMatcher.matches(*Arg, Finder, Builder))
852d7dd2c46Sziqingluo-90         return true;
853d7dd2c46Sziqingluo-90   return false;
854d7dd2c46Sziqingluo-90 }
855d7dd2c46Sziqingluo-90 
856d7dd2c46Sziqingluo-90 // This matcher requires that it is known that the callee `isNormalPrintf`.
857d7dd2c46Sziqingluo-90 // Then it matches if the first two arguments of the call is a pointer and an
858d7dd2c46Sziqingluo-90 // integer and they are not in a safe pattern.
859d7dd2c46Sziqingluo-90 //
860d7dd2c46Sziqingluo-90 // For the first two arguments: `ptr` and `size`, they are safe if in the
861d7dd2c46Sziqingluo-90 // following patterns:
862ebf25d95SZiqing Luo //
863ebf25d95SZiqing Luo // Pattern 1:
864d7dd2c46Sziqingluo-90 //    ptr := DRE.data();
865d7dd2c46Sziqingluo-90 //    size:= DRE.size()/DRE.size_bytes()
866d7dd2c46Sziqingluo-90 // And DRE is a hardened container or view.
867ebf25d95SZiqing Luo //
868ebf25d95SZiqing Luo // Pattern 2:
869ebf25d95SZiqing Luo //    ptr := Constant-Array-DRE;
870ebf25d95SZiqing Luo //    size:= any expression that has compile-time constant value equivalent to
871ebf25d95SZiqing Luo //           sizeof (Constant-Array-DRE)
872d7dd2c46Sziqingluo-90 AST_MATCHER(CallExpr, hasUnsafeSnprintfBuffer) {
873de88d7dbSziqingluo-90   const FunctionDecl *FD = Node.getDirectCallee();
874d7dd2c46Sziqingluo-90 
875de88d7dbSziqingluo-90   assert(FD && "It should have been checked that FD is non-null.");
876de88d7dbSziqingluo-90 
877de88d7dbSziqingluo-90   if (FD->getNumParams() < 3)
878de88d7dbSziqingluo-90     return false; // Not an snprint
879de88d7dbSziqingluo-90 
880de88d7dbSziqingluo-90   QualType FirstParmTy = FD->getParamDecl(0)->getType();
881de88d7dbSziqingluo-90 
882de88d7dbSziqingluo-90   if (!FirstParmTy->isPointerType())
883de88d7dbSziqingluo-90     return false; // Not an snprint
884de88d7dbSziqingluo-90 
885090dc77aSZiqing Luo   QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
886d7dd2c46Sziqingluo-90   const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
887d7dd2c46Sziqingluo-90 
888de88d7dbSziqingluo-90   if (FirstPteTy.isConstQualified() || !Buf->getType()->isPointerType() ||
889de88d7dbSziqingluo-90       !Size->getType()->isIntegerType())
890d7dd2c46Sziqingluo-90     return false; // not an snprintf call
891d7dd2c46Sziqingluo-90 
892ebf25d95SZiqing Luo   // Pattern 1:
893d7dd2c46Sziqingluo-90   static StringRef SizedObjs[] = {"span", "array", "vector",
894d7dd2c46Sziqingluo-90                                   "basic_string_view", "basic_string"};
895d7dd2c46Sziqingluo-90   Buf = Buf->IgnoreParenImpCasts();
896d7dd2c46Sziqingluo-90   Size = Size->IgnoreParenImpCasts();
897d7dd2c46Sziqingluo-90   if (auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Buf))
898d7dd2c46Sziqingluo-90     if (auto *MCESize = dyn_cast<CXXMemberCallExpr>(Size)) {
899d7dd2c46Sziqingluo-90       auto *DREOfPtr = dyn_cast<DeclRefExpr>(
900d7dd2c46Sziqingluo-90           MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
901d7dd2c46Sziqingluo-90       auto *DREOfSize = dyn_cast<DeclRefExpr>(
902d7dd2c46Sziqingluo-90           MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
903d7dd2c46Sziqingluo-90 
904d7dd2c46Sziqingluo-90       if (!DREOfPtr || !DREOfSize)
905d7dd2c46Sziqingluo-90         return true; // not in safe pattern
906d7dd2c46Sziqingluo-90       if (DREOfPtr->getDecl() != DREOfSize->getDecl())
907d7dd2c46Sziqingluo-90         return true; // not in safe pattern
908d7dd2c46Sziqingluo-90       if (MCEPtr->getMethodDecl()->getName() != "data")
909d7dd2c46Sziqingluo-90         return true; // not in safe pattern
910d7dd2c46Sziqingluo-90 
911d7dd2c46Sziqingluo-90       if (MCESize->getMethodDecl()->getName() == "size_bytes" ||
912d7dd2c46Sziqingluo-90           // Note here the pointer must be a pointer-to-char type unless there
913d7dd2c46Sziqingluo-90           // is explicit casting.  If there is explicit casting, this branch
914d7dd2c46Sziqingluo-90           // is unreachable. Thus, at this branch "size" and "size_bytes" are
915d7dd2c46Sziqingluo-90           // equivalent as the pointer is a char pointer:
916d7dd2c46Sziqingluo-90           MCESize->getMethodDecl()->getName() == "size")
917d7dd2c46Sziqingluo-90         for (StringRef SizedObj : SizedObjs)
918d7dd2c46Sziqingluo-90           if (MCEPtr->getRecordDecl()->isInStdNamespace() &&
919d7dd2c46Sziqingluo-90               MCEPtr->getRecordDecl()->getCanonicalDecl()->getName() ==
920d7dd2c46Sziqingluo-90                   SizedObj)
921d7dd2c46Sziqingluo-90             return false; // It is in fact safe
922d7dd2c46Sziqingluo-90     }
923ebf25d95SZiqing Luo 
924ebf25d95SZiqing Luo   // Pattern 2:
925ebf25d95SZiqing Luo   if (auto *DRE = dyn_cast<DeclRefExpr>(Buf->IgnoreParenImpCasts())) {
926ebf25d95SZiqing Luo     ASTContext &Ctx = Finder->getASTContext();
927ebf25d95SZiqing Luo 
928ebf25d95SZiqing Luo     if (auto *CAT = Ctx.getAsConstantArrayType(DRE->getType())) {
929ebf25d95SZiqing Luo       Expr::EvalResult ER;
930ebf25d95SZiqing Luo       // The array element type must be compatible with `char` otherwise an
931ebf25d95SZiqing Luo       // explicit cast will be needed, which will make this check unreachable.
932ebf25d95SZiqing Luo       // Therefore, the array extent is same as its' bytewise size.
933ebf25d95SZiqing Luo       if (Size->EvaluateAsConstantExpr(ER, Ctx)) {
934ebf25d95SZiqing Luo         APSInt EVal = ER.Val.getInt(); // Size must have integer type
935ebf25d95SZiqing Luo 
936ebf25d95SZiqing Luo         return APSInt::compareValues(EVal, APSInt(CAT->getSize(), true)) != 0;
937ebf25d95SZiqing Luo       }
938ebf25d95SZiqing Luo     }
939ebf25d95SZiqing Luo   }
940d7dd2c46Sziqingluo-90   return true; // ptr and size are not in safe pattern
941d7dd2c46Sziqingluo-90 }
942d7dd2c46Sziqingluo-90 } // namespace libc_func_matchers
9437d0d34fbSziqingluo-90 } // namespace clang::ast_matchers
9447d0d34fbSziqingluo-90 
9458086323aSArtem Dergachev namespace {
9468086323aSArtem Dergachev // Because the analysis revolves around variables and their types, we'll need to
9478086323aSArtem Dergachev // track uses of variables (aka DeclRefExprs).
9488086323aSArtem Dergachev using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
9498086323aSArtem Dergachev 
9508086323aSArtem Dergachev // Convenience typedef.
9518086323aSArtem Dergachev using FixItList = SmallVector<FixItHint, 4>;
9528086323aSArtem Dergachev } // namespace
9538086323aSArtem Dergachev 
9540d00a972SArtem Dergachev namespace {
9550d00a972SArtem Dergachev /// Gadget is an individual operation in the code that may be of interest to
9560d00a972SArtem Dergachev /// this analysis. Each (non-abstract) subclass corresponds to a specific
9570d00a972SArtem Dergachev /// rigid AST structure that constitutes an operation on a pointer-type object.
9580d00a972SArtem Dergachev /// Discovery of a gadget in the code corresponds to claiming that we understand
9590d00a972SArtem Dergachev /// what this part of code is doing well enough to potentially improve it.
96050d4a1f7SMalavikaSamak /// Gadgets can be warning (immediately deserving a warning) or fixable (not
96150d4a1f7SMalavikaSamak /// always deserving a warning per se, but requires our attention to identify
96250d4a1f7SMalavikaSamak /// it warrants a fixit).
9630d00a972SArtem Dergachev class Gadget {
9640d00a972SArtem Dergachev public:
9650d00a972SArtem Dergachev   enum class Kind {
9660d00a972SArtem Dergachev #define GADGET(x) x,
9670d00a972SArtem Dergachev #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
9680d00a972SArtem Dergachev   };
9690d00a972SArtem Dergachev 
9700d00a972SArtem Dergachev   /// Common type of ASTMatchers used for discovering gadgets.
9710d00a972SArtem Dergachev   /// Useful for implementing the static matcher() methods
9720d00a972SArtem Dergachev   /// that are expected from all non-abstract subclasses.
9730d00a972SArtem Dergachev   using Matcher = decltype(stmt());
9740d00a972SArtem Dergachev 
9750d00a972SArtem Dergachev   Gadget(Kind K) : K(K) {}
9760d00a972SArtem Dergachev 
9770d00a972SArtem Dergachev   Kind getKind() const { return K; }
9780d00a972SArtem Dergachev 
979a6ae740eSRashmi Mudduluru #ifndef NDEBUG
980a6ae740eSRashmi Mudduluru   StringRef getDebugName() const {
981a6ae740eSRashmi Mudduluru     switch (K) {
9823fa91021Sjkorous-apple #define GADGET(x)                                                              \
9833fa91021Sjkorous-apple   case Kind::x:                                                                \
9843fa91021Sjkorous-apple     return #x;
985a6ae740eSRashmi Mudduluru #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
986a6ae740eSRashmi Mudduluru     }
9874cd7d8e3SSimon Pilgrim     llvm_unreachable("Unhandled Gadget::Kind enum");
988a6ae740eSRashmi Mudduluru   }
989a6ae740eSRashmi Mudduluru #endif
990a6ae740eSRashmi Mudduluru 
99150d4a1f7SMalavikaSamak   virtual bool isWarningGadget() const = 0;
9925ac34358SDana Jansens   // TODO remove this method from WarningGadget interface. It's only used for
9935ac34358SDana Jansens   // debug prints in FixableGadget.
9945ac34358SDana Jansens   virtual SourceLocation getSourceLoc() const = 0;
9950d00a972SArtem Dergachev 
9968086323aSArtem Dergachev   /// Returns the list of pointer-type variables on which this gadget performs
9978086323aSArtem Dergachev   /// its operation. Typically, there's only one variable. This isn't a list
9988086323aSArtem Dergachev   /// of all DeclRefExprs in the gadget's AST!
9998086323aSArtem Dergachev   virtual DeclUseList getClaimedVarUseSites() const = 0;
10008086323aSArtem Dergachev 
10010d00a972SArtem Dergachev   virtual ~Gadget() = default;
10020d00a972SArtem Dergachev 
10030d00a972SArtem Dergachev private:
10040d00a972SArtem Dergachev   Kind K;
10050d00a972SArtem Dergachev };
10060d00a972SArtem Dergachev 
100750d4a1f7SMalavikaSamak /// Warning gadgets correspond to unsafe code patterns that warrants
10080d00a972SArtem Dergachev /// an immediate warning.
100950d4a1f7SMalavikaSamak class WarningGadget : public Gadget {
10100d00a972SArtem Dergachev public:
101150d4a1f7SMalavikaSamak   WarningGadget(Kind K) : Gadget(K) {}
10120d00a972SArtem Dergachev 
101350d4a1f7SMalavikaSamak   static bool classof(const Gadget *G) { return G->isWarningGadget(); }
101450d4a1f7SMalavikaSamak   bool isWarningGadget() const final { return true; }
10155ac34358SDana Jansens 
10165ac34358SDana Jansens   virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
10175ac34358SDana Jansens                                      bool IsRelatedToDecl,
10185ac34358SDana Jansens                                      ASTContext &Ctx) const = 0;
10190d00a972SArtem Dergachev };
10200d00a972SArtem Dergachev 
10213fa91021Sjkorous-apple /// Fixable gadgets correspond to code patterns that aren't always unsafe but
10223fa91021Sjkorous-apple /// need to be properly recognized in order to emit fixes. For example, if a raw
10233fa91021Sjkorous-apple /// pointer-type variable is replaced by a safe C++ container, every use of such
10243fa91021Sjkorous-apple /// variable must be carefully considered and possibly updated.
102550d4a1f7SMalavikaSamak class FixableGadget : public Gadget {
10260d00a972SArtem Dergachev public:
102750d4a1f7SMalavikaSamak   FixableGadget(Kind K) : Gadget(K) {}
10280d00a972SArtem Dergachev 
102950d4a1f7SMalavikaSamak   static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
103050d4a1f7SMalavikaSamak   bool isWarningGadget() const final { return false; }
103150d4a1f7SMalavikaSamak 
103250d4a1f7SMalavikaSamak   /// Returns a fixit that would fix the current gadget according to
1033e955e4fbSKazu Hirata   /// the current strategy. Returns std::nullopt if the fix cannot be produced;
103450d4a1f7SMalavikaSamak   /// returns an empty list if no fixes are necessary.
1035644ac2a0Sjkorous-apple   virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
103687fe37d9SMalavikaSamak     return std::nullopt;
103750d4a1f7SMalavikaSamak   }
1038171dfc54SRashmi Mudduluru 
10393fa91021Sjkorous-apple   /// Returns a list of two elements where the first element is the LHS of a
10403fa91021Sjkorous-apple   /// pointer assignment statement and the second element is the RHS. This
10413fa91021Sjkorous-apple   /// two-element list represents the fact that the LHS buffer gets its bounds
10423fa91021Sjkorous-apple   /// information from the RHS buffer. This information will be used later to
10433fa91021Sjkorous-apple   /// group all those variables whose types must be modified together to prevent
10443fa91021Sjkorous-apple   /// type mismatches.
1045171dfc54SRashmi Mudduluru   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1046171dfc54SRashmi Mudduluru   getStrategyImplications() const {
1047171dfc54SRashmi Mudduluru     return std::nullopt;
1048171dfc54SRashmi Mudduluru   }
10490d00a972SArtem Dergachev };
10500d00a972SArtem Dergachev 
10513fa91021Sjkorous-apple static auto toSupportedVariable() { return to(varDecl()); }
10522afcda69SRashmi Mudduluru 
105350d4a1f7SMalavikaSamak using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
105450d4a1f7SMalavikaSamak using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
105550d4a1f7SMalavikaSamak 
10560d00a972SArtem Dergachev /// An increment of a pointer-type value is unsafe as it may run the pointer
10570d00a972SArtem Dergachev /// out of bounds.
105850d4a1f7SMalavikaSamak class IncrementGadget : public WarningGadget {
10590d00a972SArtem Dergachev   static constexpr const char *const OpTag = "op";
10600d00a972SArtem Dergachev   const UnaryOperator *Op;
10610d00a972SArtem Dergachev 
10620d00a972SArtem Dergachev public:
10630d00a972SArtem Dergachev   IncrementGadget(const MatchFinder::MatchResult &Result)
106450d4a1f7SMalavikaSamak       : WarningGadget(Kind::Increment),
10650d00a972SArtem Dergachev         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
10660d00a972SArtem Dergachev 
10670d00a972SArtem Dergachev   static bool classof(const Gadget *G) {
10680d00a972SArtem Dergachev     return G->getKind() == Kind::Increment;
10690d00a972SArtem Dergachev   }
10700d00a972SArtem Dergachev 
10710d00a972SArtem Dergachev   static Matcher matcher() {
10723fa91021Sjkorous-apple     return stmt(
10733fa91021Sjkorous-apple         unaryOperator(hasOperatorName("++"),
10743fa91021Sjkorous-apple                       hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
10753fa91021Sjkorous-apple             .bind(OpTag));
10760d00a972SArtem Dergachev   }
10770d00a972SArtem Dergachev 
10785ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
10795ac34358SDana Jansens                              bool IsRelatedToDecl,
10805ac34358SDana Jansens                              ASTContext &Ctx) const override {
10815ac34358SDana Jansens     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
10825ac34358SDana Jansens   }
10835ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
10848086323aSArtem Dergachev 
10858086323aSArtem Dergachev   DeclUseList getClaimedVarUseSites() const override {
10868086323aSArtem Dergachev     SmallVector<const DeclRefExpr *, 2> Uses;
10878086323aSArtem Dergachev     if (const auto *DRE =
10888086323aSArtem Dergachev             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
10898086323aSArtem Dergachev       Uses.push_back(DRE);
10908086323aSArtem Dergachev     }
10918086323aSArtem Dergachev 
10928086323aSArtem Dergachev     return std::move(Uses);
10938086323aSArtem Dergachev   }
10940d00a972SArtem Dergachev };
10950d00a972SArtem Dergachev 
10960d00a972SArtem Dergachev /// A decrement of a pointer-type value is unsafe as it may run the pointer
10970d00a972SArtem Dergachev /// out of bounds.
109850d4a1f7SMalavikaSamak class DecrementGadget : public WarningGadget {
10990d00a972SArtem Dergachev   static constexpr const char *const OpTag = "op";
11000d00a972SArtem Dergachev   const UnaryOperator *Op;
11010d00a972SArtem Dergachev 
11020d00a972SArtem Dergachev public:
11030d00a972SArtem Dergachev   DecrementGadget(const MatchFinder::MatchResult &Result)
110450d4a1f7SMalavikaSamak       : WarningGadget(Kind::Decrement),
11050d00a972SArtem Dergachev         Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
11060d00a972SArtem Dergachev 
11070d00a972SArtem Dergachev   static bool classof(const Gadget *G) {
11080d00a972SArtem Dergachev     return G->getKind() == Kind::Decrement;
11090d00a972SArtem Dergachev   }
11100d00a972SArtem Dergachev 
11110d00a972SArtem Dergachev   static Matcher matcher() {
11123fa91021Sjkorous-apple     return stmt(
11133fa91021Sjkorous-apple         unaryOperator(hasOperatorName("--"),
11143fa91021Sjkorous-apple                       hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
11153fa91021Sjkorous-apple             .bind(OpTag));
11160d00a972SArtem Dergachev   }
11170d00a972SArtem Dergachev 
11185ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
11195ac34358SDana Jansens                              bool IsRelatedToDecl,
11205ac34358SDana Jansens                              ASTContext &Ctx) const override {
11215ac34358SDana Jansens     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
11225ac34358SDana Jansens   }
11235ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
11248086323aSArtem Dergachev 
11258086323aSArtem Dergachev   DeclUseList getClaimedVarUseSites() const override {
11268086323aSArtem Dergachev     if (const auto *DRE =
11278086323aSArtem Dergachev             dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
11288086323aSArtem Dergachev       return {DRE};
11298086323aSArtem Dergachev     }
11308086323aSArtem Dergachev 
11318086323aSArtem Dergachev     return {};
11328086323aSArtem Dergachev   }
11330d00a972SArtem Dergachev };
11346d1d055fSziqingluo-90 
11356d1d055fSziqingluo-90 /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
11366d1d055fSziqingluo-90 /// it doesn't have any bounds checks for the array.
113750d4a1f7SMalavikaSamak class ArraySubscriptGadget : public WarningGadget {
1138bdf4f2beSZiqing Luo   static constexpr const char *const ArraySubscrTag = "ArraySubscript";
11396d1d055fSziqingluo-90   const ArraySubscriptExpr *ASE;
11406d1d055fSziqingluo-90 
11416d1d055fSziqingluo-90 public:
11426d1d055fSziqingluo-90   ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
114350d4a1f7SMalavikaSamak       : WarningGadget(Kind::ArraySubscript),
11446d1d055fSziqingluo-90         ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
11456d1d055fSziqingluo-90 
11466d1d055fSziqingluo-90   static bool classof(const Gadget *G) {
11476d1d055fSziqingluo-90     return G->getKind() == Kind::ArraySubscript;
11486d1d055fSziqingluo-90   }
11496d1d055fSziqingluo-90 
11506d1d055fSziqingluo-90   static Matcher matcher() {
1151fe93da22SRashmi Mudduluru     // clang-format off
1152fe93da22SRashmi Mudduluru       return stmt(arraySubscriptExpr(
1153fe93da22SRashmi Mudduluru             hasBase(ignoringParenImpCasts(
1154fe93da22SRashmi Mudduluru               anyOf(hasPointerType(), hasArrayType()))),
11559a1e6373Sjkorous-apple             unless(anyOf(
11569a1e6373Sjkorous-apple               isSafeArraySubscript(),
11579a1e6373Sjkorous-apple               hasIndex(
115827c10337SRashmi Mudduluru                   anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
11599a1e6373Sjkorous-apple               )
11609a1e6373Sjkorous-apple             ))).bind(ArraySubscrTag));
1161fe93da22SRashmi Mudduluru     // clang-format on
11626d1d055fSziqingluo-90   }
11636d1d055fSziqingluo-90 
11645ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
11655ac34358SDana Jansens                              bool IsRelatedToDecl,
11665ac34358SDana Jansens                              ASTContext &Ctx) const override {
11675ac34358SDana Jansens     Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
11685ac34358SDana Jansens   }
11695ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
11708086323aSArtem Dergachev 
11718086323aSArtem Dergachev   DeclUseList getClaimedVarUseSites() const override {
11728086323aSArtem Dergachev     if (const auto *DRE =
11738086323aSArtem Dergachev             dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
11748086323aSArtem Dergachev       return {DRE};
11758086323aSArtem Dergachev     }
11768086323aSArtem Dergachev 
11778086323aSArtem Dergachev     return {};
11788086323aSArtem Dergachev   }
11796d1d055fSziqingluo-90 };
1180f84f17c4SZiqing Luo 
1181f84f17c4SZiqing Luo /// A pointer arithmetic expression of one of the forms:
1182f84f17c4SZiqing Luo ///  \code
1183f84f17c4SZiqing Luo ///  ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
1184f84f17c4SZiqing Luo ///  \endcode
118550d4a1f7SMalavikaSamak class PointerArithmeticGadget : public WarningGadget {
1186f84f17c4SZiqing Luo   static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1187f84f17c4SZiqing Luo   static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1188f84f17c4SZiqing Luo   const BinaryOperator *PA; // pointer arithmetic expression
1189f84f17c4SZiqing Luo   const Expr *Ptr;          // the pointer expression in `PA`
1190f84f17c4SZiqing Luo 
1191f84f17c4SZiqing Luo public:
1192f84f17c4SZiqing Luo   PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
119350d4a1f7SMalavikaSamak       : WarningGadget(Kind::PointerArithmetic),
1194f84f17c4SZiqing Luo         PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1195f84f17c4SZiqing Luo         Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1196f84f17c4SZiqing Luo 
1197f84f17c4SZiqing Luo   static bool classof(const Gadget *G) {
1198f84f17c4SZiqing Luo     return G->getKind() == Kind::PointerArithmetic;
1199f84f17c4SZiqing Luo   }
1200f84f17c4SZiqing Luo 
1201f84f17c4SZiqing Luo   static Matcher matcher() {
1202148dc8a2Sziqingluo-90     auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
1203148dc8a2Sziqingluo-90     auto PtrAtRight =
1204148dc8a2Sziqingluo-90         allOf(hasOperatorName("+"),
1205f84f17c4SZiqing Luo               hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
1206f84f17c4SZiqing Luo               hasLHS(HasIntegerType));
1207148dc8a2Sziqingluo-90     auto PtrAtLeft =
1208148dc8a2Sziqingluo-90         allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
1209f84f17c4SZiqing Luo                     hasOperatorName("+="), hasOperatorName("-=")),
1210f84f17c4SZiqing Luo               hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
1211f84f17c4SZiqing Luo               hasRHS(HasIntegerType));
1212f84f17c4SZiqing Luo 
1213148dc8a2Sziqingluo-90     return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight))
1214148dc8a2Sziqingluo-90                     .bind(PointerArithmeticTag));
1215f84f17c4SZiqing Luo   }
1216f84f17c4SZiqing Luo 
12175ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
12185ac34358SDana Jansens                              bool IsRelatedToDecl,
12195ac34358SDana Jansens                              ASTContext &Ctx) const override {
12205ac34358SDana Jansens     Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
12215ac34358SDana Jansens   }
12225ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
1223f84f17c4SZiqing Luo 
1224f84f17c4SZiqing Luo   DeclUseList getClaimedVarUseSites() const override {
1225148dc8a2Sziqingluo-90     if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1226f84f17c4SZiqing Luo       return {DRE};
1227f84f17c4SZiqing Luo     }
1228f84f17c4SZiqing Luo 
1229f84f17c4SZiqing Luo     return {};
1230f84f17c4SZiqing Luo   }
1231f84f17c4SZiqing Luo   // FIXME: pointer adding zero should be fine
1232f84f17c4SZiqing Luo   // FIXME: this gadge will need a fix-it
1233f84f17c4SZiqing Luo };
1234acc3cc69SRashmi Mudduluru 
12359816863dSZiqing Luo class SpanTwoParamConstructorGadget : public WarningGadget {
12369816863dSZiqing Luo   static constexpr const char *const SpanTwoParamConstructorTag =
12379816863dSZiqing Luo       "spanTwoParamConstructor";
12389816863dSZiqing Luo   const CXXConstructExpr *Ctor; // the span constructor expression
12399816863dSZiqing Luo 
12409816863dSZiqing Luo public:
12419816863dSZiqing Luo   SpanTwoParamConstructorGadget(const MatchFinder::MatchResult &Result)
12429816863dSZiqing Luo       : WarningGadget(Kind::SpanTwoParamConstructor),
12439816863dSZiqing Luo         Ctor(Result.Nodes.getNodeAs<CXXConstructExpr>(
12449816863dSZiqing Luo             SpanTwoParamConstructorTag)) {}
12459816863dSZiqing Luo 
12469816863dSZiqing Luo   static bool classof(const Gadget *G) {
12479816863dSZiqing Luo     return G->getKind() == Kind::SpanTwoParamConstructor;
12489816863dSZiqing Luo   }
12499816863dSZiqing Luo 
12509816863dSZiqing Luo   static Matcher matcher() {
12519816863dSZiqing Luo     auto HasTwoParamSpanCtorDecl = hasDeclaration(
12529816863dSZiqing Luo         cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"),
12539816863dSZiqing Luo                            parameterCountIs(2)));
12549816863dSZiqing Luo 
12559816863dSZiqing Luo     return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl,
12569816863dSZiqing Luo                                  unless(isSafeSpanTwoParamConstruct()))
12579816863dSZiqing Luo                     .bind(SpanTwoParamConstructorTag));
12589816863dSZiqing Luo   }
12599816863dSZiqing Luo 
1260d7dd2c46Sziqingluo-90   static Matcher matcher(const UnsafeBufferUsageHandler *Handler) {
1261d7dd2c46Sziqingluo-90     return stmt(unless(ignoreUnsafeBufferInContainer(Handler)), matcher());
1262d7dd2c46Sziqingluo-90   }
1263d7dd2c46Sziqingluo-90 
12645ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
12655ac34358SDana Jansens                              bool IsRelatedToDecl,
12665ac34358SDana Jansens                              ASTContext &Ctx) const override {
12675ac34358SDana Jansens     Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
12685ac34358SDana Jansens   }
12695ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
12709816863dSZiqing Luo 
12719816863dSZiqing Luo   DeclUseList getClaimedVarUseSites() const override {
12729816863dSZiqing Luo     // If the constructor call is of the form `std::span{var, n}`, `var` is
12739816863dSZiqing Luo     // considered an unsafe variable.
12749816863dSZiqing Luo     if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
12759816863dSZiqing Luo       if (isa<VarDecl>(DRE->getDecl()))
12769816863dSZiqing Luo         return {DRE};
12779816863dSZiqing Luo     }
12789816863dSZiqing Luo     return {};
12799816863dSZiqing Luo   }
12809816863dSZiqing Luo };
12819816863dSZiqing Luo 
1282db3dcedbSRashmi Mudduluru /// A pointer initialization expression of the form:
1283db3dcedbSRashmi Mudduluru ///  \code
1284db3dcedbSRashmi Mudduluru ///  int *p = q;
1285db3dcedbSRashmi Mudduluru ///  \endcode
1286db3dcedbSRashmi Mudduluru class PointerInitGadget : public FixableGadget {
1287db3dcedbSRashmi Mudduluru private:
1288db3dcedbSRashmi Mudduluru   static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1289db3dcedbSRashmi Mudduluru   static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1290db3dcedbSRashmi Mudduluru   const VarDecl *PtrInitLHS;     // the LHS pointer expression in `PI`
1291db3dcedbSRashmi Mudduluru   const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
1292db3dcedbSRashmi Mudduluru 
1293db3dcedbSRashmi Mudduluru public:
1294db3dcedbSRashmi Mudduluru   PointerInitGadget(const MatchFinder::MatchResult &Result)
1295db3dcedbSRashmi Mudduluru       : FixableGadget(Kind::PointerInit),
1296db3dcedbSRashmi Mudduluru         PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
1297db3dcedbSRashmi Mudduluru         PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
1298db3dcedbSRashmi Mudduluru 
1299db3dcedbSRashmi Mudduluru   static bool classof(const Gadget *G) {
1300db3dcedbSRashmi Mudduluru     return G->getKind() == Kind::PointerInit;
1301db3dcedbSRashmi Mudduluru   }
1302db3dcedbSRashmi Mudduluru 
1303db3dcedbSRashmi Mudduluru   static Matcher matcher() {
13043fa91021Sjkorous-apple     auto PtrInitStmt = declStmt(hasSingleDecl(
13053fa91021Sjkorous-apple         varDecl(hasInitializer(ignoringImpCasts(
13063fa91021Sjkorous-apple                     declRefExpr(hasPointerType(), toSupportedVariable())
13073fa91021Sjkorous-apple                         .bind(PointerInitRHSTag))))
13083fa91021Sjkorous-apple             .bind(PointerInitLHSTag)));
1309db3dcedbSRashmi Mudduluru 
1310db3dcedbSRashmi Mudduluru     return stmt(PtrInitStmt);
1311db3dcedbSRashmi Mudduluru   }
1312db3dcedbSRashmi Mudduluru 
1313644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1314644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
13155ac34358SDana Jansens   SourceLocation getSourceLoc() const override {
13165ac34358SDana Jansens     return PtrInitRHS->getBeginLoc();
1317a6ae740eSRashmi Mudduluru   }
1318db3dcedbSRashmi Mudduluru 
1319db3dcedbSRashmi Mudduluru   virtual DeclUseList getClaimedVarUseSites() const override {
1320db3dcedbSRashmi Mudduluru     return DeclUseList{PtrInitRHS};
1321db3dcedbSRashmi Mudduluru   }
1322db3dcedbSRashmi Mudduluru 
1323db3dcedbSRashmi Mudduluru   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1324db3dcedbSRashmi Mudduluru   getStrategyImplications() const override {
13253fa91021Sjkorous-apple     return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
1326db3dcedbSRashmi Mudduluru   }
1327db3dcedbSRashmi Mudduluru };
1328db3dcedbSRashmi Mudduluru 
1329171dfc54SRashmi Mudduluru /// A pointer assignment expression of the form:
1330171dfc54SRashmi Mudduluru ///  \code
1331171dfc54SRashmi Mudduluru ///  p = q;
1332171dfc54SRashmi Mudduluru ///  \endcode
13336fce42f8Sjkorous-apple /// where both `p` and `q` are pointers.
13346fce42f8Sjkorous-apple class PtrToPtrAssignmentGadget : public FixableGadget {
1335171dfc54SRashmi Mudduluru private:
1336171dfc54SRashmi Mudduluru   static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1337171dfc54SRashmi Mudduluru   static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1338171dfc54SRashmi Mudduluru   const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1339171dfc54SRashmi Mudduluru   const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1340171dfc54SRashmi Mudduluru 
1341171dfc54SRashmi Mudduluru public:
13426fce42f8Sjkorous-apple   PtrToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
13436fce42f8Sjkorous-apple       : FixableGadget(Kind::PtrToPtrAssignment),
1344171dfc54SRashmi Mudduluru         PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1345171dfc54SRashmi Mudduluru         PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1346171dfc54SRashmi Mudduluru 
1347171dfc54SRashmi Mudduluru   static bool classof(const Gadget *G) {
13486fce42f8Sjkorous-apple     return G->getKind() == Kind::PtrToPtrAssignment;
1349171dfc54SRashmi Mudduluru   }
1350171dfc54SRashmi Mudduluru 
1351171dfc54SRashmi Mudduluru   static Matcher matcher() {
13523fa91021Sjkorous-apple     auto PtrAssignExpr = binaryOperator(
13533fa91021Sjkorous-apple         allOf(hasOperatorName("="),
13543fa91021Sjkorous-apple               hasRHS(ignoringParenImpCasts(
13553fa91021Sjkorous-apple                   declRefExpr(hasPointerType(), toSupportedVariable())
13563fa91021Sjkorous-apple                       .bind(PointerAssignRHSTag))),
13573fa91021Sjkorous-apple               hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
13583fa91021Sjkorous-apple                          .bind(PointerAssignLHSTag))));
1359171dfc54SRashmi Mudduluru 
1360171dfc54SRashmi Mudduluru     return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
1361171dfc54SRashmi Mudduluru   }
1362171dfc54SRashmi Mudduluru 
1363644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1364644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
13655ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1366171dfc54SRashmi Mudduluru 
1367171dfc54SRashmi Mudduluru   virtual DeclUseList getClaimedVarUseSites() const override {
1368171dfc54SRashmi Mudduluru     return DeclUseList{PtrLHS, PtrRHS};
1369171dfc54SRashmi Mudduluru   }
1370171dfc54SRashmi Mudduluru 
1371171dfc54SRashmi Mudduluru   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1372171dfc54SRashmi Mudduluru   getStrategyImplications() const override {
1373171dfc54SRashmi Mudduluru     return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
1374171dfc54SRashmi Mudduluru                           cast<VarDecl>(PtrRHS->getDecl()));
1375171dfc54SRashmi Mudduluru   }
1376171dfc54SRashmi Mudduluru };
1377171dfc54SRashmi Mudduluru 
13786fce42f8Sjkorous-apple /// An assignment expression of the form:
13796fce42f8Sjkorous-apple ///  \code
13806fce42f8Sjkorous-apple ///  ptr = array;
13816fce42f8Sjkorous-apple ///  \endcode
13826fce42f8Sjkorous-apple /// where `p` is a pointer and `array` is a constant size array.
13836fce42f8Sjkorous-apple class CArrayToPtrAssignmentGadget : public FixableGadget {
13846fce42f8Sjkorous-apple private:
13856fce42f8Sjkorous-apple   static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
13866fce42f8Sjkorous-apple   static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
13876fce42f8Sjkorous-apple   const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
13886fce42f8Sjkorous-apple   const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
13896fce42f8Sjkorous-apple 
13906fce42f8Sjkorous-apple public:
13916fce42f8Sjkorous-apple   CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
13926fce42f8Sjkorous-apple       : FixableGadget(Kind::CArrayToPtrAssignment),
13936fce42f8Sjkorous-apple         PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
13946fce42f8Sjkorous-apple         PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
13956fce42f8Sjkorous-apple 
13966fce42f8Sjkorous-apple   static bool classof(const Gadget *G) {
13976fce42f8Sjkorous-apple     return G->getKind() == Kind::CArrayToPtrAssignment;
13986fce42f8Sjkorous-apple   }
13996fce42f8Sjkorous-apple 
14006fce42f8Sjkorous-apple   static Matcher matcher() {
14016fce42f8Sjkorous-apple     auto PtrAssignExpr = binaryOperator(
14026fce42f8Sjkorous-apple         allOf(hasOperatorName("="),
14036fce42f8Sjkorous-apple               hasRHS(ignoringParenImpCasts(
14046fce42f8Sjkorous-apple                   declRefExpr(hasType(hasCanonicalType(constantArrayType())),
14056fce42f8Sjkorous-apple                               toSupportedVariable())
14066fce42f8Sjkorous-apple                       .bind(PointerAssignRHSTag))),
14076fce42f8Sjkorous-apple               hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
14086fce42f8Sjkorous-apple                          .bind(PointerAssignLHSTag))));
14096fce42f8Sjkorous-apple 
14106fce42f8Sjkorous-apple     return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
14116fce42f8Sjkorous-apple   }
14126fce42f8Sjkorous-apple 
14136fce42f8Sjkorous-apple   virtual std::optional<FixItList>
14146fce42f8Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
14155ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
14166fce42f8Sjkorous-apple 
14176fce42f8Sjkorous-apple   virtual DeclUseList getClaimedVarUseSites() const override {
14186fce42f8Sjkorous-apple     return DeclUseList{PtrLHS, PtrRHS};
14196fce42f8Sjkorous-apple   }
14206fce42f8Sjkorous-apple 
14216fce42f8Sjkorous-apple   virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
14226fce42f8Sjkorous-apple   getStrategyImplications() const override {
14236fce42f8Sjkorous-apple     return {};
14246fce42f8Sjkorous-apple   }
14256fce42f8Sjkorous-apple };
14266fce42f8Sjkorous-apple 
1427acc3cc69SRashmi Mudduluru /// A call of a function or method that performs unchecked buffer operations
1428acc3cc69SRashmi Mudduluru /// over one of its pointer parameters.
1429acc3cc69SRashmi Mudduluru class UnsafeBufferUsageAttrGadget : public WarningGadget {
14306b652f6eSMalavika Samak   constexpr static const char *const OpTag = "attr_expr";
14316b652f6eSMalavika Samak   const Expr *Op;
1432acc3cc69SRashmi Mudduluru 
1433acc3cc69SRashmi Mudduluru public:
1434acc3cc69SRashmi Mudduluru   UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
1435acc3cc69SRashmi Mudduluru       : WarningGadget(Kind::UnsafeBufferUsageAttr),
14366b652f6eSMalavika Samak         Op(Result.Nodes.getNodeAs<Expr>(OpTag)) {}
1437acc3cc69SRashmi Mudduluru 
1438acc3cc69SRashmi Mudduluru   static bool classof(const Gadget *G) {
1439acc3cc69SRashmi Mudduluru     return G->getKind() == Kind::UnsafeBufferUsageAttr;
1440acc3cc69SRashmi Mudduluru   }
1441acc3cc69SRashmi Mudduluru 
1442acc3cc69SRashmi Mudduluru   static Matcher matcher() {
14436b652f6eSMalavika Samak     auto HasUnsafeFieldDecl =
14446b652f6eSMalavika Samak         member(fieldDecl(hasAttr(attr::UnsafeBufferUsage)));
14456b652f6eSMalavika Samak 
14465ac34358SDana Jansens     auto HasUnsafeFnDecl =
14475ac34358SDana Jansens         callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)));
14486b652f6eSMalavika Samak 
14496b652f6eSMalavika Samak     return stmt(anyOf(callExpr(HasUnsafeFnDecl).bind(OpTag),
14506b652f6eSMalavika Samak                       memberExpr(HasUnsafeFieldDecl).bind(OpTag)));
14515ac34358SDana Jansens   }
14525ac34358SDana Jansens 
14535ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
14545ac34358SDana Jansens                              bool IsRelatedToDecl,
14555ac34358SDana Jansens                              ASTContext &Ctx) const override {
14565ac34358SDana Jansens     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
14575ac34358SDana Jansens   }
14585ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
14595ac34358SDana Jansens 
14605ac34358SDana Jansens   DeclUseList getClaimedVarUseSites() const override { return {}; }
14615ac34358SDana Jansens };
14625ac34358SDana Jansens 
14635ac34358SDana Jansens /// A call of a constructor that performs unchecked buffer operations
14645ac34358SDana Jansens /// over one of its pointer parameters, or constructs a class object that will
14655ac34358SDana Jansens /// perform buffer operations that depend on the correctness of the parameters.
14665ac34358SDana Jansens class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
14675ac34358SDana Jansens   constexpr static const char *const OpTag = "cxx_construct_expr";
14685ac34358SDana Jansens   const CXXConstructExpr *Op;
14695ac34358SDana Jansens 
14705ac34358SDana Jansens public:
14715ac34358SDana Jansens   UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult &Result)
14725ac34358SDana Jansens       : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
14735ac34358SDana Jansens         Op(Result.Nodes.getNodeAs<CXXConstructExpr>(OpTag)) {}
14745ac34358SDana Jansens 
14755ac34358SDana Jansens   static bool classof(const Gadget *G) {
14765ac34358SDana Jansens     return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
14775ac34358SDana Jansens   }
14785ac34358SDana Jansens 
14795ac34358SDana Jansens   static Matcher matcher() {
14805ac34358SDana Jansens     auto HasUnsafeCtorDecl =
14815ac34358SDana Jansens         hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage)));
14825ac34358SDana Jansens     // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
14835ac34358SDana Jansens     auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();
14845ac34358SDana Jansens     return stmt(
14855ac34358SDana Jansens         cxxConstructExpr(HasUnsafeCtorDecl, unless(HasTwoParamSpanCtorDecl))
1486acc3cc69SRashmi Mudduluru             .bind(OpTag));
1487acc3cc69SRashmi Mudduluru   }
14885ac34358SDana Jansens 
14895ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
14905ac34358SDana Jansens                              bool IsRelatedToDecl,
14915ac34358SDana Jansens                              ASTContext &Ctx) const override {
14925ac34358SDana Jansens     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
14935ac34358SDana Jansens   }
14945ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1495acc3cc69SRashmi Mudduluru 
1496148dc8a2Sziqingluo-90   DeclUseList getClaimedVarUseSites() const override { return {}; }
1497acc3cc69SRashmi Mudduluru };
1498bdf4f2beSZiqing Luo 
14997122f55cSMalavika Samak // Warning gadget for unsafe invocation of span::data method.
15007122f55cSMalavika Samak // Triggers when the pointer returned by the invocation is immediately
15017122f55cSMalavika Samak // cast to a larger type.
15027122f55cSMalavika Samak 
15037122f55cSMalavika Samak class DataInvocationGadget : public WarningGadget {
15047122f55cSMalavika Samak   constexpr static const char *const OpTag = "data_invocation_expr";
15057122f55cSMalavika Samak   const ExplicitCastExpr *Op;
15067122f55cSMalavika Samak 
15077122f55cSMalavika Samak public:
15087122f55cSMalavika Samak   DataInvocationGadget(const MatchFinder::MatchResult &Result)
15097122f55cSMalavika Samak       : WarningGadget(Kind::DataInvocation),
15107122f55cSMalavika Samak         Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {}
15117122f55cSMalavika Samak 
15127122f55cSMalavika Samak   static bool classof(const Gadget *G) {
15137122f55cSMalavika Samak     return G->getKind() == Kind::DataInvocation;
15147122f55cSMalavika Samak   }
15157122f55cSMalavika Samak 
15167122f55cSMalavika Samak   static Matcher matcher() {
1517e913a33fSMalavika Samak 
1518e913a33fSMalavika Samak     Matcher callExpr = cxxMemberCallExpr(callee(
1519e913a33fSMalavika Samak         cxxMethodDecl(hasName("data"),
1520e913a33fSMalavika Samak                       ofClass(anyOf(hasName("std::span"), hasName("std::array"),
1521e913a33fSMalavika Samak                                     hasName("std::vector"))))));
15227122f55cSMalavika Samak     return stmt(
1523414df705SMalavika Samak         explicitCastExpr(anyOf(has(callExpr), has(parenExpr(has(callExpr)))))
15247122f55cSMalavika Samak             .bind(OpTag));
15257122f55cSMalavika Samak   }
15265ac34358SDana Jansens 
15275ac34358SDana Jansens   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
15285ac34358SDana Jansens                              bool IsRelatedToDecl,
15295ac34358SDana Jansens                              ASTContext &Ctx) const override {
15305ac34358SDana Jansens     Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
15315ac34358SDana Jansens   }
15325ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
15337122f55cSMalavika Samak 
15347122f55cSMalavika Samak   DeclUseList getClaimedVarUseSites() const override { return {}; }
15357122f55cSMalavika Samak };
15367122f55cSMalavika Samak 
1537d7dd2c46Sziqingluo-90 class UnsafeLibcFunctionCallGadget : public WarningGadget {
1538d7dd2c46Sziqingluo-90   const CallExpr *const Call;
1539d7dd2c46Sziqingluo-90   const Expr *UnsafeArg = nullptr;
1540d7dd2c46Sziqingluo-90   constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
1541d7dd2c46Sziqingluo-90   // Extra tags for additional information:
1542d7dd2c46Sziqingluo-90   constexpr static const char *const UnsafeSprintfTag =
1543d7dd2c46Sziqingluo-90       "UnsafeLibcFunctionCall_sprintf";
1544d7dd2c46Sziqingluo-90   constexpr static const char *const UnsafeSizedByTag =
1545d7dd2c46Sziqingluo-90       "UnsafeLibcFunctionCall_sized_by";
1546d7dd2c46Sziqingluo-90   constexpr static const char *const UnsafeStringTag =
1547d7dd2c46Sziqingluo-90       "UnsafeLibcFunctionCall_string";
1548d7dd2c46Sziqingluo-90   constexpr static const char *const UnsafeVaListTag =
1549d7dd2c46Sziqingluo-90       "UnsafeLibcFunctionCall_va_list";
1550d7dd2c46Sziqingluo-90 
1551d7dd2c46Sziqingluo-90   enum UnsafeKind {
1552d7dd2c46Sziqingluo-90     OTHERS = 0,  // no specific information, the callee function is unsafe
1553d7dd2c46Sziqingluo-90     SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
1554d7dd2c46Sziqingluo-90     SIZED_BY =
1555d7dd2c46Sziqingluo-90         2, // the first two arguments of `snprintf` function have
1556d7dd2c46Sziqingluo-90            // "__sized_by" relation but they do not conform to safe patterns
1557d7dd2c46Sziqingluo-90     STRING = 3,  // an argument is a pointer-to-char-as-string but does not
1558d7dd2c46Sziqingluo-90                  // guarantee null-termination
1559d7dd2c46Sziqingluo-90     VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
1560d7dd2c46Sziqingluo-90                  // considered unsafe as it is not compile-time check
1561d7dd2c46Sziqingluo-90   } WarnedFunKind = OTHERS;
1562d7dd2c46Sziqingluo-90 
1563d7dd2c46Sziqingluo-90 public:
1564d7dd2c46Sziqingluo-90   UnsafeLibcFunctionCallGadget(const MatchFinder::MatchResult &Result)
1565d7dd2c46Sziqingluo-90       : WarningGadget(Kind::UnsafeLibcFunctionCall),
1566d7dd2c46Sziqingluo-90         Call(Result.Nodes.getNodeAs<CallExpr>(Tag)) {
1567d7dd2c46Sziqingluo-90     if (Result.Nodes.getNodeAs<Decl>(UnsafeSprintfTag))
1568d7dd2c46Sziqingluo-90       WarnedFunKind = SPRINTF;
1569d7dd2c46Sziqingluo-90     else if (auto *E = Result.Nodes.getNodeAs<Expr>(UnsafeStringTag)) {
1570d7dd2c46Sziqingluo-90       WarnedFunKind = STRING;
1571d7dd2c46Sziqingluo-90       UnsafeArg = E;
1572d7dd2c46Sziqingluo-90     } else if (Result.Nodes.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
1573d7dd2c46Sziqingluo-90       WarnedFunKind = SIZED_BY;
1574d7dd2c46Sziqingluo-90       UnsafeArg = Call->getArg(0);
1575d7dd2c46Sziqingluo-90     } else if (Result.Nodes.getNodeAs<Decl>(UnsafeVaListTag))
1576d7dd2c46Sziqingluo-90       WarnedFunKind = VA_LIST;
1577d7dd2c46Sziqingluo-90   }
1578d7dd2c46Sziqingluo-90 
1579d7dd2c46Sziqingluo-90   static Matcher matcher(const UnsafeBufferUsageHandler *Handler) {
1580d7dd2c46Sziqingluo-90     return stmt(unless(ignoreUnsafeLibcCall(Handler)),
1581d7dd2c46Sziqingluo-90       anyOf(
1582d7dd2c46Sziqingluo-90         callExpr(
1583d7dd2c46Sziqingluo-90             callee(functionDecl(anyOf(
1584d7dd2c46Sziqingluo-90                 // Match a predefined unsafe libc
1585d7dd2c46Sziqingluo-90                 // function:
1586d7dd2c46Sziqingluo-90                 functionDecl(libc_func_matchers::isPredefinedUnsafeLibcFunc()),
1587d7dd2c46Sziqingluo-90                 // Match a call to one of the `v*printf` functions
1588d7dd2c46Sziqingluo-90                 // taking va-list, which cannot be checked at
1589d7dd2c46Sziqingluo-90                 // compile-time:
1590d7dd2c46Sziqingluo-90                 functionDecl(libc_func_matchers::isUnsafeVaListPrintfFunc())
1591d7dd2c46Sziqingluo-90                     .bind(UnsafeVaListTag),
1592d7dd2c46Sziqingluo-90                 // Match a call to a `sprintf` function, which is never
1593d7dd2c46Sziqingluo-90                 // safe:
1594d7dd2c46Sziqingluo-90                 functionDecl(libc_func_matchers::isUnsafeSprintfFunc())
1595d7dd2c46Sziqingluo-90                     .bind(UnsafeSprintfTag)))),
1596d7dd2c46Sziqingluo-90             //  (unless the call has a sole string literal argument):
1597d7dd2c46Sziqingluo-90             unless(
1598d7dd2c46Sziqingluo-90                 allOf(hasArgument(0, expr(stringLiteral())), hasNumArgs(1)))),
1599d7dd2c46Sziqingluo-90 
1600d7dd2c46Sziqingluo-90         // The following two cases require checking against actual
1601d7dd2c46Sziqingluo-90         // arguments of the call:
1602d7dd2c46Sziqingluo-90 
1603d7dd2c46Sziqingluo-90         // Match a call to an `snprintf` function. And first two
1604d7dd2c46Sziqingluo-90         // arguments of the call (that describe a buffer) are not in
1605d7dd2c46Sziqingluo-90         // safe patterns:
1606d7dd2c46Sziqingluo-90         callExpr(callee(functionDecl(libc_func_matchers::isNormalPrintfFunc())),
1607d7dd2c46Sziqingluo-90                  libc_func_matchers::hasUnsafeSnprintfBuffer())
1608d7dd2c46Sziqingluo-90             .bind(UnsafeSizedByTag),
1609d7dd2c46Sziqingluo-90         // Match a call to a `printf` function, which can be safe if
1610d7dd2c46Sziqingluo-90         // all arguments are null-terminated:
1611d7dd2c46Sziqingluo-90         callExpr(callee(functionDecl(libc_func_matchers::isNormalPrintfFunc())),
1612d7dd2c46Sziqingluo-90                  libc_func_matchers::hasUnsafePrintfStringArg(
1613d7dd2c46Sziqingluo-90                      expr().bind(UnsafeStringTag)))));
1614d7dd2c46Sziqingluo-90   }
1615d7dd2c46Sziqingluo-90 
1616d7dd2c46Sziqingluo-90   const Stmt *getBaseStmt() const { return Call; }
1617d7dd2c46Sziqingluo-90 
1618d7dd2c46Sziqingluo-90   SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
1619d7dd2c46Sziqingluo-90 
1620d7dd2c46Sziqingluo-90   void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1621d7dd2c46Sziqingluo-90                              bool IsRelatedToDecl,
1622d7dd2c46Sziqingluo-90                              ASTContext &Ctx) const override {
1623d7dd2c46Sziqingluo-90     Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
1624d7dd2c46Sziqingluo-90   }
1625d7dd2c46Sziqingluo-90 
1626d7dd2c46Sziqingluo-90   DeclUseList getClaimedVarUseSites() const override { return {}; }
1627d7dd2c46Sziqingluo-90 };
1628d7dd2c46Sziqingluo-90 
1629bdf4f2beSZiqing Luo // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
1630bdf4f2beSZiqing Luo // Context (see `isInUnspecifiedLvalueContext`).
1631bdf4f2beSZiqing Luo // Note here `[]` is the built-in subscript operator.
1632bdf4f2beSZiqing Luo class ULCArraySubscriptGadget : public FixableGadget {
1633bdf4f2beSZiqing Luo private:
1634148dc8a2Sziqingluo-90   static constexpr const char *const ULCArraySubscriptTag =
1635148dc8a2Sziqingluo-90       "ArraySubscriptUnderULC";
1636bdf4f2beSZiqing Luo   const ArraySubscriptExpr *Node;
1637bdf4f2beSZiqing Luo 
1638bdf4f2beSZiqing Luo public:
1639bdf4f2beSZiqing Luo   ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1640bdf4f2beSZiqing Luo       : FixableGadget(Kind::ULCArraySubscript),
1641bdf4f2beSZiqing Luo         Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
1642bdf4f2beSZiqing Luo     assert(Node != nullptr && "Expecting a non-null matching result");
1643bdf4f2beSZiqing Luo   }
1644bdf4f2beSZiqing Luo 
1645bdf4f2beSZiqing Luo   static bool classof(const Gadget *G) {
1646bdf4f2beSZiqing Luo     return G->getKind() == Kind::ULCArraySubscript;
1647bdf4f2beSZiqing Luo   }
1648bdf4f2beSZiqing Luo 
1649bdf4f2beSZiqing Luo   static Matcher matcher() {
1650bdf4f2beSZiqing Luo     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
16513fa91021Sjkorous-apple     auto BaseIsArrayOrPtrDRE = hasBase(
16523fa91021Sjkorous-apple         ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));
1653bdf4f2beSZiqing Luo     auto Target =
1654bdf4f2beSZiqing Luo         arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
1655bdf4f2beSZiqing Luo 
1656bdf4f2beSZiqing Luo     return expr(isInUnspecifiedLvalueContext(Target));
1657bdf4f2beSZiqing Luo   }
1658bdf4f2beSZiqing Luo 
1659644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1660644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
16615ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1662bdf4f2beSZiqing Luo 
1663bdf4f2beSZiqing Luo   virtual DeclUseList getClaimedVarUseSites() const override {
1664148dc8a2Sziqingluo-90     if (const auto *DRE =
1665148dc8a2Sziqingluo-90             dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
1666bdf4f2beSZiqing Luo       return {DRE};
1667bdf4f2beSZiqing Luo     }
1668bdf4f2beSZiqing Luo     return {};
1669bdf4f2beSZiqing Luo   }
1670bdf4f2beSZiqing Luo };
1671e7596a99SMalavikaSamak 
1672a046d187SMalavikaSamak // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
1673a046d187SMalavikaSamak // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
1674a046d187SMalavikaSamak // fixit of the form `UPC(DRE.data())`.
1675a046d187SMalavikaSamak class UPCStandalonePointerGadget : public FixableGadget {
1676a046d187SMalavikaSamak private:
1677a046d187SMalavikaSamak   static constexpr const char *const DeclRefExprTag = "StandalonePointer";
1678a046d187SMalavikaSamak   const DeclRefExpr *Node;
1679a046d187SMalavikaSamak 
1680a046d187SMalavikaSamak public:
1681a046d187SMalavikaSamak   UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
1682a046d187SMalavikaSamak       : FixableGadget(Kind::UPCStandalonePointer),
1683a046d187SMalavikaSamak         Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
1684a046d187SMalavikaSamak     assert(Node != nullptr && "Expecting a non-null matching result");
1685a046d187SMalavikaSamak   }
1686a046d187SMalavikaSamak 
1687a046d187SMalavikaSamak   static bool classof(const Gadget *G) {
1688a046d187SMalavikaSamak     return G->getKind() == Kind::UPCStandalonePointer;
1689a046d187SMalavikaSamak   }
1690a046d187SMalavikaSamak 
1691a046d187SMalavikaSamak   static Matcher matcher() {
1692a046d187SMalavikaSamak     auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
16933fa91021Sjkorous-apple     auto target = expr(ignoringParenImpCasts(
16943fa91021Sjkorous-apple         declRefExpr(allOf(ArrayOrPtr, toSupportedVariable()))
16953fa91021Sjkorous-apple             .bind(DeclRefExprTag)));
1696a046d187SMalavikaSamak     return stmt(isInUnspecifiedPointerContext(target));
1697a046d187SMalavikaSamak   }
1698a046d187SMalavikaSamak 
1699644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1700644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
17015ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1702a046d187SMalavikaSamak 
17033fa91021Sjkorous-apple   virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
1704a046d187SMalavikaSamak };
1705a046d187SMalavikaSamak 
1706e7596a99SMalavikaSamak class PointerDereferenceGadget : public FixableGadget {
1707e7596a99SMalavikaSamak   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1708e7596a99SMalavikaSamak   static constexpr const char *const OperatorTag = "op";
1709e7596a99SMalavikaSamak 
1710e7596a99SMalavikaSamak   const DeclRefExpr *BaseDeclRefExpr = nullptr;
1711e7596a99SMalavikaSamak   const UnaryOperator *Op = nullptr;
1712e7596a99SMalavikaSamak 
1713e7596a99SMalavikaSamak public:
1714e7596a99SMalavikaSamak   PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
1715e7596a99SMalavikaSamak       : FixableGadget(Kind::PointerDereference),
1716e7596a99SMalavikaSamak         BaseDeclRefExpr(
1717e7596a99SMalavikaSamak             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
1718e7596a99SMalavikaSamak         Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
1719e7596a99SMalavikaSamak 
1720e7596a99SMalavikaSamak   static bool classof(const Gadget *G) {
1721e7596a99SMalavikaSamak     return G->getKind() == Kind::PointerDereference;
1722e7596a99SMalavikaSamak   }
1723e7596a99SMalavikaSamak 
1724e7596a99SMalavikaSamak   static Matcher matcher() {
1725e7596a99SMalavikaSamak     auto Target =
1726e7596a99SMalavikaSamak         unaryOperator(
1727e7596a99SMalavikaSamak             hasOperatorName("*"),
1728e7596a99SMalavikaSamak             has(expr(ignoringParenImpCasts(
17292afcda69SRashmi Mudduluru                 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
1730e7596a99SMalavikaSamak             .bind(OperatorTag);
1731e7596a99SMalavikaSamak 
1732e7596a99SMalavikaSamak     return expr(isInUnspecifiedLvalueContext(Target));
1733e7596a99SMalavikaSamak   }
1734e7596a99SMalavikaSamak 
1735e7596a99SMalavikaSamak   DeclUseList getClaimedVarUseSites() const override {
1736e7596a99SMalavikaSamak     return {BaseDeclRefExpr};
1737e7596a99SMalavikaSamak   }
1738e7596a99SMalavikaSamak 
1739644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1740644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
17415ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1742e7596a99SMalavikaSamak };
1743e7596a99SMalavikaSamak 
1744ca6ceeb0SZiqing Luo // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
1745ca6ceeb0SZiqing Luo // Context (see `isInUnspecifiedPointerContext`).
1746ca6ceeb0SZiqing Luo // Note here `[]` is the built-in subscript operator.
1747ca6ceeb0SZiqing Luo class UPCAddressofArraySubscriptGadget : public FixableGadget {
1748ca6ceeb0SZiqing Luo private:
1749ca6ceeb0SZiqing Luo   static constexpr const char *const UPCAddressofArraySubscriptTag =
1750ca6ceeb0SZiqing Luo       "AddressofArraySubscriptUnderUPC";
1751ca6ceeb0SZiqing Luo   const UnaryOperator *Node; // the `&DRE[any]` node
1752ca6ceeb0SZiqing Luo 
1753ca6ceeb0SZiqing Luo public:
1754ca6ceeb0SZiqing Luo   UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1755ca6ceeb0SZiqing Luo       : FixableGadget(Kind::ULCArraySubscript),
1756ca6ceeb0SZiqing Luo         Node(Result.Nodes.getNodeAs<UnaryOperator>(
1757ca6ceeb0SZiqing Luo             UPCAddressofArraySubscriptTag)) {
1758ca6ceeb0SZiqing Luo     assert(Node != nullptr && "Expecting a non-null matching result");
1759ca6ceeb0SZiqing Luo   }
1760ca6ceeb0SZiqing Luo 
1761ca6ceeb0SZiqing Luo   static bool classof(const Gadget *G) {
1762ca6ceeb0SZiqing Luo     return G->getKind() == Kind::UPCAddressofArraySubscript;
1763ca6ceeb0SZiqing Luo   }
1764ca6ceeb0SZiqing Luo 
1765ca6ceeb0SZiqing Luo   static Matcher matcher() {
1766ca6ceeb0SZiqing Luo     return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
17673fa91021Sjkorous-apple         unaryOperator(
17683fa91021Sjkorous-apple             hasOperatorName("&"),
17693fa91021Sjkorous-apple             hasUnaryOperand(arraySubscriptExpr(hasBase(
17703fa91021Sjkorous-apple                 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
1771ca6ceeb0SZiqing Luo             .bind(UPCAddressofArraySubscriptTag)))));
1772ca6ceeb0SZiqing Luo   }
1773ca6ceeb0SZiqing Luo 
1774644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1775644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &) const override;
17765ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1777ca6ceeb0SZiqing Luo 
1778ca6ceeb0SZiqing Luo   virtual DeclUseList getClaimedVarUseSites() const override {
1779ca6ceeb0SZiqing Luo     const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
1780ca6ceeb0SZiqing Luo     const auto *DRE =
178113ea36dbSjuan.vazquez         cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
1782ca6ceeb0SZiqing Luo     return {DRE};
1783ca6ceeb0SZiqing Luo   }
1784ca6ceeb0SZiqing Luo };
17850d00a972SArtem Dergachev } // namespace
17860d00a972SArtem Dergachev 
17878086323aSArtem Dergachev namespace {
17888086323aSArtem Dergachev // An auxiliary tracking facility for the fixit analysis. It helps connect
1789db3dcedbSRashmi Mudduluru // declarations to its uses and make sure we've covered all uses with our
1790db3dcedbSRashmi Mudduluru // analysis before we try to fix the declaration.
17918086323aSArtem Dergachev class DeclUseTracker {
17928086323aSArtem Dergachev   using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
17938086323aSArtem Dergachev   using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
17943b7af279SArtem Dergachev 
17958086323aSArtem Dergachev   // Allocate on the heap for easier move.
17968086323aSArtem Dergachev   std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
17978086323aSArtem Dergachev   DefMapTy Defs{};
17983b7af279SArtem Dergachev 
17993b7af279SArtem Dergachev public:
18008086323aSArtem Dergachev   DeclUseTracker() = default;
18018086323aSArtem Dergachev   DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
18027e1b62bdSManna, Soumi   DeclUseTracker &operator=(const DeclUseTracker &) = delete;
18038086323aSArtem Dergachev   DeclUseTracker(DeclUseTracker &&) = default;
1804214312efSJan Korous   DeclUseTracker &operator=(DeclUseTracker &&) = default;
18058086323aSArtem Dergachev 
18068086323aSArtem Dergachev   // Start tracking a freshly discovered DRE.
18078086323aSArtem Dergachev   void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
18088086323aSArtem Dergachev 
18098086323aSArtem Dergachev   // Stop tracking the DRE as it's been fully figured out.
18108086323aSArtem Dergachev   void claimUse(const DeclRefExpr *DRE) {
18118086323aSArtem Dergachev     assert(Uses->count(DRE) &&
18128086323aSArtem Dergachev            "DRE not found or claimed by multiple matchers!");
18138086323aSArtem Dergachev     Uses->erase(DRE);
18148086323aSArtem Dergachev   }
18158086323aSArtem Dergachev 
18168086323aSArtem Dergachev   // A variable is unclaimed if at least one use is unclaimed.
18178086323aSArtem Dergachev   bool hasUnclaimedUses(const VarDecl *VD) const {
18188086323aSArtem Dergachev     // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
18198086323aSArtem Dergachev     return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
18208086323aSArtem Dergachev       return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
18218086323aSArtem Dergachev     });
18228086323aSArtem Dergachev   }
18238086323aSArtem Dergachev 
1824a6ae740eSRashmi Mudduluru   UseSetTy getUnclaimedUses(const VarDecl *VD) const {
1825a6ae740eSRashmi Mudduluru     UseSetTy ReturnSet;
1826a6ae740eSRashmi Mudduluru     for (auto use : *Uses) {
1827a6ae740eSRashmi Mudduluru       if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
1828a6ae740eSRashmi Mudduluru         ReturnSet.insert(use);
1829a6ae740eSRashmi Mudduluru       }
1830a6ae740eSRashmi Mudduluru     }
1831a6ae740eSRashmi Mudduluru     return ReturnSet;
1832a6ae740eSRashmi Mudduluru   }
1833a6ae740eSRashmi Mudduluru 
18348086323aSArtem Dergachev   void discoverDecl(const DeclStmt *DS) {
18358086323aSArtem Dergachev     for (const Decl *D : DS->decls()) {
18368086323aSArtem Dergachev       if (const auto *VD = dyn_cast<VarDecl>(D)) {
18378fd62e70SArtem Dergachev         // FIXME: Assertion temporarily disabled due to a bug in
18388fd62e70SArtem Dergachev         // ASTMatcher internal behavior in presence of GNU
18398fd62e70SArtem Dergachev         // statement-expressions. We need to properly investigate this
18408fd62e70SArtem Dergachev         // because it can screw up our algorithm in other ways.
18418fd62e70SArtem Dergachev         // assert(Defs.count(VD) == 0 && "Definition already discovered!");
18428086323aSArtem Dergachev         Defs[VD] = DS;
18438086323aSArtem Dergachev       }
18448086323aSArtem Dergachev     }
18458086323aSArtem Dergachev   }
18468086323aSArtem Dergachev 
18478086323aSArtem Dergachev   const DeclStmt *lookupDecl(const VarDecl *VD) const {
1848c81ff8b7SKazu Hirata     return Defs.lookup(VD);
18498086323aSArtem Dergachev   }
18508086323aSArtem Dergachev };
18518086323aSArtem Dergachev } // namespace
18528086323aSArtem Dergachev 
1853762af11dSZiqing Luo // Representing a pointer type expression of the form `++Ptr` in an Unspecified
1854762af11dSZiqing Luo // Pointer Context (UPC):
1855762af11dSZiqing Luo class UPCPreIncrementGadget : public FixableGadget {
1856762af11dSZiqing Luo private:
1857762af11dSZiqing Luo   static constexpr const char *const UPCPreIncrementTag =
1858762af11dSZiqing Luo       "PointerPreIncrementUnderUPC";
1859762af11dSZiqing Luo   const UnaryOperator *Node; // the `++Ptr` node
1860762af11dSZiqing Luo 
1861762af11dSZiqing Luo public:
1862762af11dSZiqing Luo   UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
1863762af11dSZiqing Luo       : FixableGadget(Kind::UPCPreIncrement),
1864762af11dSZiqing Luo         Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
1865762af11dSZiqing Luo     assert(Node != nullptr && "Expecting a non-null matching result");
1866762af11dSZiqing Luo   }
1867762af11dSZiqing Luo 
1868762af11dSZiqing Luo   static bool classof(const Gadget *G) {
1869762af11dSZiqing Luo     return G->getKind() == Kind::UPCPreIncrement;
1870762af11dSZiqing Luo   }
1871762af11dSZiqing Luo 
1872762af11dSZiqing Luo   static Matcher matcher() {
1873762af11dSZiqing Luo     // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
1874762af11dSZiqing Luo     // Although currently we can only provide fix-its when `Ptr` is a DRE, we
1875762af11dSZiqing Luo     // can have the matcher be general, so long as `getClaimedVarUseSites` does
1876762af11dSZiqing Luo     // things right.
1877762af11dSZiqing Luo     return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1878762af11dSZiqing Luo         unaryOperator(isPreInc(),
18793fa91021Sjkorous-apple                       hasUnaryOperand(declRefExpr(toSupportedVariable())))
18803fa91021Sjkorous-apple             .bind(UPCPreIncrementTag)))));
1881762af11dSZiqing Luo   }
1882762af11dSZiqing Luo 
1883644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1884644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
18855ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1886762af11dSZiqing Luo 
1887762af11dSZiqing Luo   virtual DeclUseList getClaimedVarUseSites() const override {
1888762af11dSZiqing Luo     return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
1889762af11dSZiqing Luo   }
1890762af11dSZiqing Luo };
1891762af11dSZiqing Luo 
1892e1655a98SRashmi Mudduluru // Representing a pointer type expression of the form `Ptr += n` in an
1893e1655a98SRashmi Mudduluru // Unspecified Untyped Context (UUC):
1894e1655a98SRashmi Mudduluru class UUCAddAssignGadget : public FixableGadget {
1895e1655a98SRashmi Mudduluru private:
1896e1655a98SRashmi Mudduluru   static constexpr const char *const UUCAddAssignTag =
1897e1655a98SRashmi Mudduluru       "PointerAddAssignUnderUUC";
1898e1655a98SRashmi Mudduluru   static constexpr const char *const OffsetTag = "Offset";
1899e1655a98SRashmi Mudduluru 
1900e1655a98SRashmi Mudduluru   const BinaryOperator *Node; // the `Ptr += n` node
1901e1655a98SRashmi Mudduluru   const Expr *Offset = nullptr;
1902e1655a98SRashmi Mudduluru 
1903e1655a98SRashmi Mudduluru public:
1904e1655a98SRashmi Mudduluru   UUCAddAssignGadget(const MatchFinder::MatchResult &Result)
1905e1655a98SRashmi Mudduluru       : FixableGadget(Kind::UUCAddAssign),
1906e1655a98SRashmi Mudduluru         Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1907e1655a98SRashmi Mudduluru         Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1908e1655a98SRashmi Mudduluru     assert(Node != nullptr && "Expecting a non-null matching result");
1909e1655a98SRashmi Mudduluru   }
1910e1655a98SRashmi Mudduluru 
1911e1655a98SRashmi Mudduluru   static bool classof(const Gadget *G) {
1912e1655a98SRashmi Mudduluru     return G->getKind() == Kind::UUCAddAssign;
1913e1655a98SRashmi Mudduluru   }
1914e1655a98SRashmi Mudduluru 
1915e1655a98SRashmi Mudduluru   static Matcher matcher() {
1916e5cebec5Sjkorous-apple     // clang-format off
1917e1655a98SRashmi Mudduluru     return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
1918e1655a98SRashmi Mudduluru         binaryOperator(hasOperatorName("+="),
1919e5cebec5Sjkorous-apple                        hasLHS(
1920e5cebec5Sjkorous-apple                         declRefExpr(
1921e5cebec5Sjkorous-apple                           hasPointerType(),
1922e5cebec5Sjkorous-apple                           toSupportedVariable())),
1923e1655a98SRashmi Mudduluru                        hasRHS(expr().bind(OffsetTag)))
1924e1655a98SRashmi Mudduluru             .bind(UUCAddAssignTag)))));
1925e5cebec5Sjkorous-apple     // clang-format on
1926e1655a98SRashmi Mudduluru   }
1927e1655a98SRashmi Mudduluru 
1928644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1929644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &S) const override;
19305ac34358SDana Jansens   SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1931e1655a98SRashmi Mudduluru 
1932e1655a98SRashmi Mudduluru   virtual DeclUseList getClaimedVarUseSites() const override {
1933e1655a98SRashmi Mudduluru     return {dyn_cast<DeclRefExpr>(Node->getLHS())};
1934e1655a98SRashmi Mudduluru   }
1935e1655a98SRashmi Mudduluru };
1936e1655a98SRashmi Mudduluru 
19376a0f2e53Sziqingluo-90 // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
19386a0f2e53Sziqingluo-90 // ptr)`:
19396a0f2e53Sziqingluo-90 class DerefSimplePtrArithFixableGadget : public FixableGadget {
19406a0f2e53Sziqingluo-90   static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
19416a0f2e53Sziqingluo-90   static constexpr const char *const DerefOpTag = "DerefOp";
19426a0f2e53Sziqingluo-90   static constexpr const char *const AddOpTag = "AddOp";
19436a0f2e53Sziqingluo-90   static constexpr const char *const OffsetTag = "Offset";
19446a0f2e53Sziqingluo-90 
19456a0f2e53Sziqingluo-90   const DeclRefExpr *BaseDeclRefExpr = nullptr;
19466a0f2e53Sziqingluo-90   const UnaryOperator *DerefOp = nullptr;
19476a0f2e53Sziqingluo-90   const BinaryOperator *AddOp = nullptr;
19486a0f2e53Sziqingluo-90   const IntegerLiteral *Offset = nullptr;
19496a0f2e53Sziqingluo-90 
19506a0f2e53Sziqingluo-90 public:
19516a0f2e53Sziqingluo-90   DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)
19526a0f2e53Sziqingluo-90       : FixableGadget(Kind::DerefSimplePtrArithFixable),
19536a0f2e53Sziqingluo-90         BaseDeclRefExpr(
19546a0f2e53Sziqingluo-90             Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
19556a0f2e53Sziqingluo-90         DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
19566a0f2e53Sziqingluo-90         AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
19576a0f2e53Sziqingluo-90         Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
19586a0f2e53Sziqingluo-90 
19596a0f2e53Sziqingluo-90   static Matcher matcher() {
19606a0f2e53Sziqingluo-90     // clang-format off
19616a0f2e53Sziqingluo-90     auto ThePtr = expr(hasPointerType(),
19622afcda69SRashmi Mudduluru                        ignoringImpCasts(declRefExpr(toSupportedVariable()).
19632afcda69SRashmi Mudduluru                                         bind(BaseDeclRefExprTag)));
19646a0f2e53Sziqingluo-90     auto PlusOverPtrAndInteger = expr(anyOf(
19656a0f2e53Sziqingluo-90           binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
19666a0f2e53Sziqingluo-90                          hasRHS(integerLiteral().bind(OffsetTag)))
19676a0f2e53Sziqingluo-90                          .bind(AddOpTag),
19686a0f2e53Sziqingluo-90           binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
19696a0f2e53Sziqingluo-90                          hasLHS(integerLiteral().bind(OffsetTag)))
19706a0f2e53Sziqingluo-90                          .bind(AddOpTag)));
19716a0f2e53Sziqingluo-90     return isInUnspecifiedLvalueContext(unaryOperator(
19726a0f2e53Sziqingluo-90         hasOperatorName("*"),
19736a0f2e53Sziqingluo-90         hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
19746a0f2e53Sziqingluo-90         .bind(DerefOpTag));
19756a0f2e53Sziqingluo-90     // clang-format on
19766a0f2e53Sziqingluo-90   }
19776a0f2e53Sziqingluo-90 
1978644ac2a0Sjkorous-apple   virtual std::optional<FixItList>
1979644ac2a0Sjkorous-apple   getFixits(const FixitStrategy &s) const final;
19805ac34358SDana Jansens   SourceLocation getSourceLoc() const override {
19815ac34358SDana Jansens     return DerefOp->getBeginLoc();
19825ac34358SDana Jansens   }
19836a0f2e53Sziqingluo-90 
19846a0f2e53Sziqingluo-90   virtual DeclUseList getClaimedVarUseSites() const final {
19856a0f2e53Sziqingluo-90     return {BaseDeclRefExpr};
19866a0f2e53Sziqingluo-90   }
19876a0f2e53Sziqingluo-90 };
19886a0f2e53Sziqingluo-90 
19898086323aSArtem Dergachev /// Scan the function and return a list of gadgets found with provided kits.
1990c60b055dSZequan Wu static void findGadgets(const Stmt *S, ASTContext &Ctx,
1991c60b055dSZequan Wu                         const UnsafeBufferUsageHandler &Handler,
1992c60b055dSZequan Wu                         bool EmitSuggestions, FixableGadgetList &FixableGadgets,
1993c60b055dSZequan Wu                         WarningGadgetList &WarningGadgets,
1994c60b055dSZequan Wu                         DeclUseTracker &Tracker) {
19958086323aSArtem Dergachev 
19968086323aSArtem Dergachev   struct GadgetFinderCallback : MatchFinder::MatchCallback {
1997c60b055dSZequan Wu     GadgetFinderCallback(FixableGadgetList &FixableGadgets,
1998c60b055dSZequan Wu                          WarningGadgetList &WarningGadgets,
1999c60b055dSZequan Wu                          DeclUseTracker &Tracker)
2000c60b055dSZequan Wu         : FixableGadgets(FixableGadgets), WarningGadgets(WarningGadgets),
2001c60b055dSZequan Wu           Tracker(Tracker) {}
20023b7af279SArtem Dergachev 
20033b7af279SArtem Dergachev     void run(const MatchFinder::MatchResult &Result) override {
20040d00a972SArtem Dergachev       // In debug mode, assert that we've found exactly one gadget.
20050d00a972SArtem Dergachev       // This helps us avoid conflicts in .bind() tags.
20060d00a972SArtem Dergachev #if NDEBUG
20070d00a972SArtem Dergachev #define NEXT return
20080d00a972SArtem Dergachev #else
2009893a0ea9SFangrui Song       [[maybe_unused]] int numFound = 0;
20100d00a972SArtem Dergachev #define NEXT ++numFound
20110d00a972SArtem Dergachev #endif
20120d00a972SArtem Dergachev 
20138086323aSArtem Dergachev       if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
20148086323aSArtem Dergachev         Tracker.discoverUse(DRE);
20158086323aSArtem Dergachev         NEXT;
20168086323aSArtem Dergachev       }
20178086323aSArtem Dergachev 
20188086323aSArtem Dergachev       if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
20198086323aSArtem Dergachev         Tracker.discoverDecl(DS);
20208086323aSArtem Dergachev         NEXT;
20218086323aSArtem Dergachev       }
20228086323aSArtem Dergachev 
20230d00a972SArtem Dergachev       // Figure out which matcher we've found, and call the appropriate
20240d00a972SArtem Dergachev       // subclass constructor.
20250d00a972SArtem Dergachev       // FIXME: Can we do this more logarithmically?
202650d4a1f7SMalavikaSamak #define FIXABLE_GADGET(name)                                                   \
20270d00a972SArtem Dergachev   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
202850d4a1f7SMalavikaSamak     FixableGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
202950d4a1f7SMalavikaSamak     NEXT;                                                                      \
203050d4a1f7SMalavikaSamak   }
203150d4a1f7SMalavikaSamak #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
203250d4a1f7SMalavikaSamak #define WARNING_GADGET(name)                                                   \
203350d4a1f7SMalavikaSamak   if (Result.Nodes.getNodeAs<Stmt>(#name)) {                                   \
203450d4a1f7SMalavikaSamak     WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));          \
20350d00a972SArtem Dergachev     NEXT;                                                                      \
20360d00a972SArtem Dergachev   }
20370d00a972SArtem Dergachev #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
20380d00a972SArtem Dergachev 
20390d00a972SArtem Dergachev       assert(numFound >= 1 && "Gadgets not found in match result!");
20400d00a972SArtem Dergachev       assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
20413b7af279SArtem Dergachev     }
2042c60b055dSZequan Wu 
2043c60b055dSZequan Wu     FixableGadgetList &FixableGadgets;
2044c60b055dSZequan Wu     WarningGadgetList &WarningGadgets;
2045c60b055dSZequan Wu     DeclUseTracker &Tracker;
20463b7af279SArtem Dergachev   };
20473b7af279SArtem Dergachev 
20483b7af279SArtem Dergachev   MatchFinder M;
2049c60b055dSZequan Wu   GadgetFinderCallback CB{FixableGadgets, WarningGadgets, Tracker};
20503b7af279SArtem Dergachev 
20510d00a972SArtem Dergachev   // clang-format off
20523b7af279SArtem Dergachev   M.addMatcher(
2053b7bdf199SArtem Dergachev       stmt(
2054b7bdf199SArtem Dergachev         forEachDescendantEvaluatedStmt(stmt(anyOf(
2055b7bdf199SArtem Dergachev           // Add Gadget::matcher() for every gadget in the registry.
2056b7bdf199SArtem Dergachev #define WARNING_GADGET(x)                                                      \
2057b7bdf199SArtem Dergachev           allOf(x ## Gadget::matcher().bind(#x),                               \
2058b7bdf199SArtem Dergachev                 notInSafeBufferOptOut(&Handler)),
2059d7dd2c46Sziqingluo-90 #define WARNING_OPTIONAL_GADGET(x)                                            \
2060d7dd2c46Sziqingluo-90           allOf(x ## Gadget::matcher(&Handler).bind(#x),                      \
2061d7dd2c46Sziqingluo-90                 notInSafeBufferOptOut(&Handler)),
2062b7bdf199SArtem Dergachev #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2063b7bdf199SArtem Dergachev             // Avoid a hanging comma.
2064b7bdf199SArtem Dergachev             unless(stmt())
2065b7bdf199SArtem Dergachev         )))
2066b7bdf199SArtem Dergachev     ),
2067b7bdf199SArtem Dergachev     &CB
2068b7bdf199SArtem Dergachev   );
2069b7bdf199SArtem Dergachev   // clang-format on
2070b7bdf199SArtem Dergachev 
2071b7bdf199SArtem Dergachev   if (EmitSuggestions) {
2072b7bdf199SArtem Dergachev     // clang-format off
2073b7bdf199SArtem Dergachev     M.addMatcher(
2074b7bdf199SArtem Dergachev         stmt(
20759516419cSMalavikaSamak           forEachDescendantStmt(stmt(eachOf(
2076bdf4f2beSZiqing Luo #define FIXABLE_GADGET(x)                                                      \
2077a29e6761SZiqing Luo             x ## Gadget::matcher().bind(#x),
2078a29e6761SZiqing Luo #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
20799516419cSMalavikaSamak             // In parallel, match all DeclRefExprs so that to find out
20809516419cSMalavikaSamak             // whether there are any uncovered by gadgets.
2081b7bdf199SArtem Dergachev             declRefExpr(anyOf(hasPointerType(), hasArrayType()),
20822afcda69SRashmi Mudduluru                         to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
2083622be09cSZiqing Luo             // Also match DeclStmts because we'll need them when fixing
2084622be09cSZiqing Luo             // their underlying VarDecls that otherwise don't have
2085622be09cSZiqing Luo             // any backreferences to DeclStmts.
2086622be09cSZiqing Luo             declStmt().bind("any_ds")
2087b7bdf199SArtem Dergachev           )))
2088b7bdf199SArtem Dergachev       ),
20890d00a972SArtem Dergachev       &CB
20900d00a972SArtem Dergachev     );
20910d00a972SArtem Dergachev     // clang-format on
2092b7bdf199SArtem Dergachev   }
20933b7af279SArtem Dergachev 
2094c60b055dSZequan Wu   M.match(*S, Ctx);
20953b7af279SArtem Dergachev }
20963b7af279SArtem Dergachev 
2097148dc8a2Sziqingluo-90 // Compares AST nodes by source locations.
2098148dc8a2Sziqingluo-90 template <typename NodeTy> struct CompareNode {
2099148dc8a2Sziqingluo-90   bool operator()(const NodeTy *N1, const NodeTy *N2) const {
2100148dc8a2Sziqingluo-90     return N1->getBeginLoc().getRawEncoding() <
2101148dc8a2Sziqingluo-90            N2->getBeginLoc().getRawEncoding();
2102148dc8a2Sziqingluo-90   }
2103148dc8a2Sziqingluo-90 };
2104148dc8a2Sziqingluo-90 
2105214312efSJan Korous struct WarningGadgetSets {
2106171dfc54SRashmi Mudduluru   std::map<const VarDecl *, std::set<const WarningGadget *>,
2107148dc8a2Sziqingluo-90            // To keep keys sorted by their locations in the map so that the
2108148dc8a2Sziqingluo-90            // order is deterministic:
2109148dc8a2Sziqingluo-90            CompareNode<VarDecl>>
2110148dc8a2Sziqingluo-90       byVar;
2111214312efSJan Korous   // These Gadgets are not related to pointer variables (e. g. temporaries).
2112171dfc54SRashmi Mudduluru   llvm::SmallVector<const WarningGadget *, 16> noVar;
2113214312efSJan Korous };
2114214312efSJan Korous 
2115214312efSJan Korous static WarningGadgetSets
2116171dfc54SRashmi Mudduluru groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
2117214312efSJan Korous   WarningGadgetSets result;
2118214312efSJan Korous   // If some gadgets cover more than one
2119214312efSJan Korous   // variable, they'll appear more than once in the map.
2120214312efSJan Korous   for (auto &G : AllUnsafeOperations) {
2121214312efSJan Korous     DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
2122214312efSJan Korous 
2123214312efSJan Korous     bool AssociatedWithVarDecl = false;
2124214312efSJan Korous     for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
2125214312efSJan Korous       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2126171dfc54SRashmi Mudduluru         result.byVar[VD].insert(G.get());
2127214312efSJan Korous         AssociatedWithVarDecl = true;
2128214312efSJan Korous       }
2129214312efSJan Korous     }
2130214312efSJan Korous 
2131214312efSJan Korous     if (!AssociatedWithVarDecl) {
2132171dfc54SRashmi Mudduluru       result.noVar.push_back(G.get());
2133214312efSJan Korous       continue;
2134214312efSJan Korous     }
2135214312efSJan Korous   }
2136214312efSJan Korous   return result;
2137214312efSJan Korous }
2138214312efSJan Korous 
2139214312efSJan Korous struct FixableGadgetSets {
2140472a510bSziqingluo-90   std::map<const VarDecl *, std::set<const FixableGadget *>,
2141472a510bSziqingluo-90            // To keep keys sorted by their locations in the map so that the
2142472a510bSziqingluo-90            // order is deterministic:
2143472a510bSziqingluo-90            CompareNode<VarDecl>>
2144472a510bSziqingluo-90       byVar;
2145214312efSJan Korous };
2146214312efSJan Korous 
2147214312efSJan Korous static FixableGadgetSets
2148214312efSJan Korous groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
2149214312efSJan Korous   FixableGadgetSets FixablesForUnsafeVars;
2150214312efSJan Korous   for (auto &F : AllFixableOperations) {
2151214312efSJan Korous     DeclUseList DREs = F->getClaimedVarUseSites();
2152214312efSJan Korous 
2153214312efSJan Korous     for (const DeclRefExpr *DRE : DREs) {
2154214312efSJan Korous       if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2155171dfc54SRashmi Mudduluru         FixablesForUnsafeVars.byVar[VD].insert(F.get());
2156214312efSJan Korous       }
2157214312efSJan Korous     }
2158214312efSJan Korous   }
2159214312efSJan Korous   return FixablesForUnsafeVars;
2160214312efSJan Korous }
2161214312efSJan Korous 
2162148dc8a2Sziqingluo-90 bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts,
2163148dc8a2Sziqingluo-90                                   const SourceManager &SM) {
2164692da624SZiqing Luo   // A simple interval overlap detection algorithm.  Sorts all ranges by their
2165692da624SZiqing Luo   // begin location then finds the first overlap in one pass.
2166692da624SZiqing Luo   std::vector<const FixItHint *> All; // a copy of `FixIts`
2167692da624SZiqing Luo 
2168692da624SZiqing Luo   for (const FixItHint &H : FixIts)
2169692da624SZiqing Luo     All.push_back(&H);
2170692da624SZiqing Luo   std::sort(All.begin(), All.end(),
2171692da624SZiqing Luo             [&SM](const FixItHint *H1, const FixItHint *H2) {
2172692da624SZiqing Luo               return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
2173692da624SZiqing Luo                                                   H2->RemoveRange.getBegin());
2174692da624SZiqing Luo             });
2175692da624SZiqing Luo 
2176692da624SZiqing Luo   const FixItHint *CurrHint = nullptr;
2177692da624SZiqing Luo 
2178692da624SZiqing Luo   for (const FixItHint *Hint : All) {
2179692da624SZiqing Luo     if (!CurrHint ||
2180692da624SZiqing Luo         SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
2181692da624SZiqing Luo                                      Hint->RemoveRange.getBegin())) {
2182692da624SZiqing Luo       // Either to initialize `CurrHint` or `CurrHint` does not
2183692da624SZiqing Luo       // overlap with `Hint`:
2184692da624SZiqing Luo       CurrHint = Hint;
2185692da624SZiqing Luo     } else
2186692da624SZiqing Luo       // In case `Hint` overlaps the `CurrHint`, we found at least one
2187692da624SZiqing Luo       // conflict:
2188692da624SZiqing Luo       return true;
2189692da624SZiqing Luo   }
2190692da624SZiqing Luo   return false;
2191692da624SZiqing Luo }
2192692da624SZiqing Luo 
2193bdf4f2beSZiqing Luo std::optional<FixItList>
21946fce42f8Sjkorous-apple PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2195171dfc54SRashmi Mudduluru   const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
2196171dfc54SRashmi Mudduluru   const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
2197171dfc54SRashmi Mudduluru   switch (S.lookup(LeftVD)) {
2198644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Span:
2199644ac2a0Sjkorous-apple     if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2200171dfc54SRashmi Mudduluru       return FixItList{};
2201171dfc54SRashmi Mudduluru     return std::nullopt;
2202644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Wontfix:
2203171dfc54SRashmi Mudduluru     return std::nullopt;
2204644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Iterator:
2205644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Array:
2206644ac2a0Sjkorous-apple     return std::nullopt;
2207644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Vector:
2208171dfc54SRashmi Mudduluru     llvm_unreachable("unsupported strategies for FixableGadgets");
2209171dfc54SRashmi Mudduluru   }
2210171dfc54SRashmi Mudduluru   return std::nullopt;
2211171dfc54SRashmi Mudduluru }
2212171dfc54SRashmi Mudduluru 
22136fce42f8Sjkorous-apple /// \returns fixit that adds .data() call after \DRE.
22146fce42f8Sjkorous-apple static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
22156fce42f8Sjkorous-apple                                                        const DeclRefExpr *DRE);
22166fce42f8Sjkorous-apple 
22176fce42f8Sjkorous-apple std::optional<FixItList>
22186fce42f8Sjkorous-apple CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
22196fce42f8Sjkorous-apple   const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
22206fce42f8Sjkorous-apple   const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
22216fce42f8Sjkorous-apple   // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
22226fce42f8Sjkorous-apple   // non-trivial.
22236fce42f8Sjkorous-apple   //
22246fce42f8Sjkorous-apple   // CArrayToPtrAssignmentGadget doesn't have strategy implications because
22256fce42f8Sjkorous-apple   // constant size array propagates its bounds. Because of that LHS and RHS are
22266fce42f8Sjkorous-apple   // addressed by two different fixits.
22276fce42f8Sjkorous-apple   //
22286fce42f8Sjkorous-apple   // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
22296fce42f8Sjkorous-apple   // to and can't be generally relied on in multi-variable Fixables!
22306fce42f8Sjkorous-apple   //
22316fce42f8Sjkorous-apple   // E. g. If an instance of this gadget is fixing variable on LHS then the
22326fce42f8Sjkorous-apple   // variable on RHS is fixed by a different fixit and its strategy for LHS
22336fce42f8Sjkorous-apple   // fixit is as if Wontfix.
22346fce42f8Sjkorous-apple   //
22356fce42f8Sjkorous-apple   // The only exception is Wontfix strategy for a given variable as that is
22366fce42f8Sjkorous-apple   // valid for any fixit produced for the given input source code.
22376fce42f8Sjkorous-apple   if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
22386fce42f8Sjkorous-apple     if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
22396fce42f8Sjkorous-apple       return FixItList{};
22406fce42f8Sjkorous-apple     }
22416fce42f8Sjkorous-apple   } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
22426fce42f8Sjkorous-apple     if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
22436fce42f8Sjkorous-apple       return createDataFixit(RightVD->getASTContext(), PtrRHS);
22446fce42f8Sjkorous-apple     }
22456fce42f8Sjkorous-apple   }
22466fce42f8Sjkorous-apple   return std::nullopt;
22476fce42f8Sjkorous-apple }
22486fce42f8Sjkorous-apple 
2249db3dcedbSRashmi Mudduluru std::optional<FixItList>
2250644ac2a0Sjkorous-apple PointerInitGadget::getFixits(const FixitStrategy &S) const {
2251db3dcedbSRashmi Mudduluru   const auto *LeftVD = PtrInitLHS;
2252db3dcedbSRashmi Mudduluru   const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
2253db3dcedbSRashmi Mudduluru   switch (S.lookup(LeftVD)) {
2254644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Span:
2255644ac2a0Sjkorous-apple     if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2256db3dcedbSRashmi Mudduluru       return FixItList{};
2257db3dcedbSRashmi Mudduluru     return std::nullopt;
2258644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Wontfix:
2259db3dcedbSRashmi Mudduluru     return std::nullopt;
2260644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Iterator:
2261644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Array:
2262644ac2a0Sjkorous-apple     return std::nullopt;
2263644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Vector:
2264db3dcedbSRashmi Mudduluru     llvm_unreachable("unsupported strategies for FixableGadgets");
2265db3dcedbSRashmi Mudduluru   }
2266db3dcedbSRashmi Mudduluru   return std::nullopt;
2267db3dcedbSRashmi Mudduluru }
2268171dfc54SRashmi Mudduluru 
2269e1655a98SRashmi Mudduluru static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
2270e1655a98SRashmi Mudduluru                                      const ASTContext &Ctx) {
2271e1655a98SRashmi Mudduluru   if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
2272e1655a98SRashmi Mudduluru     if (ConstVal->isNegative())
2273e1655a98SRashmi Mudduluru       return false;
2274e1655a98SRashmi Mudduluru   } else if (!Expr->getType()->isUnsignedIntegerType())
2275e1655a98SRashmi Mudduluru     return false;
2276e1655a98SRashmi Mudduluru   return true;
2277e1655a98SRashmi Mudduluru }
2278e1655a98SRashmi Mudduluru 
2279171dfc54SRashmi Mudduluru std::optional<FixItList>
2280644ac2a0Sjkorous-apple ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2281148dc8a2Sziqingluo-90   if (const auto *DRE =
2282148dc8a2Sziqingluo-90           dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
2283bdf4f2beSZiqing Luo     if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2284bdf4f2beSZiqing Luo       switch (S.lookup(VD)) {
2285644ac2a0Sjkorous-apple       case FixitStrategy::Kind::Span: {
2286e1655a98SRashmi Mudduluru 
2287bdf4f2beSZiqing Luo         // If the index has a negative constant value, we give up as no valid
2288bdf4f2beSZiqing Luo         // fix-it can be generated:
2289bdf4f2beSZiqing Luo         const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
2290bdf4f2beSZiqing Luo             VD->getASTContext();
2291e1655a98SRashmi Mudduluru         if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
2292bdf4f2beSZiqing Luo           return std::nullopt;
2293bdf4f2beSZiqing Luo         // no-op is a good fix-it, otherwise
2294bdf4f2beSZiqing Luo         return FixItList{};
2295bdf4f2beSZiqing Luo       }
2296644ac2a0Sjkorous-apple       case FixitStrategy::Kind::Array:
2297644ac2a0Sjkorous-apple         return FixItList{};
2298644ac2a0Sjkorous-apple       case FixitStrategy::Kind::Wontfix:
2299644ac2a0Sjkorous-apple       case FixitStrategy::Kind::Iterator:
2300644ac2a0Sjkorous-apple       case FixitStrategy::Kind::Vector:
2301bdf4f2beSZiqing Luo         llvm_unreachable("unsupported strategies for FixableGadgets");
2302bdf4f2beSZiqing Luo       }
2303bdf4f2beSZiqing Luo     }
2304bdf4f2beSZiqing Luo   return std::nullopt;
2305bdf4f2beSZiqing Luo }
2306bdf4f2beSZiqing Luo 
2307ca6ceeb0SZiqing Luo static std::optional<FixItList> // forward declaration
2308ca6ceeb0SZiqing Luo fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
2309ca6ceeb0SZiqing Luo 
2310ca6ceeb0SZiqing Luo std::optional<FixItList>
2311644ac2a0Sjkorous-apple UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2312ca6ceeb0SZiqing Luo   auto DREs = getClaimedVarUseSites();
2313ca6ceeb0SZiqing Luo   const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
2314ca6ceeb0SZiqing Luo 
2315ca6ceeb0SZiqing Luo   switch (S.lookup(VD)) {
2316644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Span:
2317ca6ceeb0SZiqing Luo     return fixUPCAddressofArraySubscriptWithSpan(Node);
2318644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Wontfix:
2319644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Iterator:
2320644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Array:
2321644ac2a0Sjkorous-apple     return std::nullopt;
2322644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Vector:
2323ca6ceeb0SZiqing Luo     llvm_unreachable("unsupported strategies for FixableGadgets");
2324ca6ceeb0SZiqing Luo   }
2325ca6ceeb0SZiqing Luo   return std::nullopt; // something went wrong, no fix-it
2326ca6ceeb0SZiqing Luo }
2327ca6ceeb0SZiqing Luo 
23281e270be0Sziqingluo-90 // FIXME: this function should be customizable through format
23291e270be0Sziqingluo-90 static StringRef getEndOfLine() {
23301e270be0Sziqingluo-90   static const char *const EOL = "\n";
23311e270be0Sziqingluo-90   return EOL;
23321e270be0Sziqingluo-90 }
23331e270be0Sziqingluo-90 
2334a07a6f6cSziqingluo-90 // Returns the text indicating that the user needs to provide input there:
233565c36179SCongcong Cai static std::string
233665c36179SCongcong Cai getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
2337a07a6f6cSziqingluo-90   std::string s = std::string("<# ");
2338a07a6f6cSziqingluo-90   s += HintTextToUser;
2339a07a6f6cSziqingluo-90   s += " #>";
2340a07a6f6cSziqingluo-90   return s;
2341a07a6f6cSziqingluo-90 }
2342a07a6f6cSziqingluo-90 
2343bdf4f2beSZiqing Luo // Return the source location of the last character of the AST `Node`.
2344bdf4f2beSZiqing Luo template <typename NodeTy>
2345b63b2c23SMalavikaSamak static std::optional<SourceLocation>
2346b63b2c23SMalavikaSamak getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
2347bdf4f2beSZiqing Luo               const LangOptions &LangOpts) {
234863413015Sziqingluo-90   unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
234963413015Sziqingluo-90   SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
235063413015Sziqingluo-90 
2351b63b2c23SMalavikaSamak   if (Loc.isValid())
235263413015Sziqingluo-90     return Loc;
2353b63b2c23SMalavikaSamak 
2354b63b2c23SMalavikaSamak   return std::nullopt;
2355bdf4f2beSZiqing Luo }
2356bdf4f2beSZiqing Luo 
2357bdf4f2beSZiqing Luo // Return the source location just past the last character of the AST `Node`.
2358bdf4f2beSZiqing Luo template <typename NodeTy>
2359b63b2c23SMalavikaSamak static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
2360b63b2c23SMalavikaSamak                                                 const SourceManager &SM,
2361bdf4f2beSZiqing Luo                                                 const LangOptions &LangOpts) {
236263413015Sziqingluo-90   SourceLocation Loc =
236363413015Sziqingluo-90       Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
2364b63b2c23SMalavikaSamak   if (Loc.isValid())
236563413015Sziqingluo-90     return Loc;
2366b63b2c23SMalavikaSamak   return std::nullopt;
2367bdf4f2beSZiqing Luo }
2368bdf4f2beSZiqing Luo 
2369bdf4f2beSZiqing Luo // Return text representation of an `Expr`.
2370b63b2c23SMalavikaSamak static std::optional<StringRef> getExprText(const Expr *E,
2371b63b2c23SMalavikaSamak                                             const SourceManager &SM,
2372bdf4f2beSZiqing Luo                                             const LangOptions &LangOpts) {
2373b63b2c23SMalavikaSamak   std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
2374bdf4f2beSZiqing Luo 
2375b63b2c23SMalavikaSamak   if (LastCharLoc)
2376bdf4f2beSZiqing Luo     return Lexer::getSourceText(
2377b63b2c23SMalavikaSamak         CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
2378bdf4f2beSZiqing Luo         LangOpts);
2379b63b2c23SMalavikaSamak 
2380b63b2c23SMalavikaSamak   return std::nullopt;
2381bdf4f2beSZiqing Luo }
2382bdf4f2beSZiqing Luo 
23831e270be0Sziqingluo-90 // Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
23841e270be0Sziqingluo-90 static std::optional<StringRef> getRangeText(SourceRange SR,
23851e270be0Sziqingluo-90                                              const SourceManager &SM,
23861e270be0Sziqingluo-90                                              const LangOptions &LangOpts) {
23871e270be0Sziqingluo-90   bool Invalid = false;
2388472a510bSziqingluo-90   CharSourceRange CSR = CharSourceRange::getCharRange(SR);
23891e270be0Sziqingluo-90   StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
23901e270be0Sziqingluo-90 
23911e270be0Sziqingluo-90   if (!Invalid)
23921e270be0Sziqingluo-90     return Text;
23931e270be0Sziqingluo-90   return std::nullopt;
23941e270be0Sziqingluo-90 }
23951e270be0Sziqingluo-90 
239641279e87SZiqing Luo // Returns the begin location of the identifier of the given variable
239741279e87SZiqing Luo // declaration.
239841279e87SZiqing Luo static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) {
239941279e87SZiqing Luo   // According to the implementation of `VarDecl`, `VD->getLocation()` actually
240041279e87SZiqing Luo   // returns the begin location of the identifier of the declaration:
240141279e87SZiqing Luo   return VD->getLocation();
240241279e87SZiqing Luo }
240341279e87SZiqing Luo 
240441279e87SZiqing Luo // Returns the literal text of the identifier of the given variable declaration.
240541279e87SZiqing Luo static std::optional<StringRef>
240641279e87SZiqing Luo getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM,
240741279e87SZiqing Luo                          const LangOptions &LangOpts) {
240841279e87SZiqing Luo   SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
240941279e87SZiqing Luo   SourceLocation ParmIdentEndLoc =
241041279e87SZiqing Luo       Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
241141279e87SZiqing Luo 
241241279e87SZiqing Luo   if (ParmIdentEndLoc.isMacroID() &&
241341279e87SZiqing Luo       !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
241441279e87SZiqing Luo     return std::nullopt;
241541279e87SZiqing Luo   return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
241641279e87SZiqing Luo }
241741279e87SZiqing Luo 
2418b58e5288SZiqing Luo // We cannot fix a variable declaration if it has some other specifiers than the
2419b58e5288SZiqing Luo // type specifier.  Because the source ranges of those specifiers could overlap
2420b58e5288SZiqing Luo // with the source range that is being replaced using fix-its.  Especially when
2421b58e5288SZiqing Luo // we often cannot obtain accurate source ranges of cv-qualified type
2422b58e5288SZiqing Luo // specifiers.
2423b58e5288SZiqing Luo // FIXME: also deal with type attributes
2424b58e5288SZiqing Luo static bool hasUnsupportedSpecifiers(const VarDecl *VD,
2425b58e5288SZiqing Luo                                      const SourceManager &SM) {
2426b58e5288SZiqing Luo   // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
2427b58e5288SZiqing Luo   // source range of `VD`:
2428b58e5288SZiqing Luo   bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
2429b58e5288SZiqing Luo     return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
2430b58e5288SZiqing Luo                                           VD->getBeginLoc())) &&
2431b58e5288SZiqing Luo            !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
2432b58e5288SZiqing Luo                                           At->getRange().getBegin()));
2433b58e5288SZiqing Luo   });
2434b58e5288SZiqing Luo   return VD->isInlineSpecified() || VD->isConstexpr() ||
2435b58e5288SZiqing Luo          VD->hasConstantInitialization() || !VD->hasLocalStorage() ||
2436b58e5288SZiqing Luo          AttrRangeOverlapping;
2437b58e5288SZiqing Luo }
2438b58e5288SZiqing Luo 
243933f6161dSZiqing Luo // Returns the `SourceRange` of `D`.  The reason why this function exists is
244033f6161dSZiqing Luo // that `D->getSourceRange()` may return a range where the end location is the
244133f6161dSZiqing Luo // starting location of the last token.  The end location of the source range
244233f6161dSZiqing Luo // returned by this function is the last location of the last token.
244333f6161dSZiqing Luo static SourceRange getSourceRangeToTokenEnd(const Decl *D,
244433f6161dSZiqing Luo                                             const SourceManager &SM,
2445a9b30545Ssmanna12                                             const LangOptions &LangOpts) {
244633f6161dSZiqing Luo   SourceLocation Begin = D->getBeginLoc();
244733f6161dSZiqing Luo   SourceLocation
244833f6161dSZiqing Luo       End = // `D->getEndLoc` should always return the starting location of the
244933f6161dSZiqing Luo       // last token, so we should get the end of the token
245033f6161dSZiqing Luo       Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
245133f6161dSZiqing Luo 
245233f6161dSZiqing Luo   return SourceRange(Begin, End);
245333f6161dSZiqing Luo }
245433f6161dSZiqing Luo 
24551e270be0Sziqingluo-90 // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
24561e270be0Sziqingluo-90 // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does not
24571e270be0Sziqingluo-90 // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
24581e270be0Sziqingluo-90 // :( ), `Qualifiers` of the pointee type is returned separately through the
24591e270be0Sziqingluo-90 // output parameter `QualifiersToAppend`.
24601e270be0Sziqingluo-90 static std::optional<std::string>
246141279e87SZiqing Luo getPointeeTypeText(const VarDecl *VD, const SourceManager &SM,
24621e270be0Sziqingluo-90                    const LangOptions &LangOpts,
24631e270be0Sziqingluo-90                    std::optional<Qualifiers> *QualifiersToAppend) {
24641e270be0Sziqingluo-90   QualType Ty = VD->getType();
24651e270be0Sziqingluo-90   QualType PteTy;
24661e270be0Sziqingluo-90 
24671e270be0Sziqingluo-90   assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
24681e270be0Sziqingluo-90          "Expecting a VarDecl of type of pointer to object type");
24691e270be0Sziqingluo-90   PteTy = Ty->getPointeeType();
247041279e87SZiqing Luo 
247141279e87SZiqing Luo   TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
247241279e87SZiqing Luo   TypeLoc PteTyLoc;
247341279e87SZiqing Luo 
247441279e87SZiqing Luo   // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
247541279e87SZiqing Luo   // the `TypeLoc` of the pointee type:
247641279e87SZiqing Luo   switch (TyLoc.getTypeLocClass()) {
247741279e87SZiqing Luo   case TypeLoc::ConstantArray:
247841279e87SZiqing Luo   case TypeLoc::IncompleteArray:
247941279e87SZiqing Luo   case TypeLoc::VariableArray:
248041279e87SZiqing Luo   case TypeLoc::DependentSizedArray:
248141279e87SZiqing Luo   case TypeLoc::Decayed:
248241279e87SZiqing Luo     assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
248341279e87SZiqing Luo                                    "pointer type unless it decays.");
248441279e87SZiqing Luo     PteTyLoc = TyLoc.getNextTypeLoc();
248541279e87SZiqing Luo     break;
248641279e87SZiqing Luo   case TypeLoc::Pointer:
248741279e87SZiqing Luo     PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
248841279e87SZiqing Luo     break;
248941279e87SZiqing Luo   default:
249041279e87SZiqing Luo     return std::nullopt;
249141279e87SZiqing Luo   }
249241279e87SZiqing Luo   if (PteTyLoc.isNull())
249341279e87SZiqing Luo     // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
249441279e87SZiqing Luo     // when the pointer type is `auto`.
24951e270be0Sziqingluo-90     return std::nullopt;
24961e270be0Sziqingluo-90 
249741279e87SZiqing Luo   SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD);
24981e270be0Sziqingluo-90 
249941279e87SZiqing Luo   if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
2500a6302b69Sziqingluo-90     // We are expecting these locations to be valid. But in some cases, they are
2501a6302b69Sziqingluo-90     // not all valid. It is a Clang bug to me and we are not responsible for
2502a6302b69Sziqingluo-90     // fixing it.  So we will just give up for now when it happens.
2503a6302b69Sziqingluo-90     return std::nullopt;
2504a6302b69Sziqingluo-90   }
2505a6302b69Sziqingluo-90 
2506a6302b69Sziqingluo-90   // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
2507a6302b69Sziqingluo-90   SourceLocation PteEndOfTokenLoc =
2508a6302b69Sziqingluo-90       Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
2509a6302b69Sziqingluo-90 
251041279e87SZiqing Luo   if (!PteEndOfTokenLoc.isValid())
251141279e87SZiqing Luo     // Sometimes we cannot get the end location of the pointee type, e.g., when
251241279e87SZiqing Luo     // there are macros involved.
251341279e87SZiqing Luo     return std::nullopt;
251441279e87SZiqing Luo   if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
25151e270be0Sziqingluo-90     // We only deal with the cases where the source text of the pointee type
25161e270be0Sziqingluo-90     // appears on the left-hand side of the variable identifier completely,
25171e270be0Sziqingluo-90     // including the following forms:
25181e270be0Sziqingluo-90     // `T ident`,
25191e270be0Sziqingluo-90     // `T ident[]`, where `T` is any type.
25201e270be0Sziqingluo-90     // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
25211e270be0Sziqingluo-90     return std::nullopt;
25221e270be0Sziqingluo-90   }
25231e270be0Sziqingluo-90   if (PteTy.hasQualifiers()) {
25241e270be0Sziqingluo-90     // TypeLoc does not provide source ranges for qualifiers (it says it's
25251e270be0Sziqingluo-90     // intentional but seems fishy to me), so we cannot get the full text
25261e270be0Sziqingluo-90     // `PteTy` via source ranges.
25271e270be0Sziqingluo-90     *QualifiersToAppend = PteTy.getQualifiers();
25281e270be0Sziqingluo-90   }
2529a6302b69Sziqingluo-90   return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
2530a6302b69Sziqingluo-90       ->str();
25311e270be0Sziqingluo-90 }
25321e270be0Sziqingluo-90 
25331e270be0Sziqingluo-90 // Returns the text of the name (with qualifiers) of a `FunctionDecl`.
25341e270be0Sziqingluo-90 static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
25351e270be0Sziqingluo-90                                                const SourceManager &SM,
25361e270be0Sziqingluo-90                                                const LangOptions &LangOpts) {
25371e270be0Sziqingluo-90   SourceLocation BeginLoc = FD->getQualifier()
25381e270be0Sziqingluo-90                                 ? FD->getQualifierLoc().getBeginLoc()
25391e270be0Sziqingluo-90                                 : FD->getNameInfo().getBeginLoc();
25401e270be0Sziqingluo-90   // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
25411e270be0Sziqingluo-90   // last token:
25421e270be0Sziqingluo-90   SourceLocation EndLoc = Lexer::getLocForEndOfToken(
25431e270be0Sziqingluo-90       FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
25441e270be0Sziqingluo-90   SourceRange NameRange{BeginLoc, EndLoc};
25451e270be0Sziqingluo-90 
25461e270be0Sziqingluo-90   return getRangeText(NameRange, SM, LangOpts);
25471e270be0Sziqingluo-90 }
25481e270be0Sziqingluo-90 
254933f6161dSZiqing Luo // Returns the text representing a `std::span` type where the element type is
255033f6161dSZiqing Luo // represented by `EltTyText`.
255133f6161dSZiqing Luo //
255233f6161dSZiqing Luo // Note the optional parameter `Qualifiers`: one needs to pass qualifiers
255333f6161dSZiqing Luo // explicitly if the element type needs to be qualified.
255433f6161dSZiqing Luo static std::string
255533f6161dSZiqing Luo getSpanTypeText(StringRef EltTyText,
255633f6161dSZiqing Luo                 std::optional<Qualifiers> Quals = std::nullopt) {
255733f6161dSZiqing Luo   const char *const SpanOpen = "std::span<";
255833f6161dSZiqing Luo 
255933f6161dSZiqing Luo   if (Quals)
256033f6161dSZiqing Luo     return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
256133f6161dSZiqing Luo   return SpanOpen + EltTyText.str() + '>';
256233f6161dSZiqing Luo }
256333f6161dSZiqing Luo 
25646a0f2e53Sziqingluo-90 std::optional<FixItList>
2565644ac2a0Sjkorous-apple DerefSimplePtrArithFixableGadget::getFixits(const FixitStrategy &s) const {
25666a0f2e53Sziqingluo-90   const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
25676a0f2e53Sziqingluo-90 
2568644ac2a0Sjkorous-apple   if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
25696a0f2e53Sziqingluo-90     ASTContext &Ctx = VD->getASTContext();
25706a0f2e53Sziqingluo-90     // std::span can't represent elements before its begin()
25716a0f2e53Sziqingluo-90     if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
25726a0f2e53Sziqingluo-90       if (ConstVal->isNegative())
25736a0f2e53Sziqingluo-90         return std::nullopt;
25746a0f2e53Sziqingluo-90 
25756a0f2e53Sziqingluo-90     // note that the expr may (oddly) has multiple layers of parens
25766a0f2e53Sziqingluo-90     // example:
25776a0f2e53Sziqingluo-90     //   *((..(pointer + 123)..))
25786a0f2e53Sziqingluo-90     // goal:
25796a0f2e53Sziqingluo-90     //   pointer[123]
25806a0f2e53Sziqingluo-90     // Fix-It:
25816a0f2e53Sziqingluo-90     //   remove '*('
25826a0f2e53Sziqingluo-90     //   replace ' + ' with '['
25836a0f2e53Sziqingluo-90     //   replace ')' with ']'
25846a0f2e53Sziqingluo-90 
25856a0f2e53Sziqingluo-90     // example:
25866a0f2e53Sziqingluo-90     //   *((..(123 + pointer)..))
25876a0f2e53Sziqingluo-90     // goal:
25886a0f2e53Sziqingluo-90     //   123[pointer]
25896a0f2e53Sziqingluo-90     // Fix-It:
25906a0f2e53Sziqingluo-90     //   remove '*('
25916a0f2e53Sziqingluo-90     //   replace ' + ' with '['
25926a0f2e53Sziqingluo-90     //   replace ')' with ']'
25936a0f2e53Sziqingluo-90 
25946a0f2e53Sziqingluo-90     const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
25956a0f2e53Sziqingluo-90     const SourceManager &SM = Ctx.getSourceManager();
25966a0f2e53Sziqingluo-90     const LangOptions &LangOpts = Ctx.getLangOpts();
25976a0f2e53Sziqingluo-90     CharSourceRange StarWithTrailWhitespace =
25986a0f2e53Sziqingluo-90         clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
25996a0f2e53Sziqingluo-90                                              LHS->getBeginLoc());
2600b63b2c23SMalavikaSamak 
2601b63b2c23SMalavikaSamak     std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
2602b63b2c23SMalavikaSamak     if (!LHSLocation)
2603b63b2c23SMalavikaSamak       return std::nullopt;
2604b63b2c23SMalavikaSamak 
26056a0f2e53Sziqingluo-90     CharSourceRange PlusWithSurroundingWhitespace =
2606b63b2c23SMalavikaSamak         clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
2607b63b2c23SMalavikaSamak 
2608b63b2c23SMalavikaSamak     std::optional<SourceLocation> AddOpLocation =
2609b63b2c23SMalavikaSamak         getPastLoc(AddOp, SM, LangOpts);
2610b63b2c23SMalavikaSamak     std::optional<SourceLocation> DerefOpLocation =
2611b63b2c23SMalavikaSamak         getPastLoc(DerefOp, SM, LangOpts);
2612b63b2c23SMalavikaSamak 
2613b63b2c23SMalavikaSamak     if (!AddOpLocation || !DerefOpLocation)
2614b63b2c23SMalavikaSamak       return std::nullopt;
2615b63b2c23SMalavikaSamak 
26166a0f2e53Sziqingluo-90     CharSourceRange ClosingParenWithPrecWhitespace =
2617b63b2c23SMalavikaSamak         clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
26186a0f2e53Sziqingluo-90 
26196a0f2e53Sziqingluo-90     return FixItList{
26206a0f2e53Sziqingluo-90         {FixItHint::CreateRemoval(StarWithTrailWhitespace),
26216a0f2e53Sziqingluo-90          FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
26226a0f2e53Sziqingluo-90          FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
26236a0f2e53Sziqingluo-90   }
26246a0f2e53Sziqingluo-90   return std::nullopt; // something wrong or unsupported, give up
26256a0f2e53Sziqingluo-90 }
26266a0f2e53Sziqingluo-90 
2627e7596a99SMalavikaSamak std::optional<FixItList>
2628644ac2a0Sjkorous-apple PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
2629e7596a99SMalavikaSamak   const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
2630e7596a99SMalavikaSamak   switch (S.lookup(VD)) {
2631644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Span: {
2632e7596a99SMalavikaSamak     ASTContext &Ctx = VD->getASTContext();
2633e7596a99SMalavikaSamak     SourceManager &SM = Ctx.getSourceManager();
2634e7596a99SMalavikaSamak     // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
2635e7596a99SMalavikaSamak     // Deletes the *operand
2636e7596a99SMalavikaSamak     CharSourceRange derefRange = clang::CharSourceRange::getCharRange(
2637e7596a99SMalavikaSamak         Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
2638e7596a99SMalavikaSamak     // Inserts the [0]
2639700baeb7SZiqing Luo     if (auto LocPastOperand =
2640700baeb7SZiqing Luo             getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
2641e7596a99SMalavikaSamak       return FixItList{{FixItHint::CreateRemoval(derefRange),
2642700baeb7SZiqing Luo                         FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
2643e7596a99SMalavikaSamak     }
2644070358ecSRashmi Mudduluru     break;
2645e7596a99SMalavikaSamak   }
2646644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Iterator:
2647644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Array:
2648644ac2a0Sjkorous-apple     return std::nullopt;
2649644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Vector:
2650644ac2a0Sjkorous-apple     llvm_unreachable("FixitStrategy not implemented yet!");
2651644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Wontfix:
2652e7596a99SMalavikaSamak     llvm_unreachable("Invalid strategy!");
2653e7596a99SMalavikaSamak   }
2654e7596a99SMalavikaSamak 
2655e7596a99SMalavikaSamak   return std::nullopt;
2656e7596a99SMalavikaSamak }
2657e7596a99SMalavikaSamak 
26586fce42f8Sjkorous-apple static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
26596fce42f8Sjkorous-apple                                                        const DeclRefExpr *DRE) {
26606fce42f8Sjkorous-apple   const SourceManager &SM = Ctx.getSourceManager();
26616fce42f8Sjkorous-apple   // Inserts the .data() after the DRE
26626fce42f8Sjkorous-apple   std::optional<SourceLocation> EndOfOperand =
26636fce42f8Sjkorous-apple       getPastLoc(DRE, SM, Ctx.getLangOpts());
26646fce42f8Sjkorous-apple 
26656fce42f8Sjkorous-apple   if (EndOfOperand)
26666fce42f8Sjkorous-apple     return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
26676fce42f8Sjkorous-apple 
26686fce42f8Sjkorous-apple   return std::nullopt;
26696fce42f8Sjkorous-apple }
26706fce42f8Sjkorous-apple 
2671a046d187SMalavikaSamak // Generates fix-its replacing an expression of the form UPC(DRE) with
2672a046d187SMalavikaSamak // `DRE.data()`
2673644ac2a0Sjkorous-apple std::optional<FixItList>
2674644ac2a0Sjkorous-apple UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
2675a046d187SMalavikaSamak   const auto VD = cast<VarDecl>(Node->getDecl());
2676a046d187SMalavikaSamak   switch (S.lookup(VD)) {
2677e06f3522Sjkorous-apple   case FixitStrategy::Kind::Array:
2678644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Span: {
26796fce42f8Sjkorous-apple     return createDataFixit(VD->getASTContext(), Node);
2680070358ecSRashmi Mudduluru     // FIXME: Points inside a macro expansion.
2681070358ecSRashmi Mudduluru     break;
2682a046d187SMalavikaSamak   }
2683644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Wontfix:
2684644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Iterator:
2685644ac2a0Sjkorous-apple     return std::nullopt;
2686644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Vector:
2687a046d187SMalavikaSamak     llvm_unreachable("unsupported strategies for FixableGadgets");
2688a046d187SMalavikaSamak   }
2689a046d187SMalavikaSamak 
2690a046d187SMalavikaSamak   return std::nullopt;
2691a046d187SMalavikaSamak }
2692a046d187SMalavikaSamak 
2693ca6ceeb0SZiqing Luo // Generates fix-its replacing an expression of the form `&DRE[e]` with
2694ca6ceeb0SZiqing Luo // `&DRE.data()[e]`:
2695ca6ceeb0SZiqing Luo static std::optional<FixItList>
2696ca6ceeb0SZiqing Luo fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) {
2697ca6ceeb0SZiqing Luo   const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
2698ca6ceeb0SZiqing Luo   const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
2699ca6ceeb0SZiqing Luo   // FIXME: this `getASTContext` call is costly, we should pass the
2700ca6ceeb0SZiqing Luo   // ASTContext in:
2701ca6ceeb0SZiqing Luo   const ASTContext &Ctx = DRE->getDecl()->getASTContext();
2702ca6ceeb0SZiqing Luo   const Expr *Idx = ArraySub->getIdx();
2703ca6ceeb0SZiqing Luo   const SourceManager &SM = Ctx.getSourceManager();
2704ca6ceeb0SZiqing Luo   const LangOptions &LangOpts = Ctx.getLangOpts();
2705ca6ceeb0SZiqing Luo   std::stringstream SS;
2706ca6ceeb0SZiqing Luo   bool IdxIsLitZero = false;
2707ca6ceeb0SZiqing Luo 
2708ca6ceeb0SZiqing Luo   if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
2709ca6ceeb0SZiqing Luo     if ((*ICE).isZero())
2710ca6ceeb0SZiqing Luo       IdxIsLitZero = true;
2711b63b2c23SMalavikaSamak   std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
2712b63b2c23SMalavikaSamak   if (!DreString)
2713b63b2c23SMalavikaSamak     return std::nullopt;
2714b63b2c23SMalavikaSamak 
2715ca6ceeb0SZiqing Luo   if (IdxIsLitZero) {
2716ca6ceeb0SZiqing Luo     // If the index is literal zero, we produce the most concise fix-it:
2717b63b2c23SMalavikaSamak     SS << (*DreString).str() << ".data()";
2718ca6ceeb0SZiqing Luo   } else {
2719b63b2c23SMalavikaSamak     std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
2720b63b2c23SMalavikaSamak     if (!IndexString)
2721b63b2c23SMalavikaSamak       return std::nullopt;
2722b63b2c23SMalavikaSamak 
2723b63b2c23SMalavikaSamak     SS << "&" << (*DreString).str() << ".data()"
2724b63b2c23SMalavikaSamak        << "[" << (*IndexString).str() << "]";
2725ca6ceeb0SZiqing Luo   }
2726ca6ceeb0SZiqing Luo   return FixItList{
2727ca6ceeb0SZiqing Luo       FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
2728ca6ceeb0SZiqing Luo }
2729ca6ceeb0SZiqing Luo 
2730e1655a98SRashmi Mudduluru std::optional<FixItList>
2731644ac2a0Sjkorous-apple UUCAddAssignGadget::getFixits(const FixitStrategy &S) const {
2732e1655a98SRashmi Mudduluru   DeclUseList DREs = getClaimedVarUseSites();
2733e1655a98SRashmi Mudduluru 
2734e1655a98SRashmi Mudduluru   if (DREs.size() != 1)
2735e1655a98SRashmi Mudduluru     return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
2736e1655a98SRashmi Mudduluru                          // give up
2737e1655a98SRashmi Mudduluru   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2738644ac2a0Sjkorous-apple     if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2739e1655a98SRashmi Mudduluru       FixItList Fixes;
2740e1655a98SRashmi Mudduluru 
27415ac34358SDana Jansens       const Stmt *AddAssignNode = Node;
2742e1655a98SRashmi Mudduluru       StringRef varName = VD->getName();
2743e1655a98SRashmi Mudduluru       const ASTContext &Ctx = VD->getASTContext();
2744e1655a98SRashmi Mudduluru 
2745e1655a98SRashmi Mudduluru       if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
2746e1655a98SRashmi Mudduluru         return std::nullopt;
2747e1655a98SRashmi Mudduluru 
2748e1655a98SRashmi Mudduluru       // To transform UUC(p += n) to UUC(p = p.subspan(..)):
2749e1655a98SRashmi Mudduluru       bool NotParenExpr =
2750e1655a98SRashmi Mudduluru           (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
2751e1655a98SRashmi Mudduluru       std::string SS = varName.str() + " = " + varName.str() + ".subspan";
2752e1655a98SRashmi Mudduluru       if (NotParenExpr)
2753e1655a98SRashmi Mudduluru         SS += "(";
2754e1655a98SRashmi Mudduluru 
2755e1655a98SRashmi Mudduluru       std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
2756e1655a98SRashmi Mudduluru           AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
2757e1655a98SRashmi Mudduluru       if (!AddAssignLocation)
2758e1655a98SRashmi Mudduluru         return std::nullopt;
2759e1655a98SRashmi Mudduluru 
2760e1655a98SRashmi Mudduluru       Fixes.push_back(FixItHint::CreateReplacement(
2761e1655a98SRashmi Mudduluru           SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
2762e1655a98SRashmi Mudduluru           SS));
2763e1655a98SRashmi Mudduluru       if (NotParenExpr)
2764e1655a98SRashmi Mudduluru         Fixes.push_back(FixItHint::CreateInsertion(
2765e1655a98SRashmi Mudduluru             Offset->getEndLoc().getLocWithOffset(1), ")"));
2766e1655a98SRashmi Mudduluru       return Fixes;
2767e1655a98SRashmi Mudduluru     }
2768e1655a98SRashmi Mudduluru   }
2769e1655a98SRashmi Mudduluru   return std::nullopt; // Not in the cases that we can handle for now, give up.
2770e1655a98SRashmi Mudduluru }
2771762af11dSZiqing Luo 
2772644ac2a0Sjkorous-apple std::optional<FixItList>
2773644ac2a0Sjkorous-apple UPCPreIncrementGadget::getFixits(const FixitStrategy &S) const {
2774762af11dSZiqing Luo   DeclUseList DREs = getClaimedVarUseSites();
2775762af11dSZiqing Luo 
2776762af11dSZiqing Luo   if (DREs.size() != 1)
2777762af11dSZiqing Luo     return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
2778762af11dSZiqing Luo                          // give up
2779762af11dSZiqing Luo   if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2780644ac2a0Sjkorous-apple     if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2781762af11dSZiqing Luo       FixItList Fixes;
2782762af11dSZiqing Luo       std::stringstream SS;
2783762af11dSZiqing Luo       StringRef varName = VD->getName();
2784762af11dSZiqing Luo       const ASTContext &Ctx = VD->getASTContext();
2785762af11dSZiqing Luo 
2786762af11dSZiqing Luo       // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
2787762af11dSZiqing Luo       SS << "(" << varName.data() << " = " << varName.data()
2788762af11dSZiqing Luo          << ".subspan(1)).data()";
2789b63b2c23SMalavikaSamak       std::optional<SourceLocation> PreIncLocation =
27905ac34358SDana Jansens           getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts());
2791b63b2c23SMalavikaSamak       if (!PreIncLocation)
2792b63b2c23SMalavikaSamak         return std::nullopt;
2793b63b2c23SMalavikaSamak 
2794762af11dSZiqing Luo       Fixes.push_back(FixItHint::CreateReplacement(
27955ac34358SDana Jansens           SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
2796762af11dSZiqing Luo       return Fixes;
2797762af11dSZiqing Luo     }
2798762af11dSZiqing Luo   }
2799762af11dSZiqing Luo   return std::nullopt; // Not in the cases that we can handle for now, give up.
2800762af11dSZiqing Luo }
2801762af11dSZiqing Luo 
2802bdf4f2beSZiqing Luo // For a non-null initializer `Init` of `T *` type, this function returns
2803bdf4f2beSZiqing Luo // `FixItHint`s producing a list initializer `{Init,  S}` as a part of a fix-it
2804bdf4f2beSZiqing Luo // to output stream.
2805bdf4f2beSZiqing Luo // In many cases, this function cannot figure out the actual extent `S`.  It
2806bdf4f2beSZiqing Luo // then will use a place holder to replace `S` to ask users to fill `S` in.  The
2807bdf4f2beSZiqing Luo // initializer shall be used to initialize a variable of type `std::span<T>`.
28087c3ad9e7Sjkorous-apple // In some cases (e. g. constant size array) the initializer should remain
28097c3ad9e7Sjkorous-apple // unchanged and the function returns empty list. In case the function can't
28107c3ad9e7Sjkorous-apple // provide the right fixit it will return nullopt.
2811bdf4f2beSZiqing Luo //
2812bdf4f2beSZiqing Luo // FIXME: Support multi-level pointers
2813bdf4f2beSZiqing Luo //
2814bdf4f2beSZiqing Luo // Parameters:
2815bdf4f2beSZiqing Luo //   `Init` a pointer to the initializer expression
2816bdf4f2beSZiqing Luo //   `Ctx` a reference to the ASTContext
28177c3ad9e7Sjkorous-apple static std::optional<FixItList>
28183a67b912SZiqing Luo FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
2819bdf4f2beSZiqing Luo                           const StringRef UserFillPlaceHolder) {
2820bdf4f2beSZiqing Luo   const SourceManager &SM = Ctx.getSourceManager();
2821bdf4f2beSZiqing Luo   const LangOptions &LangOpts = Ctx.getLangOpts();
282263413015Sziqingluo-90 
282363413015Sziqingluo-90   // If `Init` has a constant value that is (or equivalent to) a
282463413015Sziqingluo-90   // NULL pointer, we use the default constructor to initialize the span
282563413015Sziqingluo-90   // object, i.e., a `std:span` variable declaration with no initializer.
282663413015Sziqingluo-90   // So the fix-it is just to remove the initializer.
28273fa91021Sjkorous-apple   if (Init->isNullPointerConstant(
28283fa91021Sjkorous-apple           Ctx,
282963413015Sziqingluo-90           // FIXME: Why does this function not ask for `const ASTContext
283063413015Sziqingluo-90           // &`? It should. Maybe worth an NFC patch later.
283163413015Sziqingluo-90           Expr::NullPointerConstantValueDependence::
283263413015Sziqingluo-90               NPC_ValueDependentIsNotNull)) {
2833b63b2c23SMalavikaSamak     std::optional<SourceLocation> InitLocation =
2834b63b2c23SMalavikaSamak         getEndCharLoc(Init, SM, LangOpts);
2835b63b2c23SMalavikaSamak     if (!InitLocation)
28367c3ad9e7Sjkorous-apple       return std::nullopt;
2837b63b2c23SMalavikaSamak 
2838b63b2c23SMalavikaSamak     SourceRange SR(Init->getBeginLoc(), *InitLocation);
283963413015Sziqingluo-90 
28407c3ad9e7Sjkorous-apple     return FixItList{FixItHint::CreateRemoval(SR)};
284163413015Sziqingluo-90   }
284263413015Sziqingluo-90 
284363413015Sziqingluo-90   FixItList FixIts{};
2844bdf4f2beSZiqing Luo   std::string ExtentText = UserFillPlaceHolder.data();
2845bdf4f2beSZiqing Luo   StringRef One = "1";
2846bdf4f2beSZiqing Luo 
2847bdf4f2beSZiqing Luo   // Insert `{` before `Init`:
2848bdf4f2beSZiqing Luo   FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
2849bdf4f2beSZiqing Luo   // Try to get the data extent. Break into different cases:
2850bdf4f2beSZiqing Luo   if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
2851bdf4f2beSZiqing Luo     // In cases `Init` is `new T[n]` and there is no explicit cast over
2852bdf4f2beSZiqing Luo     // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
2853bdf4f2beSZiqing Luo     // of `T`. So the extent is `n` unless `n` has side effects.  Similar but
2854bdf4f2beSZiqing Luo     // simpler for the case where `Init` is `new T`.
2855bdf4f2beSZiqing Luo     if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
2856b63b2c23SMalavikaSamak       if (!Ext->HasSideEffects(Ctx)) {
2857b63b2c23SMalavikaSamak         std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
2858b63b2c23SMalavikaSamak         if (!ExtentString)
28597c3ad9e7Sjkorous-apple           return std::nullopt;
2860b63b2c23SMalavikaSamak         ExtentText = *ExtentString;
2861b63b2c23SMalavikaSamak       }
2862bdf4f2beSZiqing Luo     } else if (!CxxNew->isArray())
2863bdf4f2beSZiqing Luo       // Although the initializer is not allocating a buffer, the pointer
2864bdf4f2beSZiqing Luo       // variable could still be used in buffer access operations.
2865bdf4f2beSZiqing Luo       ExtentText = One;
286619f6689fSMikael Holmen   } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
28677c3ad9e7Sjkorous-apple     // std::span has a single parameter constructor for initialization with
28687c3ad9e7Sjkorous-apple     // constant size array. The size is auto-deduced as the constructor is a
28697c3ad9e7Sjkorous-apple     // function template. The correct fixit is empty - no changes should happen.
28707c3ad9e7Sjkorous-apple     return FixItList{};
2871bdf4f2beSZiqing Luo   } else {
2872bdf4f2beSZiqing Luo     // In cases `Init` is of the form `&Var` after stripping of implicit
2873bdf4f2beSZiqing Luo     // casts, where `&` is the built-in operator, the extent is 1.
2874bdf4f2beSZiqing Luo     if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
2875bdf4f2beSZiqing Luo       if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
2876bdf4f2beSZiqing Luo           isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
2877bdf4f2beSZiqing Luo         ExtentText = One;
2878148dc8a2Sziqingluo-90     // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
2879148dc8a2Sziqingluo-90     // and explicit casting, etc. etc.
2880bdf4f2beSZiqing Luo   }
2881bdf4f2beSZiqing Luo 
2882bdf4f2beSZiqing Luo   SmallString<32> StrBuffer{};
2883b63b2c23SMalavikaSamak   std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
2884b63b2c23SMalavikaSamak 
2885b63b2c23SMalavikaSamak   if (!LocPassInit)
28867c3ad9e7Sjkorous-apple     return std::nullopt;
2887bdf4f2beSZiqing Luo 
2888bdf4f2beSZiqing Luo   StrBuffer.append(", ");
2889bdf4f2beSZiqing Luo   StrBuffer.append(ExtentText);
2890bdf4f2beSZiqing Luo   StrBuffer.append("}");
2891b63b2c23SMalavikaSamak   FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
2892bdf4f2beSZiqing Luo   return FixIts;
2893bdf4f2beSZiqing Luo }
2894bdf4f2beSZiqing Luo 
2895a6ae740eSRashmi Mudduluru #ifndef NDEBUG
2896a6ae740eSRashmi Mudduluru #define DEBUG_NOTE_DECL_FAIL(D, Msg)                                           \
28973fa91021Sjkorous-apple   Handler.addDebugNoteForVar((D), (D)->getBeginLoc(),                          \
28983fa91021Sjkorous-apple                              "failed to produce fixit for declaration '" +     \
28993fa91021Sjkorous-apple                                  (D)->getNameAsString() + "'" + (Msg))
2900a6ae740eSRashmi Mudduluru #else
2901a6ae740eSRashmi Mudduluru #define DEBUG_NOTE_DECL_FAIL(D, Msg)
2902a6ae740eSRashmi Mudduluru #endif
2903a6ae740eSRashmi Mudduluru 
290441279e87SZiqing Luo // For the given variable declaration with a pointer-to-T type, returns the text
290541279e87SZiqing Luo // `std::span<T>`.  If it is unable to generate the text, returns
290641279e87SZiqing Luo // `std::nullopt`.
29073fa91021Sjkorous-apple static std::optional<std::string>
29083fa91021Sjkorous-apple createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx) {
290941279e87SZiqing Luo   assert(VD->getType()->isPointerType());
291041279e87SZiqing Luo 
291141279e87SZiqing Luo   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
291241279e87SZiqing Luo   std::optional<std::string> PteTyText = getPointeeTypeText(
291341279e87SZiqing Luo       VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
291441279e87SZiqing Luo 
291541279e87SZiqing Luo   if (!PteTyText)
291641279e87SZiqing Luo     return std::nullopt;
291741279e87SZiqing Luo 
291841279e87SZiqing Luo   std::string SpanTyText = "std::span<";
291941279e87SZiqing Luo 
292041279e87SZiqing Luo   SpanTyText.append(*PteTyText);
292141279e87SZiqing Luo   // Append qualifiers to span element type if any:
292241279e87SZiqing Luo   if (PteTyQualifiers) {
292341279e87SZiqing Luo     SpanTyText.append(" ");
292441279e87SZiqing Luo     SpanTyText.append(PteTyQualifiers->getAsString());
292541279e87SZiqing Luo   }
292641279e87SZiqing Luo   SpanTyText.append(">");
292741279e87SZiqing Luo   return SpanTyText;
292841279e87SZiqing Luo }
292941279e87SZiqing Luo 
2930bdf4f2beSZiqing Luo // For a `VarDecl` of the form `T  * var (= Init)?`, this
29313a67b912SZiqing Luo // function generates fix-its that
29323a67b912SZiqing Luo //  1) replace `T * var` with `std::span<T> var`; and
29333a67b912SZiqing Luo //  2) change `Init` accordingly to a span constructor, if it exists.
2934bdf4f2beSZiqing Luo //
2935bdf4f2beSZiqing Luo // FIXME: support Multi-level pointers
2936bdf4f2beSZiqing Luo //
2937bdf4f2beSZiqing Luo // Parameters:
2938bdf4f2beSZiqing Luo //   `D` a pointer the variable declaration node
2939bdf4f2beSZiqing Luo //   `Ctx` a reference to the ASTContext
29403a67b912SZiqing Luo //   `UserFillPlaceHolder` the user-input placeholder text
2941bdf4f2beSZiqing Luo // Returns:
29423a67b912SZiqing Luo //    the non-empty fix-it list, if fix-its are successfuly generated; empty
29433a67b912SZiqing Luo //    list otherwise.
29443a67b912SZiqing Luo static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
2945a6ae740eSRashmi Mudduluru                                          const StringRef UserFillPlaceHolder,
2946a6ae740eSRashmi Mudduluru                                          UnsafeBufferUsageHandler &Handler) {
2947b58e5288SZiqing Luo   if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager()))
2948b58e5288SZiqing Luo     return {};
2949b58e5288SZiqing Luo 
2950bdf4f2beSZiqing Luo   FixItList FixIts{};
29513a67b912SZiqing Luo   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
2952bdf4f2beSZiqing Luo 
29533a67b912SZiqing Luo   if (!SpanTyText) {
29543a67b912SZiqing Luo     DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
29553a67b912SZiqing Luo     return {};
29563a67b912SZiqing Luo   }
29573a67b912SZiqing Luo 
29583a67b912SZiqing Luo   // Will hold the text for `std::span<T> Ident`:
29593a67b912SZiqing Luo   std::stringstream SS;
29603a67b912SZiqing Luo 
29613a67b912SZiqing Luo   SS << *SpanTyText;
29623a67b912SZiqing Luo   // Fix the initializer if it exists:
2963bdf4f2beSZiqing Luo   if (const Expr *Init = D->getInit()) {
29647c3ad9e7Sjkorous-apple     std::optional<FixItList> InitFixIts =
29653a67b912SZiqing Luo         FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
29667c3ad9e7Sjkorous-apple     if (!InitFixIts)
2967b63b2c23SMalavikaSamak       return {};
29687c3ad9e7Sjkorous-apple     FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
29697c3ad9e7Sjkorous-apple                   std::make_move_iterator(InitFixIts->end()));
29703a67b912SZiqing Luo   }
2971fde4b80cSjkorous-apple   // For declaration of the form `T * ident = init;`, we want to replace
2972fde4b80cSjkorous-apple   // `T * ` with `std::span<T>`.
2973fde4b80cSjkorous-apple   // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
2974fde4b80cSjkorous-apple   // just `T *` with `std::span<T>`.
2975fde4b80cSjkorous-apple   const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
29763a67b912SZiqing Luo   if (!EndLocForReplacement.isValid()) {
29773a67b912SZiqing Luo     DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
2978b63b2c23SMalavikaSamak     return {};
2979a6ae740eSRashmi Mudduluru   }
2980fde4b80cSjkorous-apple   // The only exception is that for `T *ident` we'll add a single space between
2981fde4b80cSjkorous-apple   // "std::span<T>" and "ident".
2982fde4b80cSjkorous-apple   // FIXME: The condition is false for identifiers expended from macros.
2983fde4b80cSjkorous-apple   if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
2984fde4b80cSjkorous-apple     SS << " ";
2985fde4b80cSjkorous-apple 
2986bdf4f2beSZiqing Luo   FixIts.push_back(FixItHint::CreateReplacement(
29873a67b912SZiqing Luo       SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
2988bdf4f2beSZiqing Luo   return FixIts;
2989bdf4f2beSZiqing Luo }
2990bdf4f2beSZiqing Luo 
29911e270be0Sziqingluo-90 static bool hasConflictingOverload(const FunctionDecl *FD) {
29921e270be0Sziqingluo-90   return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
29931e270be0Sziqingluo-90 }
29941e270be0Sziqingluo-90 
299533f6161dSZiqing Luo // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
299633f6161dSZiqing Luo // types, this function produces fix-its to make the change self-contained.  Let
299733f6161dSZiqing Luo // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
299833f6161dSZiqing Luo // entity defined by the `FunctionDecl` after the change to the parameters.
299933f6161dSZiqing Luo // Fix-its produced by this function are
30001e270be0Sziqingluo-90 //   1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
30011e270be0Sziqingluo-90 //   of 'F';
30021e270be0Sziqingluo-90 //   2. Create a declaration of "NewF" next to each declaration of `F`;
30031e270be0Sziqingluo-90 //   3. Create a definition of "F" (as its' original definition is now belongs
30041e270be0Sziqingluo-90 //      to "NewF") next to its original definition.  The body of the creating
30051e270be0Sziqingluo-90 //      definition calls to "NewF".
30061e270be0Sziqingluo-90 //
30071e270be0Sziqingluo-90 // Example:
30081e270be0Sziqingluo-90 //
30091e270be0Sziqingluo-90 // void f(int *p);  // original declaration
30101e270be0Sziqingluo-90 // void f(int *p) { // original definition
30111e270be0Sziqingluo-90 //    p[5];
30121e270be0Sziqingluo-90 // }
30131e270be0Sziqingluo-90 //
30141e270be0Sziqingluo-90 // To change the parameter `p` to be of `std::span<int>` type, we
30151e270be0Sziqingluo-90 // also add overloads:
30161e270be0Sziqingluo-90 //
30171e270be0Sziqingluo-90 // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
30181e270be0Sziqingluo-90 // void f(std::span<int> p);                      // added overload decl
30191e270be0Sziqingluo-90 // void f(std::span<int> p) {     // original def where param is changed
30201e270be0Sziqingluo-90 //    p[5];
30211e270be0Sziqingluo-90 // }
30221e270be0Sziqingluo-90 // [[clang::unsafe_buffer_usage]] void f(int *p) {  // added def
30231e270be0Sziqingluo-90 //   return f(std::span(p, <# size #>));
30241e270be0Sziqingluo-90 // }
30251e270be0Sziqingluo-90 //
30261e270be0Sziqingluo-90 static std::optional<FixItList>
3027644ac2a0Sjkorous-apple createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD,
3028700baeb7SZiqing Luo                               const ASTContext &Ctx,
3029c915908fSNAKAMURA Takumi                               UnsafeBufferUsageHandler &Handler) {
30301e270be0Sziqingluo-90   // FIXME: need to make this conflict checking better:
30311e270be0Sziqingluo-90   if (hasConflictingOverload(FD))
30321e270be0Sziqingluo-90     return std::nullopt;
30331e270be0Sziqingluo-90 
30341e270be0Sziqingluo-90   const SourceManager &SM = Ctx.getSourceManager();
30351e270be0Sziqingluo-90   const LangOptions &LangOpts = Ctx.getLangOpts();
303633f6161dSZiqing Luo   const unsigned NumParms = FD->getNumParams();
303733f6161dSZiqing Luo   std::vector<std::string> NewTysTexts(NumParms);
3038700baeb7SZiqing Luo   std::vector<bool> ParmsMask(NumParms, false);
3039700baeb7SZiqing Luo   bool AtLeastOneParmToFix = false;
304033f6161dSZiqing Luo 
304133f6161dSZiqing Luo   for (unsigned i = 0; i < NumParms; i++) {
3042700baeb7SZiqing Luo     const ParmVarDecl *PVD = FD->getParamDecl(i);
3043700baeb7SZiqing Luo 
3044644ac2a0Sjkorous-apple     if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
304533f6161dSZiqing Luo       continue;
3046644ac2a0Sjkorous-apple     if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3047700baeb7SZiqing Luo       // Not supported, not suppose to happen:
3048700baeb7SZiqing Luo       return std::nullopt;
304933f6161dSZiqing Luo 
305033f6161dSZiqing Luo     std::optional<Qualifiers> PteTyQuals = std::nullopt;
305133f6161dSZiqing Luo     std::optional<std::string> PteTyText =
3052700baeb7SZiqing Luo         getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
305333f6161dSZiqing Luo 
305433f6161dSZiqing Luo     if (!PteTyText)
305533f6161dSZiqing Luo       // something wrong in obtaining the text of the pointee type, give up
305633f6161dSZiqing Luo       return std::nullopt;
3057644ac2a0Sjkorous-apple     // FIXME: whether we should create std::span type depends on the
3058644ac2a0Sjkorous-apple     // FixitStrategy.
305933f6161dSZiqing Luo     NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3060700baeb7SZiqing Luo     ParmsMask[i] = true;
3061700baeb7SZiqing Luo     AtLeastOneParmToFix = true;
306233f6161dSZiqing Luo   }
3063700baeb7SZiqing Luo   if (!AtLeastOneParmToFix)
3064700baeb7SZiqing Luo     // No need to create function overloads:
3065700baeb7SZiqing Luo     return {};
30661e270be0Sziqingluo-90   // FIXME Respect indentation of the original code.
30671e270be0Sziqingluo-90 
30681e270be0Sziqingluo-90   // A lambda that creates the text representation of a function declaration
306933f6161dSZiqing Luo   // with the new type signatures:
30701e270be0Sziqingluo-90   const auto NewOverloadSignatureCreator =
307133f6161dSZiqing Luo       [&SM, &LangOpts, &NewTysTexts,
307233f6161dSZiqing Luo        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
30731e270be0Sziqingluo-90     std::stringstream SS;
30741e270be0Sziqingluo-90 
30751e270be0Sziqingluo-90     SS << ";";
30761e270be0Sziqingluo-90     SS << getEndOfLine().str();
30771e270be0Sziqingluo-90     // Append: ret-type func-name "("
30781e270be0Sziqingluo-90     if (auto Prefix = getRangeText(
30791e270be0Sziqingluo-90             SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
30801e270be0Sziqingluo-90             SM, LangOpts))
30811e270be0Sziqingluo-90       SS << Prefix->str();
30821e270be0Sziqingluo-90     else
30831e270be0Sziqingluo-90       return std::nullopt; // give up
30841e270be0Sziqingluo-90     // Append: parameter-type-list
30851e270be0Sziqingluo-90     const unsigned NumParms = FD->getNumParams();
30861e270be0Sziqingluo-90 
30871e270be0Sziqingluo-90     for (unsigned i = 0; i < NumParms; i++) {
30881e270be0Sziqingluo-90       const ParmVarDecl *Parm = FD->getParamDecl(i);
30891e270be0Sziqingluo-90 
30901e270be0Sziqingluo-90       if (Parm->isImplicit())
30911e270be0Sziqingluo-90         continue;
309233f6161dSZiqing Luo       if (ParmsMask[i]) {
309333f6161dSZiqing Luo         // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
309433f6161dSZiqing Luo         // new type:
309533f6161dSZiqing Luo         SS << NewTysTexts[i];
30961e270be0Sziqingluo-90         // print parameter name if provided:
30971e270be0Sziqingluo-90         if (IdentifierInfo *II = Parm->getIdentifier())
309833f6161dSZiqing Luo           SS << ' ' << II->getName().str();
30993fa91021Sjkorous-apple       } else if (auto ParmTypeText =
31003fa91021Sjkorous-apple                      getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
310133f6161dSZiqing Luo                                   SM, LangOpts)) {
31021e270be0Sziqingluo-90         // print the whole `Parm` without modification:
31031e270be0Sziqingluo-90         SS << ParmTypeText->str();
31041e270be0Sziqingluo-90       } else
31051e270be0Sziqingluo-90         return std::nullopt; // something wrong, give up
31061e270be0Sziqingluo-90       if (i != NumParms - 1)
31071e270be0Sziqingluo-90         SS << ", ";
31081e270be0Sziqingluo-90     }
31091e270be0Sziqingluo-90     SS << ")";
31101e270be0Sziqingluo-90     return SS.str();
31111e270be0Sziqingluo-90   };
31121e270be0Sziqingluo-90 
31131e270be0Sziqingluo-90   // A lambda that creates the text representation of a function definition with
31141e270be0Sziqingluo-90   // the original signature:
31151e270be0Sziqingluo-90   const auto OldOverloadDefCreator =
311633f6161dSZiqing Luo       [&Handler, &SM, &LangOpts, &NewTysTexts,
311733f6161dSZiqing Luo        &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
31181e270be0Sziqingluo-90     std::stringstream SS;
31191e270be0Sziqingluo-90 
31201e270be0Sziqingluo-90     SS << getEndOfLine().str();
31211e270be0Sziqingluo-90     // Append: attr-name ret-type func-name "(" param-list ")" "{"
31221e270be0Sziqingluo-90     if (auto FDPrefix = getRangeText(
31231e270be0Sziqingluo-90             SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
31241e270be0Sziqingluo-90             LangOpts))
3125a07a6f6cSziqingluo-90       SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
3126a07a6f6cSziqingluo-90          << FDPrefix->str() << "{";
31271e270be0Sziqingluo-90     else
31281e270be0Sziqingluo-90       return std::nullopt;
31291e270be0Sziqingluo-90     // Append: "return" func-name "("
31301e270be0Sziqingluo-90     if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
31311e270be0Sziqingluo-90       SS << "return " << FunQualName->str() << "(";
31321e270be0Sziqingluo-90     else
31331e270be0Sziqingluo-90       return std::nullopt;
31341e270be0Sziqingluo-90 
31351e270be0Sziqingluo-90     // Append: arg-list
31361e270be0Sziqingluo-90     const unsigned NumParms = FD->getNumParams();
31371e270be0Sziqingluo-90     for (unsigned i = 0; i < NumParms; i++) {
31381e270be0Sziqingluo-90       const ParmVarDecl *Parm = FD->getParamDecl(i);
31391e270be0Sziqingluo-90 
31401e270be0Sziqingluo-90       if (Parm->isImplicit())
31411e270be0Sziqingluo-90         continue;
31424b5f17e0SZiqing Luo       // FIXME: If a parameter has no name, it is unused in the
31434b5f17e0SZiqing Luo       // definition. So we could just leave it as it is.
31444b5f17e0SZiqing Luo       if (!Parm->getIdentifier())
31454b5f17e0SZiqing Luo         // If a parameter of a function definition has no name:
31464b5f17e0SZiqing Luo         return std::nullopt;
314733f6161dSZiqing Luo       if (ParmsMask[i])
31481e270be0Sziqingluo-90         // This is our spanified paramter!
314933f6161dSZiqing Luo         SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
315033f6161dSZiqing Luo            << ", " << getUserFillPlaceHolder("size") << ")";
31511e270be0Sziqingluo-90       else
31521e270be0Sziqingluo-90         SS << Parm->getIdentifier()->getName().str();
31531e270be0Sziqingluo-90       if (i != NumParms - 1)
31541e270be0Sziqingluo-90         SS << ", ";
31551e270be0Sziqingluo-90     }
31561e270be0Sziqingluo-90     // finish call and the body
31571e270be0Sziqingluo-90     SS << ");}" << getEndOfLine().str();
31581e270be0Sziqingluo-90     // FIXME: 80-char line formatting?
31591e270be0Sziqingluo-90     return SS.str();
31601e270be0Sziqingluo-90   };
31611e270be0Sziqingluo-90 
31621e270be0Sziqingluo-90   FixItList FixIts{};
31631e270be0Sziqingluo-90   for (FunctionDecl *FReDecl : FD->redecls()) {
31641e270be0Sziqingluo-90     std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
31651e270be0Sziqingluo-90 
31661e270be0Sziqingluo-90     if (!Loc)
31671e270be0Sziqingluo-90       return {};
31681e270be0Sziqingluo-90     if (FReDecl->isThisDeclarationADefinition()) {
31691e270be0Sziqingluo-90       assert(FReDecl == FD && "inconsistent function definition");
31701e270be0Sziqingluo-90       // Inserts a definition with the old signature to the end of
31711e270be0Sziqingluo-90       // `FReDecl`:
317233f6161dSZiqing Luo       if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
31731e270be0Sziqingluo-90         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
31741e270be0Sziqingluo-90       else
31751e270be0Sziqingluo-90         return {}; // give up
31761e270be0Sziqingluo-90     } else {
31771e270be0Sziqingluo-90       // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
31781e270be0Sziqingluo-90       if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
31791e270be0Sziqingluo-90         FixIts.emplace_back(FixItHint::CreateInsertion(
3180a07a6f6cSziqingluo-90             FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
3181a07a6f6cSziqingluo-90                                         FReDecl->getBeginLoc(), " ")));
31821e270be0Sziqingluo-90       }
31831e270be0Sziqingluo-90       // Inserts a declaration with the new signature to the end of `FReDecl`:
318433f6161dSZiqing Luo       if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
31851e270be0Sziqingluo-90         FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
31861e270be0Sziqingluo-90       else
31871e270be0Sziqingluo-90         return {};
31881e270be0Sziqingluo-90     }
31891e270be0Sziqingluo-90   }
31901e270be0Sziqingluo-90   return FixIts;
31911e270be0Sziqingluo-90 }
31921e270be0Sziqingluo-90 
319333f6161dSZiqing Luo // To fix a `ParmVarDecl` to be of `std::span` type.
31941e270be0Sziqingluo-90 static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
3195c915908fSNAKAMURA Takumi                                   UnsafeBufferUsageHandler &Handler) {
3196b58e5288SZiqing Luo   if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) {
3197b58e5288SZiqing Luo     DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
3198b58e5288SZiqing Luo     return {};
3199b58e5288SZiqing Luo   }
3200a6ae740eSRashmi Mudduluru   if (PVD->hasDefaultArg()) {
32011e270be0Sziqingluo-90     // FIXME: generate fix-its for default values:
3202a6ae740eSRashmi Mudduluru     DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
32031e270be0Sziqingluo-90     return {};
3204a6ae740eSRashmi Mudduluru   }
3205a6ae740eSRashmi Mudduluru 
320633f6161dSZiqing Luo   std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
320733f6161dSZiqing Luo   std::optional<std::string> PteTyText = getPointeeTypeText(
320833f6161dSZiqing Luo       PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
32091e270be0Sziqingluo-90 
321033f6161dSZiqing Luo   if (!PteTyText) {
321133f6161dSZiqing Luo     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
321233f6161dSZiqing Luo     return {};
321333f6161dSZiqing Luo   }
321433f6161dSZiqing Luo 
321533f6161dSZiqing Luo   std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
321633f6161dSZiqing Luo 
321733f6161dSZiqing Luo   if (!PVDNameText) {
321833f6161dSZiqing Luo     DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
32191e270be0Sziqingluo-90     return {};
3220a6ae740eSRashmi Mudduluru   }
32211e270be0Sziqingluo-90 
32221e270be0Sziqingluo-90   std::stringstream SS;
322341279e87SZiqing Luo   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
32241e270be0Sziqingluo-90 
322533f6161dSZiqing Luo   if (PteTyQualifiers)
322633f6161dSZiqing Luo     // Append qualifiers if they exist:
322733f6161dSZiqing Luo     SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
322833f6161dSZiqing Luo   else
322933f6161dSZiqing Luo     SS << getSpanTypeText(*PteTyText);
32301e270be0Sziqingluo-90   // Append qualifiers to the type of the parameter:
32311e270be0Sziqingluo-90   if (PVD->getType().hasQualifiers())
323233f6161dSZiqing Luo     SS << ' ' << PVD->getType().getQualifiers().getAsString();
32331e270be0Sziqingluo-90   // Append parameter's name:
323433f6161dSZiqing Luo   SS << ' ' << PVDNameText->str();
323533f6161dSZiqing Luo   // Add replacement fix-it:
323633f6161dSZiqing Luo   return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
32371e270be0Sziqingluo-90 }
32381e270be0Sziqingluo-90 
3239bdf4f2beSZiqing Luo static FixItList fixVariableWithSpan(const VarDecl *VD,
3240bdf4f2beSZiqing Luo                                      const DeclUseTracker &Tracker,
324110e83005Sziqingluo-90                                      ASTContext &Ctx,
3242bdf4f2beSZiqing Luo                                      UnsafeBufferUsageHandler &Handler) {
3243bdf4f2beSZiqing Luo   const DeclStmt *DS = Tracker.lookupDecl(VD);
3244cf1c64b9SRashmi Mudduluru   if (!DS) {
32453fa91021Sjkorous-apple     DEBUG_NOTE_DECL_FAIL(VD,
32463fa91021Sjkorous-apple                          " : variables declared this way not implemented yet");
3247cf1c64b9SRashmi Mudduluru     return {};
3248cf1c64b9SRashmi Mudduluru   }
3249bdf4f2beSZiqing Luo   if (!DS->isSingleDecl()) {
3250bdf4f2beSZiqing Luo     // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3251a6ae740eSRashmi Mudduluru     DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
3252bdf4f2beSZiqing Luo     return {};
3253bdf4f2beSZiqing Luo   }
3254bdf4f2beSZiqing Luo   // Currently DS is an unused variable but we'll need it when
3255bdf4f2beSZiqing Luo   // non-single decls are implemented, where the pointee type name
3256bdf4f2beSZiqing Luo   // and the '*' are spread around the place.
3257bdf4f2beSZiqing Luo   (void)DS;
3258bdf4f2beSZiqing Luo 
3259bdf4f2beSZiqing Luo   // FIXME: handle cases where DS has multiple declarations
32603a67b912SZiqing Luo   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
3261bdf4f2beSZiqing Luo }
3262bdf4f2beSZiqing Luo 
3263644ac2a0Sjkorous-apple static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
3264644ac2a0Sjkorous-apple                                      UnsafeBufferUsageHandler &Handler) {
3265644ac2a0Sjkorous-apple   FixItList FixIts{};
3266644ac2a0Sjkorous-apple 
3267644ac2a0Sjkorous-apple   // Note: the code below expects the declaration to not use any type sugar like
3268644ac2a0Sjkorous-apple   // typedef.
32699aa3b53aSZiqing Luo   if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
3270644ac2a0Sjkorous-apple     const QualType &ArrayEltT = CAT->getElementType();
3271644ac2a0Sjkorous-apple     assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
3272644ac2a0Sjkorous-apple     // FIXME: support multi-dimensional arrays
3273644ac2a0Sjkorous-apple     if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
3274644ac2a0Sjkorous-apple       return {};
3275644ac2a0Sjkorous-apple 
3276644ac2a0Sjkorous-apple     const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D);
3277644ac2a0Sjkorous-apple 
3278644ac2a0Sjkorous-apple     // Get the spelling of the element type as written in the source file
3279644ac2a0Sjkorous-apple     // (including macros, etc.).
3280644ac2a0Sjkorous-apple     auto MaybeElemTypeTxt =
3281644ac2a0Sjkorous-apple         getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(),
3282644ac2a0Sjkorous-apple                      Ctx.getLangOpts());
3283644ac2a0Sjkorous-apple     if (!MaybeElemTypeTxt)
3284644ac2a0Sjkorous-apple       return {};
3285644ac2a0Sjkorous-apple     const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
3286644ac2a0Sjkorous-apple 
3287644ac2a0Sjkorous-apple     // Find the '[' token.
3288644ac2a0Sjkorous-apple     std::optional<Token> NextTok = Lexer::findNextToken(
3289644ac2a0Sjkorous-apple         IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts());
3290644ac2a0Sjkorous-apple     while (NextTok && !NextTok->is(tok::l_square) &&
3291644ac2a0Sjkorous-apple            NextTok->getLocation() <= D->getSourceRange().getEnd())
3292644ac2a0Sjkorous-apple       NextTok = Lexer::findNextToken(NextTok->getLocation(),
3293644ac2a0Sjkorous-apple                                      Ctx.getSourceManager(), Ctx.getLangOpts());
3294644ac2a0Sjkorous-apple     if (!NextTok)
3295644ac2a0Sjkorous-apple       return {};
3296644ac2a0Sjkorous-apple     const SourceLocation LSqBracketLoc = NextTok->getLocation();
3297644ac2a0Sjkorous-apple 
3298644ac2a0Sjkorous-apple     // Get the spelling of the array size as written in the source file
3299644ac2a0Sjkorous-apple     // (including macros, etc.).
3300644ac2a0Sjkorous-apple     auto MaybeArraySizeTxt = getRangeText(
3301644ac2a0Sjkorous-apple         {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
3302644ac2a0Sjkorous-apple         Ctx.getSourceManager(), Ctx.getLangOpts());
3303644ac2a0Sjkorous-apple     if (!MaybeArraySizeTxt)
3304644ac2a0Sjkorous-apple       return {};
3305644ac2a0Sjkorous-apple     const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
3306644ac2a0Sjkorous-apple     if (ArraySizeTxt.empty()) {
3307644ac2a0Sjkorous-apple       // FIXME: Support array size getting determined from the initializer.
3308644ac2a0Sjkorous-apple       // Examples:
3309644ac2a0Sjkorous-apple       //    int arr1[] = {0, 1, 2};
3310644ac2a0Sjkorous-apple       //    int arr2{3, 4, 5};
3311644ac2a0Sjkorous-apple       // We might be able to preserve the non-specified size with `auto` and
3312644ac2a0Sjkorous-apple       // `std::to_array`:
3313644ac2a0Sjkorous-apple       //    auto arr1 = std::to_array<int>({0, 1, 2});
3314644ac2a0Sjkorous-apple       return {};
3315644ac2a0Sjkorous-apple     }
3316644ac2a0Sjkorous-apple 
3317644ac2a0Sjkorous-apple     std::optional<StringRef> IdentText =
3318644ac2a0Sjkorous-apple         getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
3319644ac2a0Sjkorous-apple 
3320644ac2a0Sjkorous-apple     if (!IdentText) {
3321644ac2a0Sjkorous-apple       DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
3322644ac2a0Sjkorous-apple       return {};
3323644ac2a0Sjkorous-apple     }
3324644ac2a0Sjkorous-apple 
3325644ac2a0Sjkorous-apple     SmallString<32> Replacement;
3326644ac2a0Sjkorous-apple     raw_svector_ostream OS(Replacement);
3327644ac2a0Sjkorous-apple     OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
3328644ac2a0Sjkorous-apple        << IdentText->str();
3329644ac2a0Sjkorous-apple 
3330644ac2a0Sjkorous-apple     FixIts.push_back(FixItHint::CreateReplacement(
3331644ac2a0Sjkorous-apple         SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
3332644ac2a0Sjkorous-apple   }
3333644ac2a0Sjkorous-apple 
3334644ac2a0Sjkorous-apple   return FixIts;
3335644ac2a0Sjkorous-apple }
3336644ac2a0Sjkorous-apple 
3337644ac2a0Sjkorous-apple static FixItList fixVariableWithArray(const VarDecl *VD,
3338644ac2a0Sjkorous-apple                                       const DeclUseTracker &Tracker,
3339644ac2a0Sjkorous-apple                                       const ASTContext &Ctx,
3340644ac2a0Sjkorous-apple                                       UnsafeBufferUsageHandler &Handler) {
3341644ac2a0Sjkorous-apple   const DeclStmt *DS = Tracker.lookupDecl(VD);
3342644ac2a0Sjkorous-apple   assert(DS && "Fixing non-local variables not implemented yet!");
3343644ac2a0Sjkorous-apple   if (!DS->isSingleDecl()) {
3344644ac2a0Sjkorous-apple     // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3345644ac2a0Sjkorous-apple     return {};
3346644ac2a0Sjkorous-apple   }
3347644ac2a0Sjkorous-apple   // Currently DS is an unused variable but we'll need it when
3348644ac2a0Sjkorous-apple   // non-single decls are implemented, where the pointee type name
3349644ac2a0Sjkorous-apple   // and the '*' are spread around the place.
3350644ac2a0Sjkorous-apple   (void)DS;
3351644ac2a0Sjkorous-apple 
3352644ac2a0Sjkorous-apple   // FIXME: handle cases where DS has multiple declarations
3353644ac2a0Sjkorous-apple   return fixVarDeclWithArray(VD, Ctx, Handler);
3354644ac2a0Sjkorous-apple }
3355644ac2a0Sjkorous-apple 
33561e270be0Sziqingluo-90 // TODO: we should be consistent to use `std::nullopt` to represent no-fix due
33571e270be0Sziqingluo-90 // to any unexpected problem.
33581e270be0Sziqingluo-90 static FixItList
3359644ac2a0Sjkorous-apple fixVariable(const VarDecl *VD, FixitStrategy::Kind K,
33601e270be0Sziqingluo-90             /* The function decl under analysis */ const Decl *D,
336110e83005Sziqingluo-90             const DeclUseTracker &Tracker, ASTContext &Ctx,
3362c915908fSNAKAMURA Takumi             UnsafeBufferUsageHandler &Handler) {
33631e270be0Sziqingluo-90   if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
33641e270be0Sziqingluo-90     auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
3365a6ae740eSRashmi Mudduluru     if (!FD || FD != D) {
33661e270be0Sziqingluo-90       // `FD != D` means that `PVD` belongs to a function that is not being
33671e270be0Sziqingluo-90       // analyzed currently.  Thus `FD` may not be complete.
3368a6ae740eSRashmi Mudduluru       DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
33691e270be0Sziqingluo-90       return {};
3370a6ae740eSRashmi Mudduluru     }
33711e270be0Sziqingluo-90 
33721e270be0Sziqingluo-90     // TODO If function has a try block we can't change params unless we check
33731e270be0Sziqingluo-90     // also its catch block for their use.
33741e270be0Sziqingluo-90     // FIXME We might support static class methods, some select methods,
33751e270be0Sziqingluo-90     // operators and possibly lamdas.
33761e270be0Sziqingluo-90     if (FD->isMain() || FD->isConstexpr() ||
33771e270be0Sziqingluo-90         FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
33781e270be0Sziqingluo-90         FD->isVariadic() ||
33791e270be0Sziqingluo-90         // also covers call-operator of lamdas
33801e270be0Sziqingluo-90         isa<CXXMethodDecl>(FD) ||
33811e270be0Sziqingluo-90         // skip when the function body is a try-block
33821e270be0Sziqingluo-90         (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
3383a6ae740eSRashmi Mudduluru         FD->isOverloadedOperator()) {
3384a6ae740eSRashmi Mudduluru       DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
33851e270be0Sziqingluo-90       return {}; // TODO test all these cases
33861e270be0Sziqingluo-90     }
3387a6ae740eSRashmi Mudduluru   }
33881e270be0Sziqingluo-90 
3389bdf4f2beSZiqing Luo   switch (K) {
3390644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Span: {
33911e270be0Sziqingluo-90     if (VD->getType()->isPointerType()) {
33921e270be0Sziqingluo-90       if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
3393c915908fSNAKAMURA Takumi         return fixParamWithSpan(PVD, Ctx, Handler);
33941e270be0Sziqingluo-90 
33951e270be0Sziqingluo-90       if (VD->isLocalVarDecl())
3396bdf4f2beSZiqing Luo         return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
33971e270be0Sziqingluo-90     }
3398a6ae740eSRashmi Mudduluru     DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
3399bdf4f2beSZiqing Luo     return {};
3400bdf4f2beSZiqing Luo   }
3401644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Array: {
340299313575SFangrui Song     if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
3403644ac2a0Sjkorous-apple       return fixVariableWithArray(VD, Tracker, Ctx, Handler);
3404644ac2a0Sjkorous-apple 
3405644ac2a0Sjkorous-apple     DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
3406644ac2a0Sjkorous-apple     return {};
3407644ac2a0Sjkorous-apple   }
3408644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Iterator:
3409644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Vector:
3410644ac2a0Sjkorous-apple     llvm_unreachable("FixitStrategy not implemented yet!");
3411644ac2a0Sjkorous-apple   case FixitStrategy::Kind::Wontfix:
3412bdf4f2beSZiqing Luo     llvm_unreachable("Invalid strategy!");
3413bdf4f2beSZiqing Luo   }
3414bdf4f2beSZiqing Luo   llvm_unreachable("Unknown strategy!");
3415bdf4f2beSZiqing Luo }
3416bdf4f2beSZiqing Luo 
3417bdf4f2beSZiqing Luo // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
3418bdf4f2beSZiqing Luo // `RemoveRange` of 'h' overlaps with a macro use.
3419bdf4f2beSZiqing Luo static bool overlapWithMacro(const FixItList &FixIts) {
3420bdf4f2beSZiqing Luo   // FIXME: For now we only check if the range (or the first token) is (part of)
3421bdf4f2beSZiqing Luo   // a macro expansion.  Ideally, we want to check for all tokens in the range.
3422bdf4f2beSZiqing Luo   return llvm::any_of(FixIts, [](const FixItHint &Hint) {
342363413015Sziqingluo-90     auto Range = Hint.RemoveRange;
342463413015Sziqingluo-90     if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
3425bdf4f2beSZiqing Luo       // If the range (or the first token) is (part of) a macro expansion:
3426bdf4f2beSZiqing Luo       return true;
3427bdf4f2beSZiqing Luo     return false;
3428bdf4f2beSZiqing Luo   });
3429bdf4f2beSZiqing Luo }
3430bdf4f2beSZiqing Luo 
343133f6161dSZiqing Luo // Returns true iff `VD` is a parameter of the declaration `D`:
343233f6161dSZiqing Luo static bool isParameterOf(const VarDecl *VD, const Decl *D) {
343333f6161dSZiqing Luo   return isa<ParmVarDecl>(VD) &&
343433f6161dSZiqing Luo          VD->getDeclContext() == dyn_cast<DeclContext>(D);
343533f6161dSZiqing Luo }
343633f6161dSZiqing Luo 
3437acc8a33bSziqingluo-90 // Erases variables in `FixItsForVariable`, if such a variable has an unfixable
3438acc8a33bSziqingluo-90 // group mate.  A variable `v` is unfixable iff `FixItsForVariable` does not
3439acc8a33bSziqingluo-90 // contain `v`.
3440acc8a33bSziqingluo-90 static void eraseVarsForUnfixableGroupMates(
3441acc8a33bSziqingluo-90     std::map<const VarDecl *, FixItList> &FixItsForVariable,
3442acc8a33bSziqingluo-90     const VariableGroupsManager &VarGrpMgr) {
3443acc8a33bSziqingluo-90   // Variables will be removed from `FixItsForVariable`:
3444acc8a33bSziqingluo-90   SmallVector<const VarDecl *, 8> ToErase;
3445acc8a33bSziqingluo-90 
344606c9cc7eSManna, Soumi   for (const auto &[VD, Ignore] : FixItsForVariable) {
3447acc8a33bSziqingluo-90     VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
3448acc8a33bSziqingluo-90     if (llvm::any_of(Grp,
3449acc8a33bSziqingluo-90                      [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
345033f6161dSZiqing Luo                        return !FixItsForVariable.count(GrpMember);
3451acc8a33bSziqingluo-90                      })) {
3452acc8a33bSziqingluo-90       // At least one group member cannot be fixed, so we have to erase the
3453acc8a33bSziqingluo-90       // whole group:
3454acc8a33bSziqingluo-90       for (const VarDecl *Member : Grp)
3455acc8a33bSziqingluo-90         ToErase.push_back(Member);
3456171dfc54SRashmi Mudduluru     }
3457171dfc54SRashmi Mudduluru   }
3458acc8a33bSziqingluo-90   for (auto *VarToErase : ToErase)
3459acc8a33bSziqingluo-90     FixItsForVariable.erase(VarToErase);
3460171dfc54SRashmi Mudduluru }
3461171dfc54SRashmi Mudduluru 
346233f6161dSZiqing Luo // Returns the fix-its that create bounds-safe function overloads for the
346333f6161dSZiqing Luo // function `D`, if `D`'s parameters will be changed to safe-types through
346433f6161dSZiqing Luo // fix-its in `FixItsForVariable`.
346533f6161dSZiqing Luo //
346633f6161dSZiqing Luo // NOTE: In case `D`'s parameters will be changed but bounds-safe function
346733f6161dSZiqing Luo // overloads cannot created, the whole group that contains the parameters will
346833f6161dSZiqing Luo // be erased from `FixItsForVariable`.
346933f6161dSZiqing Luo static FixItList createFunctionOverloadsForParms(
347033f6161dSZiqing Luo     std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
347133f6161dSZiqing Luo     const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
3472644ac2a0Sjkorous-apple     const FixitStrategy &S, ASTContext &Ctx,
3473644ac2a0Sjkorous-apple     UnsafeBufferUsageHandler &Handler) {
347433f6161dSZiqing Luo   FixItList FixItsSharedByParms{};
347533f6161dSZiqing Luo 
347633f6161dSZiqing Luo   std::optional<FixItList> OverloadFixes =
3477700baeb7SZiqing Luo       createOverloadsForFixedParams(S, FD, Ctx, Handler);
347833f6161dSZiqing Luo 
347933f6161dSZiqing Luo   if (OverloadFixes) {
348033f6161dSZiqing Luo     FixItsSharedByParms.append(*OverloadFixes);
348133f6161dSZiqing Luo   } else {
348233f6161dSZiqing Luo     // Something wrong in generating `OverloadFixes`, need to remove the
348333f6161dSZiqing Luo     // whole group, where parameters are in, from `FixItsForVariable` (Note
348433f6161dSZiqing Luo     // that all parameters should be in the same group):
3485700baeb7SZiqing Luo     for (auto *Member : VarGrpMgr.getGroupOfParms())
348633f6161dSZiqing Luo       FixItsForVariable.erase(Member);
348733f6161dSZiqing Luo   }
348833f6161dSZiqing Luo   return FixItsSharedByParms;
348933f6161dSZiqing Luo }
349033f6161dSZiqing Luo 
349133f6161dSZiqing Luo // Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
3492214312efSJan Korous static std::map<const VarDecl *, FixItList>
3493644ac2a0Sjkorous-apple getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
349410e83005Sziqingluo-90           ASTContext &Ctx,
34951e270be0Sziqingluo-90           /* The function decl under analysis */ const Decl *D,
34961e270be0Sziqingluo-90           const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
3497472a510bSziqingluo-90           const VariableGroupsManager &VarGrpMgr) {
3498acc8a33bSziqingluo-90   // `FixItsForVariable` will map each variable to a set of fix-its directly
3499acc8a33bSziqingluo-90   // associated to the variable itself.  Fix-its of distinct variables in
3500acc8a33bSziqingluo-90   // `FixItsForVariable` are disjoint.
3501214312efSJan Korous   std::map<const VarDecl *, FixItList> FixItsForVariable;
3502acc8a33bSziqingluo-90 
3503acc8a33bSziqingluo-90   // Populate `FixItsForVariable` with fix-its directly associated with each
3504acc8a33bSziqingluo-90   // variable.  Fix-its directly associated to a variable 'v' are the ones
3505acc8a33bSziqingluo-90   // produced by the `FixableGadget`s whose claimed variable is 'v'.
3506db3dcedbSRashmi Mudduluru   for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
3507148dc8a2Sziqingluo-90     FixItsForVariable[VD] =
3508c915908fSNAKAMURA Takumi         fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
3509bdf4f2beSZiqing Luo     // If we fail to produce Fix-It for the declaration we have to skip the
3510bdf4f2beSZiqing Luo     // variable entirely.
3511bdf4f2beSZiqing Luo     if (FixItsForVariable[VD].empty()) {
3512bdf4f2beSZiqing Luo       FixItsForVariable.erase(VD);
3513bdf4f2beSZiqing Luo       continue;
3514bdf4f2beSZiqing Luo     }
3515214312efSJan Korous     for (const auto &F : Fixables) {
35165c9013e2SKazu Hirata       std::optional<FixItList> Fixits = F->getFixits(S);
3517acc8a33bSziqingluo-90 
3518acc8a33bSziqingluo-90       if (Fixits) {
3519acc8a33bSziqingluo-90         FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
3520acc8a33bSziqingluo-90                                      Fixits->begin(), Fixits->end());
3521acc8a33bSziqingluo-90         continue;
3522acc8a33bSziqingluo-90       }
3523a6ae740eSRashmi Mudduluru #ifndef NDEBUG
3524a6ae740eSRashmi Mudduluru       Handler.addDebugNoteForVar(
35255ac34358SDana Jansens           VD, F->getSourceLoc(),
3526a6ae740eSRashmi Mudduluru           ("gadget '" + F->getDebugName() + "' refused to produce a fix")
3527a6ae740eSRashmi Mudduluru               .str());
3528a6ae740eSRashmi Mudduluru #endif
3529214312efSJan Korous       FixItsForVariable.erase(VD);
3530171dfc54SRashmi Mudduluru       break;
3531171dfc54SRashmi Mudduluru     }
3532171dfc54SRashmi Mudduluru   }
3533171dfc54SRashmi Mudduluru 
3534acc8a33bSziqingluo-90   // `FixItsForVariable` now contains only variables that can be
3535acc8a33bSziqingluo-90   // fixed. A variable can be fixed if its' declaration and all Fixables
3536acc8a33bSziqingluo-90   // associated to it can all be fixed.
3537171dfc54SRashmi Mudduluru 
3538acc8a33bSziqingluo-90   // To further remove from `FixItsForVariable` variables whose group mates
3539acc8a33bSziqingluo-90   // cannot be fixed...
3540acc8a33bSziqingluo-90   eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
3541acc8a33bSziqingluo-90   // Now `FixItsForVariable` gets further reduced: a variable is in
3542acc8a33bSziqingluo-90   // `FixItsForVariable` iff it can be fixed and all its group mates can be
3543acc8a33bSziqingluo-90   // fixed.
3544171dfc54SRashmi Mudduluru 
354533f6161dSZiqing Luo   // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
354633f6161dSZiqing Luo   // That is,  when fixing multiple parameters in one step,  these fix-its will
354733f6161dSZiqing Luo   // be applied only once (instead of being applied per parameter).
354833f6161dSZiqing Luo   FixItList FixItsSharedByParms{};
354933f6161dSZiqing Luo 
355033f6161dSZiqing Luo   if (auto *FD = dyn_cast<FunctionDecl>(D))
355133f6161dSZiqing Luo     FixItsSharedByParms = createFunctionOverloadsForParms(
355233f6161dSZiqing Luo         FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
355333f6161dSZiqing Luo 
3554472a510bSziqingluo-90   // The map that maps each variable `v` to fix-its for the whole group where
3555472a510bSziqingluo-90   // `v` is in:
3556472a510bSziqingluo-90   std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
3557472a510bSziqingluo-90       FixItsForVariable};
3558472a510bSziqingluo-90 
3559472a510bSziqingluo-90   for (auto &[Var, Ignore] : FixItsForVariable) {
356033f6161dSZiqing Luo     bool AnyParm = false;
356133f6161dSZiqing Luo     const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
3562472a510bSziqingluo-90 
3563472a510bSziqingluo-90     for (const VarDecl *GrpMate : VarGroupForVD) {
3564472a510bSziqingluo-90       if (Var == GrpMate)
3565171dfc54SRashmi Mudduluru         continue;
3566472a510bSziqingluo-90       if (FixItsForVariable.count(GrpMate))
356733f6161dSZiqing Luo         FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
356833f6161dSZiqing Luo     }
356933f6161dSZiqing Luo     if (AnyParm) {
357033f6161dSZiqing Luo       // This assertion should never fail.  Otherwise we have a bug.
357133f6161dSZiqing Luo       assert(!FixItsSharedByParms.empty() &&
357233f6161dSZiqing Luo              "Should not try to fix a parameter that does not belong to a "
357333f6161dSZiqing Luo              "FunctionDecl");
357433f6161dSZiqing Luo       FinalFixItsForVariable[Var].append(FixItsSharedByParms);
3575171dfc54SRashmi Mudduluru     }
3576171dfc54SRashmi Mudduluru   }
3577acc8a33bSziqingluo-90   // Fix-its that will be applied in one step shall NOT:
3578acc8a33bSziqingluo-90   // 1. overlap with macros or/and templates; or
3579acc8a33bSziqingluo-90   // 2. conflict with each other.
3580acc8a33bSziqingluo-90   // Otherwise, the fix-its will be dropped.
3581acc8a33bSziqingluo-90   for (auto Iter = FinalFixItsForVariable.begin();
3582acc8a33bSziqingluo-90        Iter != FinalFixItsForVariable.end();)
3583acc8a33bSziqingluo-90     if (overlapWithMacro(Iter->second) ||
3584acc8a33bSziqingluo-90         clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
3585acc8a33bSziqingluo-90       Iter = FinalFixItsForVariable.erase(Iter);
3586acc8a33bSziqingluo-90     } else
3587acc8a33bSziqingluo-90       Iter++;
3588472a510bSziqingluo-90   return FinalFixItsForVariable;
3589214312efSJan Korous }
3590214312efSJan Korous 
3591700baeb7SZiqing Luo template <typename VarDeclIterTy>
3592644ac2a0Sjkorous-apple static FixitStrategy
3593700baeb7SZiqing Luo getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
3594644ac2a0Sjkorous-apple   FixitStrategy S;
3595214312efSJan Korous   for (const VarDecl *VD : UnsafeVars) {
3596644ac2a0Sjkorous-apple     if (isa<ConstantArrayType>(VD->getType().getCanonicalType()))
3597644ac2a0Sjkorous-apple       S.set(VD, FixitStrategy::Kind::Array);
3598644ac2a0Sjkorous-apple     else
3599644ac2a0Sjkorous-apple       S.set(VD, FixitStrategy::Kind::Span);
3600214312efSJan Korous   }
3601214312efSJan Korous   return S;
3602214312efSJan Korous }
3603214312efSJan Korous 
3604472a510bSziqingluo-90 //  Manages variable groups:
3605472a510bSziqingluo-90 class VariableGroupsManagerImpl : public VariableGroupsManager {
3606472a510bSziqingluo-90   const std::vector<VarGrpTy> Groups;
3607472a510bSziqingluo-90   const std::map<const VarDecl *, unsigned> &VarGrpMap;
360833f6161dSZiqing Luo   const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
3609472a510bSziqingluo-90 
3610472a510bSziqingluo-90 public:
3611472a510bSziqingluo-90   VariableGroupsManagerImpl(
3612472a510bSziqingluo-90       const std::vector<VarGrpTy> &Groups,
361333f6161dSZiqing Luo       const std::map<const VarDecl *, unsigned> &VarGrpMap,
361433f6161dSZiqing Luo       const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
361533f6161dSZiqing Luo       : Groups(Groups), VarGrpMap(VarGrpMap),
361633f6161dSZiqing Luo         GrpsUnionForParms(GrpsUnionForParms) {}
3617472a510bSziqingluo-90 
361833f6161dSZiqing Luo   VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
361933f6161dSZiqing Luo     if (GrpsUnionForParms.contains(Var)) {
362033f6161dSZiqing Luo       if (HasParm)
362133f6161dSZiqing Luo         *HasParm = true;
362233f6161dSZiqing Luo       return GrpsUnionForParms.getArrayRef();
362333f6161dSZiqing Luo     }
362433f6161dSZiqing Luo     if (HasParm)
362533f6161dSZiqing Luo       *HasParm = false;
362633f6161dSZiqing Luo 
362733f6161dSZiqing Luo     auto It = VarGrpMap.find(Var);
362833f6161dSZiqing Luo 
362933f6161dSZiqing Luo     if (It == VarGrpMap.end())
36304dd55c56SJay Foad       return {};
363133f6161dSZiqing Luo     return Groups[It->second];
3632472a510bSziqingluo-90   }
3633700baeb7SZiqing Luo 
3634700baeb7SZiqing Luo   VarGrpRef getGroupOfParms() const override {
3635700baeb7SZiqing Luo     return GrpsUnionForParms.getArrayRef();
3636700baeb7SZiqing Luo   }
3637472a510bSziqingluo-90 };
3638472a510bSziqingluo-90 
3639c60b055dSZequan Wu void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
3640c60b055dSZequan Wu                   WarningGadgetList WarningGadgets, DeclUseTracker Tracker,
3641c60b055dSZequan Wu                   UnsafeBufferUsageHandler &Handler, bool EmitSuggestions) {
3642b7bdf199SArtem Dergachev   if (!EmitSuggestions) {
3643b7bdf199SArtem Dergachev     // Our job is very easy without suggestions. Just warn about
3644b7bdf199SArtem Dergachev     // every problematic operation and consider it done. No need to deal
3645b7bdf199SArtem Dergachev     // with fixable gadgets, no need to group operations by variable.
3646b7bdf199SArtem Dergachev     for (const auto &G : WarningGadgets) {
36475ac34358SDana Jansens       G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
36487122f55cSMalavika Samak                                D->getASTContext());
3649b7bdf199SArtem Dergachev     }
3650b7bdf199SArtem Dergachev 
3651b7bdf199SArtem Dergachev     // This return guarantees that most of the machine doesn't run when
3652b7bdf199SArtem Dergachev     // suggestions aren't requested.
3653b7bdf199SArtem Dergachev     assert(FixableGadgets.size() == 0 &&
3654b7bdf199SArtem Dergachev            "Fixable gadgets found but suggestions not requested!");
3655b7bdf199SArtem Dergachev     return;
3656b7bdf199SArtem Dergachev   }
3657b7bdf199SArtem Dergachev 
3658cfcf76c6SZiqing Luo   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3659cfcf76c6SZiqing Luo   //  function under the analysis. No need to fix any Fixables.
3660cfcf76c6SZiqing Luo   if (!WarningGadgets.empty()) {
3661cfcf76c6SZiqing Luo     // Gadgets "claim" variables they're responsible for. Once this loop
3662cfcf76c6SZiqing Luo     // finishes, the tracker will only track DREs that weren't claimed by any
3663cfcf76c6SZiqing Luo     // gadgets, i.e. not understood by the analysis.
3664cfcf76c6SZiqing Luo     for (const auto &G : FixableGadgets) {
3665cfcf76c6SZiqing Luo       for (const auto *DRE : G->getClaimedVarUseSites()) {
3666cfcf76c6SZiqing Luo         Tracker.claimUse(DRE);
3667cfcf76c6SZiqing Luo       }
3668cfcf76c6SZiqing Luo     }
3669cfcf76c6SZiqing Luo   }
3670cfcf76c6SZiqing Luo 
3671cfcf76c6SZiqing Luo   // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3672cfcf76c6SZiqing Luo   // function under the analysis.  Thus, it early returns here as there is
3673cfcf76c6SZiqing Luo   // nothing needs to be fixed.
3674cfcf76c6SZiqing Luo   //
3675cfcf76c6SZiqing Luo   // Note this claim is based on the assumption that there is no unsafe
3676cfcf76c6SZiqing Luo   // variable whose declaration is invisible from the analyzing function.
3677cfcf76c6SZiqing Luo   // Otherwise, we need to consider if the uses of those unsafe varuables needs
3678cfcf76c6SZiqing Luo   // fix.
3679cfcf76c6SZiqing Luo   // So far, we are not fixing any global variables or class members. And,
3680cfcf76c6SZiqing Luo   // lambdas will be analyzed along with the enclosing function. So this early
3681cfcf76c6SZiqing Luo   // return is correct for now.
3682cfcf76c6SZiqing Luo   if (WarningGadgets.empty())
3683cfcf76c6SZiqing Luo     return;
3684cfcf76c6SZiqing Luo 
3685c60b055dSZequan Wu   WarningGadgetSets UnsafeOps =
3686c60b055dSZequan Wu       groupWarningGadgetsByVar(std::move(WarningGadgets));
3687c60b055dSZequan Wu   FixableGadgetSets FixablesForAllVars =
3688c60b055dSZequan Wu       groupFixablesByVar(std::move(FixableGadgets));
368950d4a1f7SMalavikaSamak 
3690171dfc54SRashmi Mudduluru   std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
36918b6ae9bdSJan Korous 
3692214312efSJan Korous   // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
3693171dfc54SRashmi Mudduluru   for (auto it = FixablesForAllVars.byVar.cbegin();
3694171dfc54SRashmi Mudduluru        it != FixablesForAllVars.byVar.cend();) {
36951e270be0Sziqingluo-90     // FIXME: need to deal with global variables later
3696a6ae740eSRashmi Mudduluru     if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
3697a6ae740eSRashmi Mudduluru #ifndef NDEBUG
36983fa91021Sjkorous-apple       Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
36993fa91021Sjkorous-apple                                  ("failed to produce fixit for '" +
37003fa91021Sjkorous-apple                                   it->first->getNameAsString() +
3701a6ae740eSRashmi Mudduluru                                   "' : neither local nor a parameter"));
3702a6ae740eSRashmi Mudduluru #endif
3703a6ae740eSRashmi Mudduluru       it = FixablesForAllVars.byVar.erase(it);
3704700baeb7SZiqing Luo     } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
3705700baeb7SZiqing Luo #ifndef NDEBUG
3706700baeb7SZiqing Luo       Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3707700baeb7SZiqing Luo                                  ("failed to produce fixit for '" +
3708700baeb7SZiqing Luo                                   it->first->getNameAsString() +
3709700baeb7SZiqing Luo                                   "' : has a reference type"));
3710700baeb7SZiqing Luo #endif
3711700baeb7SZiqing Luo       it = FixablesForAllVars.byVar.erase(it);
3712a6ae740eSRashmi Mudduluru     } else if (Tracker.hasUnclaimedUses(it->first)) {
3713a6ae740eSRashmi Mudduluru       it = FixablesForAllVars.byVar.erase(it);
3714a6ae740eSRashmi Mudduluru     } else if (it->first->isInitCapture()) {
3715a6ae740eSRashmi Mudduluru #ifndef NDEBUG
37163fa91021Sjkorous-apple       Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
37173fa91021Sjkorous-apple                                  ("failed to produce fixit for '" +
37183fa91021Sjkorous-apple                                   it->first->getNameAsString() +
3719a6ae740eSRashmi Mudduluru                                   "' : init capture"));
3720a6ae740eSRashmi Mudduluru #endif
3721171dfc54SRashmi Mudduluru       it = FixablesForAllVars.byVar.erase(it);
37228086323aSArtem Dergachev     } else {
3723214312efSJan Korous       ++it;
3724214312efSJan Korous     }
3725214312efSJan Korous   }
3726214312efSJan Korous 
37272f490583Sjkorous-apple #ifndef NDEBUG
37282f490583Sjkorous-apple   for (const auto &it : UnsafeOps.byVar) {
37292f490583Sjkorous-apple     const VarDecl *const UnsafeVD = it.first;
37302f490583Sjkorous-apple     auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
37312f490583Sjkorous-apple     if (UnclaimedDREs.empty())
37322f490583Sjkorous-apple       continue;
37332f490583Sjkorous-apple     const auto UnfixedVDName = UnsafeVD->getNameAsString();
37342f490583Sjkorous-apple     for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
37352f490583Sjkorous-apple       std::string UnclaimedUseTrace =
37362f490583Sjkorous-apple           getDREAncestorString(UnclaimedDRE, D->getASTContext());
37372f490583Sjkorous-apple 
37382f490583Sjkorous-apple       Handler.addDebugNoteForVar(
37392f490583Sjkorous-apple           UnsafeVD, UnclaimedDRE->getBeginLoc(),
37402f490583Sjkorous-apple           ("failed to produce fixit for '" + UnfixedVDName +
37412f490583Sjkorous-apple            "' : has an unclaimed use\nThe unclaimed DRE trace: " +
37422f490583Sjkorous-apple            UnclaimedUseTrace));
37432f490583Sjkorous-apple     }
37442f490583Sjkorous-apple   }
37452f490583Sjkorous-apple #endif
37462f490583Sjkorous-apple 
3747171dfc54SRashmi Mudduluru   // Fixpoint iteration for pointer assignments
3748472a510bSziqingluo-90   using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
3749171dfc54SRashmi Mudduluru   DepMapTy DependenciesMap{};
3750171dfc54SRashmi Mudduluru   DepMapTy PtrAssignmentGraph{};
3751171dfc54SRashmi Mudduluru 
3752171dfc54SRashmi Mudduluru   for (auto it : FixablesForAllVars.byVar) {
3753171dfc54SRashmi Mudduluru     for (const FixableGadget *fixable : it.second) {
3754171dfc54SRashmi Mudduluru       std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
3755171dfc54SRashmi Mudduluru           fixable->getStrategyImplications();
3756171dfc54SRashmi Mudduluru       if (ImplPair) {
3757472a510bSziqingluo-90         std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
3758171dfc54SRashmi Mudduluru         PtrAssignmentGraph[Impl.first].insert(Impl.second);
3759171dfc54SRashmi Mudduluru       }
3760171dfc54SRashmi Mudduluru     }
3761171dfc54SRashmi Mudduluru   }
3762171dfc54SRashmi Mudduluru 
3763171dfc54SRashmi Mudduluru   /*
3764171dfc54SRashmi Mudduluru    The following code does a BFS traversal of the `PtrAssignmentGraph`
3765171dfc54SRashmi Mudduluru    considering all unsafe vars as starting nodes and constructs an undirected
3766171dfc54SRashmi Mudduluru    graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
3767171dfc54SRashmi Mudduluru    elimiates all variables that are unreachable from any unsafe var. In other
3768171dfc54SRashmi Mudduluru    words, this removes all dependencies that don't include any unsafe variable
3769171dfc54SRashmi Mudduluru    and consequently don't need any fixit generation.
3770171dfc54SRashmi Mudduluru    Note: A careful reader would observe that the code traverses
3771171dfc54SRashmi Mudduluru    `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
3772171dfc54SRashmi Mudduluru    `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
3773171dfc54SRashmi Mudduluru    achieve the same result but the one used here dramatically cuts the
3774171dfc54SRashmi Mudduluru    amount of hoops the second part of the algorithm needs to jump, given that
3775171dfc54SRashmi Mudduluru    a lot of these connections become "direct". The reader is advised not to
3776171dfc54SRashmi Mudduluru    imagine how the graph is transformed because of using `Var` instead of
3777171dfc54SRashmi Mudduluru    `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
3778171dfc54SRashmi Mudduluru    and think about why it's equivalent later.
3779171dfc54SRashmi Mudduluru    */
3780171dfc54SRashmi Mudduluru   std::set<const VarDecl *> VisitedVarsDirected{};
3781171dfc54SRashmi Mudduluru   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3782171dfc54SRashmi Mudduluru     if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
3783171dfc54SRashmi Mudduluru 
3784171dfc54SRashmi Mudduluru       std::queue<const VarDecl *> QueueDirected{};
3785171dfc54SRashmi Mudduluru       QueueDirected.push(Var);
3786171dfc54SRashmi Mudduluru       while (!QueueDirected.empty()) {
3787171dfc54SRashmi Mudduluru         const VarDecl *CurrentVar = QueueDirected.front();
3788171dfc54SRashmi Mudduluru         QueueDirected.pop();
3789171dfc54SRashmi Mudduluru         VisitedVarsDirected.insert(CurrentVar);
3790171dfc54SRashmi Mudduluru         auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
3791171dfc54SRashmi Mudduluru         for (const VarDecl *Adj : AdjacentNodes) {
3792171dfc54SRashmi Mudduluru           if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
3793171dfc54SRashmi Mudduluru             QueueDirected.push(Adj);
3794171dfc54SRashmi Mudduluru           }
3795171dfc54SRashmi Mudduluru           DependenciesMap[Var].insert(Adj);
3796171dfc54SRashmi Mudduluru           DependenciesMap[Adj].insert(Var);
3797171dfc54SRashmi Mudduluru         }
3798171dfc54SRashmi Mudduluru       }
3799171dfc54SRashmi Mudduluru     }
3800171dfc54SRashmi Mudduluru   }
3801171dfc54SRashmi Mudduluru 
3802472a510bSziqingluo-90   // `Groups` stores the set of Connected Components in the graph.
3803472a510bSziqingluo-90   std::vector<VarGrpTy> Groups;
3804472a510bSziqingluo-90   // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
3805472a510bSziqingluo-90   // variables belong to.  Group indexes refer to the elements in `Groups`.
3806472a510bSziqingluo-90   // `VarGrpMap` is complete in that every variable that needs fix is in it.
3807472a510bSziqingluo-90   std::map<const VarDecl *, unsigned> VarGrpMap;
380833f6161dSZiqing Luo   // The union group over the ones in "Groups" that contain parameters of `D`:
380933f6161dSZiqing Luo   llvm::SetVector<const VarDecl *>
381033f6161dSZiqing Luo       GrpsUnionForParms; // these variables need to be fixed in one step
3811472a510bSziqingluo-90 
3812171dfc54SRashmi Mudduluru   // Group Connected Components for Unsafe Vars
3813171dfc54SRashmi Mudduluru   // (Dependencies based on pointer assignments)
3814171dfc54SRashmi Mudduluru   std::set<const VarDecl *> VisitedVars{};
3815171dfc54SRashmi Mudduluru   for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3816171dfc54SRashmi Mudduluru     if (VisitedVars.find(Var) == VisitedVars.end()) {
3817472a510bSziqingluo-90       VarGrpTy &VarGroup = Groups.emplace_back();
3818ac9a76d7SZiqing Luo       std::queue<const VarDecl *> Queue{};
3819472a510bSziqingluo-90 
3820171dfc54SRashmi Mudduluru       Queue.push(Var);
3821171dfc54SRashmi Mudduluru       while (!Queue.empty()) {
3822171dfc54SRashmi Mudduluru         const VarDecl *CurrentVar = Queue.front();
3823171dfc54SRashmi Mudduluru         Queue.pop();
3824171dfc54SRashmi Mudduluru         VisitedVars.insert(CurrentVar);
3825171dfc54SRashmi Mudduluru         VarGroup.push_back(CurrentVar);
3826171dfc54SRashmi Mudduluru         auto AdjacentNodes = DependenciesMap[CurrentVar];
3827171dfc54SRashmi Mudduluru         for (const VarDecl *Adj : AdjacentNodes) {
3828171dfc54SRashmi Mudduluru           if (VisitedVars.find(Adj) == VisitedVars.end()) {
3829171dfc54SRashmi Mudduluru             Queue.push(Adj);
3830171dfc54SRashmi Mudduluru           }
3831171dfc54SRashmi Mudduluru         }
3832171dfc54SRashmi Mudduluru       }
383333f6161dSZiqing Luo 
383433f6161dSZiqing Luo       bool HasParm = false;
3835472a510bSziqingluo-90       unsigned GrpIdx = Groups.size() - 1;
3836472a510bSziqingluo-90 
3837171dfc54SRashmi Mudduluru       for (const VarDecl *V : VarGroup) {
3838472a510bSziqingluo-90         VarGrpMap[V] = GrpIdx;
383933f6161dSZiqing Luo         if (!HasParm && isParameterOf(V, D))
384033f6161dSZiqing Luo           HasParm = true;
3841171dfc54SRashmi Mudduluru       }
384233f6161dSZiqing Luo       if (HasParm)
384333f6161dSZiqing Luo         GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
3844171dfc54SRashmi Mudduluru     }
3845171dfc54SRashmi Mudduluru   }
3846171dfc54SRashmi Mudduluru 
3847cfcf76c6SZiqing Luo   // Remove a `FixableGadget` if the associated variable is not in the graph
3848cfcf76c6SZiqing Luo   // computed above.  We do not want to generate fix-its for such variables,
3849cfcf76c6SZiqing Luo   // since they are neither warned nor reachable from a warned one.
3850cfcf76c6SZiqing Luo   //
3851cfcf76c6SZiqing Luo   // Note a variable is not warned if it is not directly used in any unsafe
3852cfcf76c6SZiqing Luo   // operation. A variable `v` is NOT reachable from an unsafe variable, if it
3853cfcf76c6SZiqing Luo   // does not exist another variable `u` such that `u` is warned and fixing `u`
3854cfcf76c6SZiqing Luo   // (transitively) implicates fixing `v`.
3855cfcf76c6SZiqing Luo   //
3856cfcf76c6SZiqing Luo   // For example,
3857cfcf76c6SZiqing Luo   // ```
3858cfcf76c6SZiqing Luo   // void f(int * p) {
3859cfcf76c6SZiqing Luo   //   int * a = p; *p = 0;
3860cfcf76c6SZiqing Luo   // }
3861cfcf76c6SZiqing Luo   // ```
3862cfcf76c6SZiqing Luo   // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
3863cfcf76c6SZiqing Luo   // warned nor reachable from a warned one.  If we add `a[5] = 0` to the end of
3864cfcf76c6SZiqing Luo   // the function above, `p` becomes reachable from a warned variable.
3865cfcf76c6SZiqing Luo   for (auto I = FixablesForAllVars.byVar.begin();
3866cfcf76c6SZiqing Luo        I != FixablesForAllVars.byVar.end();) {
3867cfcf76c6SZiqing Luo     // Note `VisitedVars` contain all the variables in the graph:
386833f6161dSZiqing Luo     if (!VisitedVars.count((*I).first)) {
3869cfcf76c6SZiqing Luo       // no such var in graph:
3870cfcf76c6SZiqing Luo       I = FixablesForAllVars.byVar.erase(I);
3871cfcf76c6SZiqing Luo     } else
3872cfcf76c6SZiqing Luo       ++I;
3873cfcf76c6SZiqing Luo   }
3874cfcf76c6SZiqing Luo 
3875700baeb7SZiqing Luo   // We assign strategies to variables that are 1) in the graph and 2) can be
3876700baeb7SZiqing Luo   // fixed. Other variables have the default "Won't fix" strategy.
3877644ac2a0Sjkorous-apple   FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
3878700baeb7SZiqing Luo       VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
3879700baeb7SZiqing Luo         // If a warned variable has no "Fixable", it is considered unfixable:
3880700baeb7SZiqing Luo         return FixablesForAllVars.byVar.count(V);
3881700baeb7SZiqing Luo       }));
388233f6161dSZiqing Luo   VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
38831e270be0Sziqingluo-90 
388433f6161dSZiqing Luo   if (isa<NamedDecl>(D))
388533f6161dSZiqing Luo     // The only case where `D` is not a `NamedDecl` is when `D` is a
388633f6161dSZiqing Luo     // `BlockDecl`. Let's not fix variables in blocks for now
38871e270be0Sziqingluo-90     FixItsForVariableGroup =
38881e270be0Sziqingluo-90         getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
3889472a510bSziqingluo-90                   Tracker, Handler, VarGrpMgr);
3890214312efSJan Korous 
3891214312efSJan Korous   for (const auto &G : UnsafeOps.noVar) {
38925ac34358SDana Jansens     G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
38937122f55cSMalavika Samak                              D->getASTContext());
3894214312efSJan Korous   }
3895214312efSJan Korous 
3896214312efSJan Korous   for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
3897171dfc54SRashmi Mudduluru     auto FixItsIt = FixItsForVariableGroup.find(VD);
3898472a510bSziqingluo-90     Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
3899171dfc54SRashmi Mudduluru                                       FixItsIt != FixItsForVariableGroup.end()
3900237ca436SJan Korous                                           ? std::move(FixItsIt->second)
390133f6161dSZiqing Luo                                           : FixItList{},
3902644ac2a0Sjkorous-apple                                       D, NaiveStrategy);
3903214312efSJan Korous     for (const auto &G : WarningGadgets) {
39045ac34358SDana Jansens       G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
39057122f55cSMalavika Samak                                D->getASTContext());
39068086323aSArtem Dergachev     }
39078086323aSArtem Dergachev   }
39083b7af279SArtem Dergachev }
3909c60b055dSZequan Wu 
3910c60b055dSZequan Wu void clang::checkUnsafeBufferUsage(const Decl *D,
3911c60b055dSZequan Wu                                    UnsafeBufferUsageHandler &Handler,
3912c60b055dSZequan Wu                                    bool EmitSuggestions) {
3913c60b055dSZequan Wu #ifndef NDEBUG
3914c60b055dSZequan Wu   Handler.clearDebugNotes();
3915c60b055dSZequan Wu #endif
3916c60b055dSZequan Wu 
3917c60b055dSZequan Wu   assert(D);
3918c60b055dSZequan Wu 
3919c60b055dSZequan Wu   SmallVector<Stmt *> Stmts;
3920c60b055dSZequan Wu 
3921c60b055dSZequan Wu   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
3922c60b055dSZequan Wu     // We do not want to visit a Lambda expression defined inside a method
3923c60b055dSZequan Wu     // independently. Instead, it should be visited along with the outer method.
3924c60b055dSZequan Wu     // FIXME: do we want to do the same thing for `BlockDecl`s?
3925c60b055dSZequan Wu     if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
3926c60b055dSZequan Wu       if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
3927c60b055dSZequan Wu         return;
3928c60b055dSZequan Wu     }
3929c60b055dSZequan Wu 
3930c60b055dSZequan Wu     for (FunctionDecl *FReDecl : FD->redecls()) {
3931c60b055dSZequan Wu       if (FReDecl->isExternC()) {
3932c60b055dSZequan Wu         // Do not emit fixit suggestions for functions declared in an
3933c60b055dSZequan Wu         // extern "C" block.
3934c60b055dSZequan Wu         EmitSuggestions = false;
3935c60b055dSZequan Wu         break;
3936c60b055dSZequan Wu       }
3937c60b055dSZequan Wu     }
3938c60b055dSZequan Wu 
3939c60b055dSZequan Wu     Stmts.push_back(FD->getBody());
3940c60b055dSZequan Wu 
3941c60b055dSZequan Wu     if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
3942c60b055dSZequan Wu       for (const CXXCtorInitializer *CI : ID->inits()) {
3943c60b055dSZequan Wu         Stmts.push_back(CI->getInit());
3944c60b055dSZequan Wu       }
3945c60b055dSZequan Wu     }
3946c60b055dSZequan Wu   } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
3947c60b055dSZequan Wu     Stmts.push_back(D->getBody());
3948c60b055dSZequan Wu   }
3949c60b055dSZequan Wu 
3950c60b055dSZequan Wu   assert(!Stmts.empty());
3951c60b055dSZequan Wu 
3952c60b055dSZequan Wu   FixableGadgetList FixableGadgets;
3953c60b055dSZequan Wu   WarningGadgetList WarningGadgets;
3954c60b055dSZequan Wu   DeclUseTracker Tracker;
3955c60b055dSZequan Wu   for (Stmt *S : Stmts) {
3956c60b055dSZequan Wu     findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
3957c60b055dSZequan Wu                 WarningGadgets, Tracker);
3958c60b055dSZequan Wu   }
3959c60b055dSZequan Wu   applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
3960c60b055dSZequan Wu                std::move(Tracker), Handler, EmitSuggestions);
3961c60b055dSZequan Wu }
3962