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