1bdd1243dSDimitry Andric //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric 9bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsage.h" 1006c3fb27SDimitry Andric #include "clang/AST/Decl.h" 11*5f757f3fSDimitry Andric #include "clang/AST/Expr.h" 12bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 13*5f757f3fSDimitry Andric #include "clang/AST/StmtVisitor.h" 14bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 1506c3fb27SDimitry Andric #include "clang/Lex/Lexer.h" 1606c3fb27SDimitry Andric #include "clang/Lex/Preprocessor.h" 17bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h" 18bdd1243dSDimitry Andric #include <memory> 19bdd1243dSDimitry Andric #include <optional> 2006c3fb27SDimitry Andric #include <sstream> 2106c3fb27SDimitry Andric #include <queue> 22bdd1243dSDimitry Andric 23bdd1243dSDimitry Andric using namespace llvm; 24bdd1243dSDimitry Andric using namespace clang; 25bdd1243dSDimitry Andric using namespace ast_matchers; 26bdd1243dSDimitry Andric 27*5f757f3fSDimitry Andric #ifndef NDEBUG 28*5f757f3fSDimitry Andric namespace { 29*5f757f3fSDimitry Andric class StmtDebugPrinter 30*5f757f3fSDimitry Andric : public ConstStmtVisitor<StmtDebugPrinter, std::string> { 31*5f757f3fSDimitry Andric public: 32*5f757f3fSDimitry Andric std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); } 33*5f757f3fSDimitry Andric 34*5f757f3fSDimitry Andric std::string VisitBinaryOperator(const BinaryOperator *BO) { 35*5f757f3fSDimitry Andric return "BinaryOperator(" + BO->getOpcodeStr().str() + ")"; 36*5f757f3fSDimitry Andric } 37*5f757f3fSDimitry Andric 38*5f757f3fSDimitry Andric std::string VisitUnaryOperator(const UnaryOperator *UO) { 39*5f757f3fSDimitry Andric return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")"; 40*5f757f3fSDimitry Andric } 41*5f757f3fSDimitry Andric 42*5f757f3fSDimitry Andric std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) { 43*5f757f3fSDimitry Andric return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")"; 44*5f757f3fSDimitry Andric } 45*5f757f3fSDimitry Andric }; 46*5f757f3fSDimitry Andric 47*5f757f3fSDimitry Andric // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form: 48*5f757f3fSDimitry Andric // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...". 49*5f757f3fSDimitry Andric static std::string getDREAncestorString(const DeclRefExpr *DRE, 50*5f757f3fSDimitry Andric ASTContext &Ctx) { 51*5f757f3fSDimitry Andric std::stringstream SS; 52*5f757f3fSDimitry Andric const Stmt *St = DRE; 53*5f757f3fSDimitry Andric StmtDebugPrinter StmtPriner; 54*5f757f3fSDimitry Andric 55*5f757f3fSDimitry Andric do { 56*5f757f3fSDimitry Andric SS << StmtPriner.Visit(St); 57*5f757f3fSDimitry Andric 58*5f757f3fSDimitry Andric DynTypedNodeList StParents = Ctx.getParents(*St); 59*5f757f3fSDimitry Andric 60*5f757f3fSDimitry Andric if (StParents.size() > 1) 61*5f757f3fSDimitry Andric return "unavailable due to multiple parents"; 62*5f757f3fSDimitry Andric if (StParents.size() == 0) 63*5f757f3fSDimitry Andric break; 64*5f757f3fSDimitry Andric St = StParents.begin()->get<Stmt>(); 65*5f757f3fSDimitry Andric if (St) 66*5f757f3fSDimitry Andric SS << " ==> "; 67*5f757f3fSDimitry Andric } while (St); 68*5f757f3fSDimitry Andric return SS.str(); 69*5f757f3fSDimitry Andric } 70*5f757f3fSDimitry Andric } // namespace 71*5f757f3fSDimitry Andric #endif /* NDEBUG */ 72*5f757f3fSDimitry Andric 73bdd1243dSDimitry Andric namespace clang::ast_matchers { 74bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n" 75bdd1243dSDimitry Andric // except for those belonging to a different callable of "n". 76bdd1243dSDimitry Andric class MatchDescendantVisitor 77bdd1243dSDimitry Andric : public RecursiveASTVisitor<MatchDescendantVisitor> { 78bdd1243dSDimitry Andric public: 79bdd1243dSDimitry Andric typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase; 80bdd1243dSDimitry Andric 81bdd1243dSDimitry Andric // Creates an AST visitor that matches `Matcher` on all 82bdd1243dSDimitry Andric // descendants of a given node "n" except for the ones 83bdd1243dSDimitry Andric // belonging to a different callable of "n". 84bdd1243dSDimitry Andric MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, 85bdd1243dSDimitry Andric internal::ASTMatchFinder *Finder, 86bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *Builder, 8706c3fb27SDimitry Andric internal::ASTMatchFinder::BindKind Bind, 8806c3fb27SDimitry Andric const bool ignoreUnevaluatedContext) 89bdd1243dSDimitry Andric : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind), 9006c3fb27SDimitry Andric Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {} 91bdd1243dSDimitry Andric 92bdd1243dSDimitry Andric // Returns true if a match is found in a subtree of `DynNode`, which belongs 93bdd1243dSDimitry Andric // to the same callable of `DynNode`. 94bdd1243dSDimitry Andric bool findMatch(const DynTypedNode &DynNode) { 95bdd1243dSDimitry Andric Matches = false; 96bdd1243dSDimitry Andric if (const Stmt *StmtNode = DynNode.get<Stmt>()) { 97bdd1243dSDimitry Andric TraverseStmt(const_cast<Stmt *>(StmtNode)); 98bdd1243dSDimitry Andric *Builder = ResultBindings; 99bdd1243dSDimitry Andric return Matches; 100bdd1243dSDimitry Andric } 101bdd1243dSDimitry Andric return false; 102bdd1243dSDimitry Andric } 103bdd1243dSDimitry Andric 104bdd1243dSDimitry Andric // The following are overriding methods from the base visitor class. 105bdd1243dSDimitry Andric // They are public only to allow CRTP to work. They are *not *part 106bdd1243dSDimitry Andric // of the public API of this class. 107bdd1243dSDimitry Andric 108bdd1243dSDimitry Andric // For the matchers so far used in safe buffers, we only need to match 109bdd1243dSDimitry Andric // `Stmt`s. To override more as needed. 110bdd1243dSDimitry Andric 111bdd1243dSDimitry Andric bool TraverseDecl(Decl *Node) { 112bdd1243dSDimitry Andric if (!Node) 113bdd1243dSDimitry Andric return true; 114bdd1243dSDimitry Andric if (!match(*Node)) 115bdd1243dSDimitry Andric return false; 116bdd1243dSDimitry Andric // To skip callables: 117bdd1243dSDimitry Andric if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node)) 118bdd1243dSDimitry Andric return true; 119bdd1243dSDimitry Andric // Traverse descendants 120bdd1243dSDimitry Andric return VisitorBase::TraverseDecl(Node); 121bdd1243dSDimitry Andric } 122bdd1243dSDimitry Andric 12306c3fb27SDimitry Andric bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) { 12406c3fb27SDimitry Andric // These are unevaluated, except the result expression. 12506c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 12606c3fb27SDimitry Andric return TraverseStmt(Node->getResultExpr()); 12706c3fb27SDimitry Andric return VisitorBase::TraverseGenericSelectionExpr(Node); 12806c3fb27SDimitry Andric } 12906c3fb27SDimitry Andric 13006c3fb27SDimitry Andric bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { 13106c3fb27SDimitry Andric // Unevaluated context. 13206c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 13306c3fb27SDimitry Andric return true; 13406c3fb27SDimitry Andric return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node); 13506c3fb27SDimitry Andric } 13606c3fb27SDimitry Andric 13706c3fb27SDimitry Andric bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) { 13806c3fb27SDimitry Andric // Unevaluated context. 13906c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 14006c3fb27SDimitry Andric return true; 14106c3fb27SDimitry Andric return VisitorBase::TraverseTypeOfExprTypeLoc(Node); 14206c3fb27SDimitry Andric } 14306c3fb27SDimitry Andric 14406c3fb27SDimitry Andric bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) { 14506c3fb27SDimitry Andric // Unevaluated context. 14606c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 14706c3fb27SDimitry Andric return true; 14806c3fb27SDimitry Andric return VisitorBase::TraverseDecltypeTypeLoc(Node); 14906c3fb27SDimitry Andric } 15006c3fb27SDimitry Andric 15106c3fb27SDimitry Andric bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) { 15206c3fb27SDimitry Andric // Unevaluated context. 15306c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 15406c3fb27SDimitry Andric return true; 15506c3fb27SDimitry Andric return VisitorBase::TraverseCXXNoexceptExpr(Node); 15606c3fb27SDimitry Andric } 15706c3fb27SDimitry Andric 15806c3fb27SDimitry Andric bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) { 15906c3fb27SDimitry Andric // Unevaluated context. 16006c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 16106c3fb27SDimitry Andric return true; 16206c3fb27SDimitry Andric return VisitorBase::TraverseCXXTypeidExpr(Node); 16306c3fb27SDimitry Andric } 16406c3fb27SDimitry Andric 165bdd1243dSDimitry Andric bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) { 166bdd1243dSDimitry Andric if (!Node) 167bdd1243dSDimitry Andric return true; 168bdd1243dSDimitry Andric if (!match(*Node)) 169bdd1243dSDimitry Andric return false; 170bdd1243dSDimitry Andric return VisitorBase::TraverseStmt(Node); 171bdd1243dSDimitry Andric } 172bdd1243dSDimitry Andric 173bdd1243dSDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; } 174bdd1243dSDimitry Andric bool shouldVisitImplicitCode() const { 175bdd1243dSDimitry Andric // TODO: let's ignore implicit code for now 176bdd1243dSDimitry Andric return false; 177bdd1243dSDimitry Andric } 178bdd1243dSDimitry Andric 179bdd1243dSDimitry Andric private: 180bdd1243dSDimitry Andric // Sets 'Matched' to true if 'Matcher' matches 'Node' 181bdd1243dSDimitry Andric // 182bdd1243dSDimitry Andric // Returns 'true' if traversal should continue after this function 183bdd1243dSDimitry Andric // returns, i.e. if no match is found or 'Bind' is 'BK_All'. 184bdd1243dSDimitry Andric template <typename T> bool match(const T &Node) { 185bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder); 186bdd1243dSDimitry Andric 187bdd1243dSDimitry Andric if (Matcher->matches(DynTypedNode::create(Node), Finder, 188bdd1243dSDimitry Andric &RecursiveBuilder)) { 189bdd1243dSDimitry Andric ResultBindings.addMatch(RecursiveBuilder); 190bdd1243dSDimitry Andric Matches = true; 191bdd1243dSDimitry Andric if (Bind != internal::ASTMatchFinder::BK_All) 192bdd1243dSDimitry Andric return false; // Abort as soon as a match is found. 193bdd1243dSDimitry Andric } 194bdd1243dSDimitry Andric return true; 195bdd1243dSDimitry Andric } 196bdd1243dSDimitry Andric 197bdd1243dSDimitry Andric const internal::DynTypedMatcher *const Matcher; 198bdd1243dSDimitry Andric internal::ASTMatchFinder *const Finder; 199bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *const Builder; 200bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder ResultBindings; 201bdd1243dSDimitry Andric const internal::ASTMatchFinder::BindKind Bind; 202bdd1243dSDimitry Andric bool Matches; 20306c3fb27SDimitry Andric bool ignoreUnevaluatedContext; 204bdd1243dSDimitry Andric }; 205bdd1243dSDimitry Andric 20606c3fb27SDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that. 20706c3fb27SDimitry Andric static auto hasPointerType() { 20806c3fb27SDimitry Andric return hasType(hasCanonicalType(pointerType())); 20906c3fb27SDimitry Andric } 21006c3fb27SDimitry Andric 21106c3fb27SDimitry Andric static auto hasArrayType() { 21206c3fb27SDimitry Andric return hasType(hasCanonicalType(arrayType())); 21306c3fb27SDimitry Andric } 21406c3fb27SDimitry Andric 21506c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>, innerMatcher) { 216bdd1243dSDimitry Andric const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 217bdd1243dSDimitry Andric 21806c3fb27SDimitry Andric MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, true); 219bdd1243dSDimitry Andric return Visitor.findMatch(DynTypedNode::create(Node)); 220bdd1243dSDimitry Andric } 22106c3fb27SDimitry Andric 22206c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>, innerMatcher) { 22306c3fb27SDimitry Andric const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 22406c3fb27SDimitry Andric 22506c3fb27SDimitry Andric MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, false); 22606c3fb27SDimitry Andric return Visitor.findMatch(DynTypedNode::create(Node)); 22706c3fb27SDimitry Andric } 22806c3fb27SDimitry Andric 22906c3fb27SDimitry Andric // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region 23006c3fb27SDimitry Andric AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *, 23106c3fb27SDimitry Andric Handler) { 23206c3fb27SDimitry Andric return !Handler->isSafeBufferOptOut(Node.getBeginLoc()); 23306c3fb27SDimitry Andric } 23406c3fb27SDimitry Andric 23506c3fb27SDimitry Andric AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) { 23606c3fb27SDimitry Andric return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder); 23706c3fb27SDimitry Andric } 23806c3fb27SDimitry Andric 23906c3fb27SDimitry Andric // Matches a `UnaryOperator` whose operator is pre-increment: 24006c3fb27SDimitry Andric AST_MATCHER(UnaryOperator, isPreInc) { 24106c3fb27SDimitry Andric return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc; 24206c3fb27SDimitry Andric } 24306c3fb27SDimitry Andric 24406c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher` 24506c3fb27SDimitry Andric // matches 'e' and 'e' is in an Unspecified Lvalue Context. 24606c3fb27SDimitry Andric static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) { 24706c3fb27SDimitry Andric // clang-format off 24806c3fb27SDimitry Andric return 24906c3fb27SDimitry Andric expr(anyOf( 25006c3fb27SDimitry Andric implicitCastExpr( 25106c3fb27SDimitry Andric hasCastKind(CastKind::CK_LValueToRValue), 25206c3fb27SDimitry Andric castSubExpr(innerMatcher)), 25306c3fb27SDimitry Andric binaryOperator( 25406c3fb27SDimitry Andric hasAnyOperatorName("="), 25506c3fb27SDimitry Andric hasLHS(innerMatcher) 25606c3fb27SDimitry Andric ) 25706c3fb27SDimitry Andric )); 25806c3fb27SDimitry Andric // clang-format on 25906c3fb27SDimitry Andric } 26006c3fb27SDimitry Andric 26106c3fb27SDimitry Andric 26206c3fb27SDimitry Andric // Returns a matcher that matches any expression `e` such that `InnerMatcher` 26306c3fb27SDimitry Andric // matches `e` and `e` is in an Unspecified Pointer Context (UPC). 26406c3fb27SDimitry Andric static internal::Matcher<Stmt> 26506c3fb27SDimitry Andric isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) { 26606c3fb27SDimitry Andric // A UPC can be 26706c3fb27SDimitry Andric // 1. an argument of a function call (except the callee has [[unsafe_...]] 26806c3fb27SDimitry Andric // attribute), or 26906c3fb27SDimitry Andric // 2. the operand of a pointer-to-(integer or bool) cast operation; or 27006c3fb27SDimitry Andric // 3. the operand of a comparator operation; or 27106c3fb27SDimitry Andric // 4. the operand of a pointer subtraction operation 27206c3fb27SDimitry Andric // (i.e., computing the distance between two pointers); or ... 27306c3fb27SDimitry Andric 27406c3fb27SDimitry Andric auto CallArgMatcher = 27506c3fb27SDimitry Andric callExpr(forEachArgumentWithParam(InnerMatcher, 27606c3fb27SDimitry Andric hasPointerType() /* array also decays to pointer type*/), 27706c3fb27SDimitry Andric unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))))); 27806c3fb27SDimitry Andric 27906c3fb27SDimitry Andric auto CastOperandMatcher = 28006c3fb27SDimitry Andric castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral), 28106c3fb27SDimitry Andric hasCastKind(CastKind::CK_PointerToBoolean)), 28206c3fb27SDimitry Andric castSubExpr(allOf(hasPointerType(), InnerMatcher))); 28306c3fb27SDimitry Andric 28406c3fb27SDimitry Andric auto CompOperandMatcher = 28506c3fb27SDimitry Andric binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="), 28606c3fb27SDimitry Andric eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)), 28706c3fb27SDimitry Andric hasRHS(allOf(hasPointerType(), InnerMatcher)))); 28806c3fb27SDimitry Andric 28906c3fb27SDimitry Andric // A matcher that matches pointer subtractions: 29006c3fb27SDimitry Andric auto PtrSubtractionMatcher = 29106c3fb27SDimitry Andric binaryOperator(hasOperatorName("-"), 29206c3fb27SDimitry Andric // Note that here we need both LHS and RHS to be 29306c3fb27SDimitry Andric // pointer. Then the inner matcher can match any of 29406c3fb27SDimitry Andric // them: 29506c3fb27SDimitry Andric allOf(hasLHS(hasPointerType()), 29606c3fb27SDimitry Andric hasRHS(hasPointerType())), 29706c3fb27SDimitry Andric eachOf(hasLHS(InnerMatcher), 29806c3fb27SDimitry Andric hasRHS(InnerMatcher))); 29906c3fb27SDimitry Andric 30006c3fb27SDimitry Andric return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher, 30106c3fb27SDimitry Andric PtrSubtractionMatcher)); 30206c3fb27SDimitry Andric // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we 30306c3fb27SDimitry Andric // don't have to check that.) 30406c3fb27SDimitry Andric } 30506c3fb27SDimitry Andric 30606c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher` 30706c3fb27SDimitry Andric // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression 30806c3fb27SDimitry Andric // 'e' isn't evaluated to an RValue). For example, consider the following code: 30906c3fb27SDimitry Andric // int *p = new int[4]; 31006c3fb27SDimitry Andric // int *q = new int[4]; 31106c3fb27SDimitry Andric // if ((p = q)) {} 31206c3fb27SDimitry Andric // p = q; 31306c3fb27SDimitry Andric // The expression `p = q` in the conditional of the `if` statement 31406c3fb27SDimitry Andric // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;` 31506c3fb27SDimitry Andric // in the assignment statement is in an untyped context. 31606c3fb27SDimitry Andric static internal::Matcher<Stmt> 31706c3fb27SDimitry Andric isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) { 31806c3fb27SDimitry Andric // An unspecified context can be 31906c3fb27SDimitry Andric // 1. A compound statement, 32006c3fb27SDimitry Andric // 2. The body of an if statement 32106c3fb27SDimitry Andric // 3. Body of a loop 32206c3fb27SDimitry Andric auto CompStmt = compoundStmt(forEach(InnerMatcher)); 32306c3fb27SDimitry Andric auto IfStmtThen = ifStmt(hasThen(InnerMatcher)); 32406c3fb27SDimitry Andric auto IfStmtElse = ifStmt(hasElse(InnerMatcher)); 32506c3fb27SDimitry Andric // FIXME: Handle loop bodies. 32606c3fb27SDimitry Andric return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse)); 32706c3fb27SDimitry Andric } 328bdd1243dSDimitry Andric } // namespace clang::ast_matchers 329bdd1243dSDimitry Andric 330bdd1243dSDimitry Andric namespace { 331bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to 332bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs). 333bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>; 334bdd1243dSDimitry Andric 335bdd1243dSDimitry Andric // Convenience typedef. 336bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>; 337bdd1243dSDimitry Andric 338bdd1243dSDimitry Andric // Defined below. 339bdd1243dSDimitry Andric class Strategy; 340bdd1243dSDimitry Andric } // namespace 341bdd1243dSDimitry Andric 342bdd1243dSDimitry Andric namespace { 343bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to 344bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific 345bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object. 346bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand 347bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it. 348bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not 349bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify 350bdd1243dSDimitry Andric /// it warrants a fixit). 351bdd1243dSDimitry Andric class Gadget { 352bdd1243dSDimitry Andric public: 353bdd1243dSDimitry Andric enum class Kind { 354bdd1243dSDimitry Andric #define GADGET(x) x, 355bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 356bdd1243dSDimitry Andric }; 357bdd1243dSDimitry Andric 358bdd1243dSDimitry Andric /// Common type of ASTMatchers used for discovering gadgets. 359bdd1243dSDimitry Andric /// Useful for implementing the static matcher() methods 360bdd1243dSDimitry Andric /// that are expected from all non-abstract subclasses. 361bdd1243dSDimitry Andric using Matcher = decltype(stmt()); 362bdd1243dSDimitry Andric 363bdd1243dSDimitry Andric Gadget(Kind K) : K(K) {} 364bdd1243dSDimitry Andric 365bdd1243dSDimitry Andric Kind getKind() const { return K; } 366bdd1243dSDimitry Andric 367*5f757f3fSDimitry Andric #ifndef NDEBUG 368*5f757f3fSDimitry Andric StringRef getDebugName() const { 369*5f757f3fSDimitry Andric switch (K) { 370*5f757f3fSDimitry Andric #define GADGET(x) case Kind::x: return #x; 371*5f757f3fSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 372*5f757f3fSDimitry Andric } 373*5f757f3fSDimitry Andric llvm_unreachable("Unhandled Gadget::Kind enum"); 374*5f757f3fSDimitry Andric } 375*5f757f3fSDimitry Andric #endif 376*5f757f3fSDimitry Andric 377bdd1243dSDimitry Andric virtual bool isWarningGadget() const = 0; 378bdd1243dSDimitry Andric virtual const Stmt *getBaseStmt() const = 0; 379bdd1243dSDimitry Andric 380bdd1243dSDimitry Andric /// Returns the list of pointer-type variables on which this gadget performs 381bdd1243dSDimitry Andric /// its operation. Typically, there's only one variable. This isn't a list 382bdd1243dSDimitry Andric /// of all DeclRefExprs in the gadget's AST! 383bdd1243dSDimitry Andric virtual DeclUseList getClaimedVarUseSites() const = 0; 384bdd1243dSDimitry Andric 385bdd1243dSDimitry Andric virtual ~Gadget() = default; 386bdd1243dSDimitry Andric 387bdd1243dSDimitry Andric private: 388bdd1243dSDimitry Andric Kind K; 389bdd1243dSDimitry Andric }; 390bdd1243dSDimitry Andric 391bdd1243dSDimitry Andric 392bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants 393bdd1243dSDimitry Andric /// an immediate warning. 394bdd1243dSDimitry Andric class WarningGadget : public Gadget { 395bdd1243dSDimitry Andric public: 396bdd1243dSDimitry Andric WarningGadget(Kind K) : Gadget(K) {} 397bdd1243dSDimitry Andric 398bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return G->isWarningGadget(); } 399bdd1243dSDimitry Andric bool isWarningGadget() const final { return true; } 400bdd1243dSDimitry Andric }; 401bdd1243dSDimitry Andric 402bdd1243dSDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but need to be 403bdd1243dSDimitry Andric /// properly recognized in order to emit fixes. For example, if a raw pointer-type 404bdd1243dSDimitry Andric /// variable is replaced by a safe C++ container, every use of such variable must be 405bdd1243dSDimitry Andric /// carefully considered and possibly updated. 406bdd1243dSDimitry Andric class FixableGadget : public Gadget { 407bdd1243dSDimitry Andric public: 408bdd1243dSDimitry Andric FixableGadget(Kind K) : Gadget(K) {} 409bdd1243dSDimitry Andric 410bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return !G->isWarningGadget(); } 411bdd1243dSDimitry Andric bool isWarningGadget() const final { return false; } 412bdd1243dSDimitry Andric 413bdd1243dSDimitry Andric /// Returns a fixit that would fix the current gadget according to 41406c3fb27SDimitry Andric /// the current strategy. Returns std::nullopt if the fix cannot be produced; 415bdd1243dSDimitry Andric /// returns an empty list if no fixes are necessary. 416bdd1243dSDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &) const { 417bdd1243dSDimitry Andric return std::nullopt; 418bdd1243dSDimitry Andric } 41906c3fb27SDimitry Andric 42006c3fb27SDimitry Andric /// Returns a list of two elements where the first element is the LHS of a pointer assignment 42106c3fb27SDimitry Andric /// statement and the second element is the RHS. This two-element list represents the fact that 42206c3fb27SDimitry Andric /// the LHS buffer gets its bounds information from the RHS buffer. This information will be used 42306c3fb27SDimitry Andric /// later to group all those variables whose types must be modified together to prevent type 42406c3fb27SDimitry Andric /// mismatches. 42506c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 42606c3fb27SDimitry Andric getStrategyImplications() const { 42706c3fb27SDimitry Andric return std::nullopt; 42806c3fb27SDimitry Andric } 429bdd1243dSDimitry Andric }; 430bdd1243dSDimitry Andric 431*5f757f3fSDimitry Andric static auto toSupportedVariable() { 432*5f757f3fSDimitry Andric return to(varDecl()); 433*5f757f3fSDimitry Andric } 434*5f757f3fSDimitry Andric 435bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>; 436bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>; 437bdd1243dSDimitry Andric 438bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer 439bdd1243dSDimitry Andric /// out of bounds. 440bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget { 441bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 442bdd1243dSDimitry Andric const UnaryOperator *Op; 443bdd1243dSDimitry Andric 444bdd1243dSDimitry Andric public: 445bdd1243dSDimitry Andric IncrementGadget(const MatchFinder::MatchResult &Result) 446bdd1243dSDimitry Andric : WarningGadget(Kind::Increment), 447bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 448bdd1243dSDimitry Andric 449bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 450bdd1243dSDimitry Andric return G->getKind() == Kind::Increment; 451bdd1243dSDimitry Andric } 452bdd1243dSDimitry Andric 453bdd1243dSDimitry Andric static Matcher matcher() { 454bdd1243dSDimitry Andric return stmt(unaryOperator( 455bdd1243dSDimitry Andric hasOperatorName("++"), 456bdd1243dSDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType())) 457bdd1243dSDimitry Andric ).bind(OpTag)); 458bdd1243dSDimitry Andric } 459bdd1243dSDimitry Andric 460bdd1243dSDimitry Andric const UnaryOperator *getBaseStmt() const override { return Op; } 461bdd1243dSDimitry Andric 462bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 463bdd1243dSDimitry Andric SmallVector<const DeclRefExpr *, 2> Uses; 464bdd1243dSDimitry Andric if (const auto *DRE = 465bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 466bdd1243dSDimitry Andric Uses.push_back(DRE); 467bdd1243dSDimitry Andric } 468bdd1243dSDimitry Andric 469bdd1243dSDimitry Andric return std::move(Uses); 470bdd1243dSDimitry Andric } 471bdd1243dSDimitry Andric }; 472bdd1243dSDimitry Andric 473bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer 474bdd1243dSDimitry Andric /// out of bounds. 475bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget { 476bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 477bdd1243dSDimitry Andric const UnaryOperator *Op; 478bdd1243dSDimitry Andric 479bdd1243dSDimitry Andric public: 480bdd1243dSDimitry Andric DecrementGadget(const MatchFinder::MatchResult &Result) 481bdd1243dSDimitry Andric : WarningGadget(Kind::Decrement), 482bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 483bdd1243dSDimitry Andric 484bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 485bdd1243dSDimitry Andric return G->getKind() == Kind::Decrement; 486bdd1243dSDimitry Andric } 487bdd1243dSDimitry Andric 488bdd1243dSDimitry Andric static Matcher matcher() { 489bdd1243dSDimitry Andric return stmt(unaryOperator( 490bdd1243dSDimitry Andric hasOperatorName("--"), 491bdd1243dSDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType())) 492bdd1243dSDimitry Andric ).bind(OpTag)); 493bdd1243dSDimitry Andric } 494bdd1243dSDimitry Andric 495bdd1243dSDimitry Andric const UnaryOperator *getBaseStmt() const override { return Op; } 496bdd1243dSDimitry Andric 497bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 498bdd1243dSDimitry Andric if (const auto *DRE = 499bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 500bdd1243dSDimitry Andric return {DRE}; 501bdd1243dSDimitry Andric } 502bdd1243dSDimitry Andric 503bdd1243dSDimitry Andric return {}; 504bdd1243dSDimitry Andric } 505bdd1243dSDimitry Andric }; 506bdd1243dSDimitry Andric 507bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as 508bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array. 509bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget { 51006c3fb27SDimitry Andric static constexpr const char *const ArraySubscrTag = "ArraySubscript"; 511bdd1243dSDimitry Andric const ArraySubscriptExpr *ASE; 512bdd1243dSDimitry Andric 513bdd1243dSDimitry Andric public: 514bdd1243dSDimitry Andric ArraySubscriptGadget(const MatchFinder::MatchResult &Result) 515bdd1243dSDimitry Andric : WarningGadget(Kind::ArraySubscript), 516bdd1243dSDimitry Andric ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {} 517bdd1243dSDimitry Andric 518bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 519bdd1243dSDimitry Andric return G->getKind() == Kind::ArraySubscript; 520bdd1243dSDimitry Andric } 521bdd1243dSDimitry Andric 522bdd1243dSDimitry Andric static Matcher matcher() { 523bdd1243dSDimitry Andric // FIXME: What if the index is integer literal 0? Should this be 524bdd1243dSDimitry Andric // a safe gadget in this case? 525bdd1243dSDimitry Andric // clang-format off 526bdd1243dSDimitry Andric return stmt(arraySubscriptExpr( 527bdd1243dSDimitry Andric hasBase(ignoringParenImpCasts( 528bdd1243dSDimitry Andric anyOf(hasPointerType(), hasArrayType()))), 52906c3fb27SDimitry Andric unless(hasIndex( 53006c3fb27SDimitry Andric anyOf(integerLiteral(equals(0)), arrayInitIndexExpr()) 53106c3fb27SDimitry Andric ))) 532bdd1243dSDimitry Andric .bind(ArraySubscrTag)); 533bdd1243dSDimitry Andric // clang-format on 534bdd1243dSDimitry Andric } 535bdd1243dSDimitry Andric 536bdd1243dSDimitry Andric const ArraySubscriptExpr *getBaseStmt() const override { return ASE; } 537bdd1243dSDimitry Andric 538bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 539bdd1243dSDimitry Andric if (const auto *DRE = 540bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) { 541bdd1243dSDimitry Andric return {DRE}; 542bdd1243dSDimitry Andric } 543bdd1243dSDimitry Andric 544bdd1243dSDimitry Andric return {}; 545bdd1243dSDimitry Andric } 546bdd1243dSDimitry Andric }; 547bdd1243dSDimitry Andric 548bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms: 549bdd1243dSDimitry Andric /// \code 550bdd1243dSDimitry Andric /// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n 551bdd1243dSDimitry Andric /// \endcode 552bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget { 553bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticTag = "ptrAdd"; 554bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr"; 555bdd1243dSDimitry Andric const BinaryOperator *PA; // pointer arithmetic expression 556bdd1243dSDimitry Andric const Expr *Ptr; // the pointer expression in `PA` 557bdd1243dSDimitry Andric 558bdd1243dSDimitry Andric public: 559bdd1243dSDimitry Andric PointerArithmeticGadget(const MatchFinder::MatchResult &Result) 560bdd1243dSDimitry Andric : WarningGadget(Kind::PointerArithmetic), 561bdd1243dSDimitry Andric PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)), 562bdd1243dSDimitry Andric Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {} 563bdd1243dSDimitry Andric 564bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 565bdd1243dSDimitry Andric return G->getKind() == Kind::PointerArithmetic; 566bdd1243dSDimitry Andric } 567bdd1243dSDimitry Andric 568bdd1243dSDimitry Andric static Matcher matcher() { 56906c3fb27SDimitry Andric auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType())); 57006c3fb27SDimitry Andric auto PtrAtRight = 57106c3fb27SDimitry Andric allOf(hasOperatorName("+"), 572bdd1243dSDimitry Andric hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 573bdd1243dSDimitry Andric hasLHS(HasIntegerType)); 57406c3fb27SDimitry Andric auto PtrAtLeft = 57506c3fb27SDimitry Andric allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"), 576bdd1243dSDimitry Andric hasOperatorName("+="), hasOperatorName("-=")), 577bdd1243dSDimitry Andric hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 578bdd1243dSDimitry Andric hasRHS(HasIntegerType)); 579bdd1243dSDimitry Andric 58006c3fb27SDimitry Andric return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight)) 58106c3fb27SDimitry Andric .bind(PointerArithmeticTag)); 582bdd1243dSDimitry Andric } 583bdd1243dSDimitry Andric 584bdd1243dSDimitry Andric const Stmt *getBaseStmt() const override { return PA; } 585bdd1243dSDimitry Andric 586bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 58706c3fb27SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) { 588bdd1243dSDimitry Andric return {DRE}; 589bdd1243dSDimitry Andric } 590bdd1243dSDimitry Andric 591bdd1243dSDimitry Andric return {}; 592bdd1243dSDimitry Andric } 593bdd1243dSDimitry Andric // FIXME: pointer adding zero should be fine 594bdd1243dSDimitry Andric // FIXME: this gadge will need a fix-it 595bdd1243dSDimitry Andric }; 59606c3fb27SDimitry Andric 59706c3fb27SDimitry Andric /// A pointer initialization expression of the form: 59806c3fb27SDimitry Andric /// \code 59906c3fb27SDimitry Andric /// int *p = q; 60006c3fb27SDimitry Andric /// \endcode 60106c3fb27SDimitry Andric class PointerInitGadget : public FixableGadget { 60206c3fb27SDimitry Andric private: 60306c3fb27SDimitry Andric static constexpr const char *const PointerInitLHSTag = "ptrInitLHS"; 60406c3fb27SDimitry Andric static constexpr const char *const PointerInitRHSTag = "ptrInitRHS"; 60506c3fb27SDimitry Andric const VarDecl * PtrInitLHS; // the LHS pointer expression in `PI` 60606c3fb27SDimitry Andric const DeclRefExpr * PtrInitRHS; // the RHS pointer expression in `PI` 60706c3fb27SDimitry Andric 60806c3fb27SDimitry Andric public: 60906c3fb27SDimitry Andric PointerInitGadget(const MatchFinder::MatchResult &Result) 61006c3fb27SDimitry Andric : FixableGadget(Kind::PointerInit), 61106c3fb27SDimitry Andric PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)), 61206c3fb27SDimitry Andric PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {} 61306c3fb27SDimitry Andric 61406c3fb27SDimitry Andric static bool classof(const Gadget *G) { 61506c3fb27SDimitry Andric return G->getKind() == Kind::PointerInit; 61606c3fb27SDimitry Andric } 61706c3fb27SDimitry Andric 61806c3fb27SDimitry Andric static Matcher matcher() { 61906c3fb27SDimitry Andric auto PtrInitStmt = declStmt(hasSingleDecl(varDecl( 62006c3fb27SDimitry Andric hasInitializer(ignoringImpCasts(declRefExpr( 621*5f757f3fSDimitry Andric hasPointerType(), 622*5f757f3fSDimitry Andric toSupportedVariable()). 62306c3fb27SDimitry Andric bind(PointerInitRHSTag)))). 62406c3fb27SDimitry Andric bind(PointerInitLHSTag))); 62506c3fb27SDimitry Andric 62606c3fb27SDimitry Andric return stmt(PtrInitStmt); 62706c3fb27SDimitry Andric } 62806c3fb27SDimitry Andric 62906c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 63006c3fb27SDimitry Andric 631*5f757f3fSDimitry Andric virtual const Stmt *getBaseStmt() const override { 632*5f757f3fSDimitry Andric // FIXME: This needs to be the entire DeclStmt, assuming that this method 633*5f757f3fSDimitry Andric // makes sense at all on a FixableGadget. 634*5f757f3fSDimitry Andric return PtrInitRHS; 635*5f757f3fSDimitry Andric } 63606c3fb27SDimitry Andric 63706c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 63806c3fb27SDimitry Andric return DeclUseList{PtrInitRHS}; 63906c3fb27SDimitry Andric } 64006c3fb27SDimitry Andric 64106c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 64206c3fb27SDimitry Andric getStrategyImplications() const override { 64306c3fb27SDimitry Andric return std::make_pair(PtrInitLHS, 64406c3fb27SDimitry Andric cast<VarDecl>(PtrInitRHS->getDecl())); 64506c3fb27SDimitry Andric } 64606c3fb27SDimitry Andric }; 64706c3fb27SDimitry Andric 64806c3fb27SDimitry Andric /// A pointer assignment expression of the form: 64906c3fb27SDimitry Andric /// \code 65006c3fb27SDimitry Andric /// p = q; 65106c3fb27SDimitry Andric /// \endcode 65206c3fb27SDimitry Andric class PointerAssignmentGadget : public FixableGadget { 65306c3fb27SDimitry Andric private: 65406c3fb27SDimitry Andric static constexpr const char *const PointerAssignLHSTag = "ptrLHS"; 65506c3fb27SDimitry Andric static constexpr const char *const PointerAssignRHSTag = "ptrRHS"; 65606c3fb27SDimitry Andric const DeclRefExpr * PtrLHS; // the LHS pointer expression in `PA` 65706c3fb27SDimitry Andric const DeclRefExpr * PtrRHS; // the RHS pointer expression in `PA` 65806c3fb27SDimitry Andric 65906c3fb27SDimitry Andric public: 66006c3fb27SDimitry Andric PointerAssignmentGadget(const MatchFinder::MatchResult &Result) 66106c3fb27SDimitry Andric : FixableGadget(Kind::PointerAssignment), 66206c3fb27SDimitry Andric PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)), 66306c3fb27SDimitry Andric PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {} 66406c3fb27SDimitry Andric 66506c3fb27SDimitry Andric static bool classof(const Gadget *G) { 66606c3fb27SDimitry Andric return G->getKind() == Kind::PointerAssignment; 66706c3fb27SDimitry Andric } 66806c3fb27SDimitry Andric 66906c3fb27SDimitry Andric static Matcher matcher() { 67006c3fb27SDimitry Andric auto PtrAssignExpr = binaryOperator(allOf(hasOperatorName("="), 67106c3fb27SDimitry Andric hasRHS(ignoringParenImpCasts(declRefExpr(hasPointerType(), 672*5f757f3fSDimitry Andric toSupportedVariable()). 67306c3fb27SDimitry Andric bind(PointerAssignRHSTag))), 67406c3fb27SDimitry Andric hasLHS(declRefExpr(hasPointerType(), 675*5f757f3fSDimitry Andric toSupportedVariable()). 67606c3fb27SDimitry Andric bind(PointerAssignLHSTag)))); 67706c3fb27SDimitry Andric 67806c3fb27SDimitry Andric return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr)); 67906c3fb27SDimitry Andric } 68006c3fb27SDimitry Andric 68106c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 68206c3fb27SDimitry Andric 683*5f757f3fSDimitry Andric virtual const Stmt *getBaseStmt() const override { 684*5f757f3fSDimitry Andric // FIXME: This should be the binary operator, assuming that this method 685*5f757f3fSDimitry Andric // makes sense at all on a FixableGadget. 686*5f757f3fSDimitry Andric return PtrLHS; 687*5f757f3fSDimitry Andric } 68806c3fb27SDimitry Andric 68906c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 69006c3fb27SDimitry Andric return DeclUseList{PtrLHS, PtrRHS}; 69106c3fb27SDimitry Andric } 69206c3fb27SDimitry Andric 69306c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 69406c3fb27SDimitry Andric getStrategyImplications() const override { 69506c3fb27SDimitry Andric return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()), 69606c3fb27SDimitry Andric cast<VarDecl>(PtrRHS->getDecl())); 69706c3fb27SDimitry Andric } 69806c3fb27SDimitry Andric }; 69906c3fb27SDimitry Andric 70006c3fb27SDimitry Andric /// A call of a function or method that performs unchecked buffer operations 70106c3fb27SDimitry Andric /// over one of its pointer parameters. 70206c3fb27SDimitry Andric class UnsafeBufferUsageAttrGadget : public WarningGadget { 70306c3fb27SDimitry Andric constexpr static const char *const OpTag = "call_expr"; 70406c3fb27SDimitry Andric const CallExpr *Op; 70506c3fb27SDimitry Andric 70606c3fb27SDimitry Andric public: 70706c3fb27SDimitry Andric UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result) 70806c3fb27SDimitry Andric : WarningGadget(Kind::UnsafeBufferUsageAttr), 70906c3fb27SDimitry Andric Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {} 71006c3fb27SDimitry Andric 71106c3fb27SDimitry Andric static bool classof(const Gadget *G) { 71206c3fb27SDimitry Andric return G->getKind() == Kind::UnsafeBufferUsageAttr; 71306c3fb27SDimitry Andric } 71406c3fb27SDimitry Andric 71506c3fb27SDimitry Andric static Matcher matcher() { 71606c3fb27SDimitry Andric return stmt(callExpr(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)))) 71706c3fb27SDimitry Andric .bind(OpTag)); 71806c3fb27SDimitry Andric } 71906c3fb27SDimitry Andric const Stmt *getBaseStmt() const override { return Op; } 72006c3fb27SDimitry Andric 72106c3fb27SDimitry Andric DeclUseList getClaimedVarUseSites() const override { return {}; } 72206c3fb27SDimitry Andric }; 72306c3fb27SDimitry Andric 72406c3fb27SDimitry Andric // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue 72506c3fb27SDimitry Andric // Context (see `isInUnspecifiedLvalueContext`). 72606c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator. 72706c3fb27SDimitry Andric class ULCArraySubscriptGadget : public FixableGadget { 72806c3fb27SDimitry Andric private: 72906c3fb27SDimitry Andric static constexpr const char *const ULCArraySubscriptTag = 73006c3fb27SDimitry Andric "ArraySubscriptUnderULC"; 73106c3fb27SDimitry Andric const ArraySubscriptExpr *Node; 73206c3fb27SDimitry Andric 73306c3fb27SDimitry Andric public: 73406c3fb27SDimitry Andric ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result) 73506c3fb27SDimitry Andric : FixableGadget(Kind::ULCArraySubscript), 73606c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) { 73706c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 73806c3fb27SDimitry Andric } 73906c3fb27SDimitry Andric 74006c3fb27SDimitry Andric static bool classof(const Gadget *G) { 74106c3fb27SDimitry Andric return G->getKind() == Kind::ULCArraySubscript; 74206c3fb27SDimitry Andric } 74306c3fb27SDimitry Andric 74406c3fb27SDimitry Andric static Matcher matcher() { 74506c3fb27SDimitry Andric auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 74606c3fb27SDimitry Andric auto BaseIsArrayOrPtrDRE = 747*5f757f3fSDimitry Andric hasBase(ignoringParenImpCasts(declRefExpr(ArrayOrPtr, 748*5f757f3fSDimitry Andric toSupportedVariable()))); 74906c3fb27SDimitry Andric auto Target = 75006c3fb27SDimitry Andric arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag); 75106c3fb27SDimitry Andric 75206c3fb27SDimitry Andric return expr(isInUnspecifiedLvalueContext(Target)); 75306c3fb27SDimitry Andric } 75406c3fb27SDimitry Andric 75506c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 75606c3fb27SDimitry Andric 75706c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 75806c3fb27SDimitry Andric 75906c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 76006c3fb27SDimitry Andric if (const auto *DRE = 76106c3fb27SDimitry Andric dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) { 76206c3fb27SDimitry Andric return {DRE}; 76306c3fb27SDimitry Andric } 76406c3fb27SDimitry Andric return {}; 76506c3fb27SDimitry Andric } 76606c3fb27SDimitry Andric }; 76706c3fb27SDimitry Andric 76806c3fb27SDimitry Andric // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the 76906c3fb27SDimitry Andric // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits 77006c3fb27SDimitry Andric // fixit of the form `UPC(DRE.data())`. 77106c3fb27SDimitry Andric class UPCStandalonePointerGadget : public FixableGadget { 77206c3fb27SDimitry Andric private: 77306c3fb27SDimitry Andric static constexpr const char *const DeclRefExprTag = "StandalonePointer"; 77406c3fb27SDimitry Andric const DeclRefExpr *Node; 77506c3fb27SDimitry Andric 77606c3fb27SDimitry Andric public: 77706c3fb27SDimitry Andric UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result) 77806c3fb27SDimitry Andric : FixableGadget(Kind::UPCStandalonePointer), 77906c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) { 78006c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 78106c3fb27SDimitry Andric } 78206c3fb27SDimitry Andric 78306c3fb27SDimitry Andric static bool classof(const Gadget *G) { 78406c3fb27SDimitry Andric return G->getKind() == Kind::UPCStandalonePointer; 78506c3fb27SDimitry Andric } 78606c3fb27SDimitry Andric 78706c3fb27SDimitry Andric static Matcher matcher() { 78806c3fb27SDimitry Andric auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 78906c3fb27SDimitry Andric auto target = expr( 790*5f757f3fSDimitry Andric ignoringParenImpCasts(declRefExpr(allOf(ArrayOrPtr, 791*5f757f3fSDimitry Andric toSupportedVariable())).bind(DeclRefExprTag))); 79206c3fb27SDimitry Andric return stmt(isInUnspecifiedPointerContext(target)); 79306c3fb27SDimitry Andric } 79406c3fb27SDimitry Andric 79506c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 79606c3fb27SDimitry Andric 79706c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 79806c3fb27SDimitry Andric 79906c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 80006c3fb27SDimitry Andric return {Node}; 80106c3fb27SDimitry Andric } 80206c3fb27SDimitry Andric }; 80306c3fb27SDimitry Andric 80406c3fb27SDimitry Andric class PointerDereferenceGadget : public FixableGadget { 80506c3fb27SDimitry Andric static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 80606c3fb27SDimitry Andric static constexpr const char *const OperatorTag = "op"; 80706c3fb27SDimitry Andric 80806c3fb27SDimitry Andric const DeclRefExpr *BaseDeclRefExpr = nullptr; 80906c3fb27SDimitry Andric const UnaryOperator *Op = nullptr; 81006c3fb27SDimitry Andric 81106c3fb27SDimitry Andric public: 81206c3fb27SDimitry Andric PointerDereferenceGadget(const MatchFinder::MatchResult &Result) 81306c3fb27SDimitry Andric : FixableGadget(Kind::PointerDereference), 81406c3fb27SDimitry Andric BaseDeclRefExpr( 81506c3fb27SDimitry Andric Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 81606c3fb27SDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {} 81706c3fb27SDimitry Andric 81806c3fb27SDimitry Andric static bool classof(const Gadget *G) { 81906c3fb27SDimitry Andric return G->getKind() == Kind::PointerDereference; 82006c3fb27SDimitry Andric } 82106c3fb27SDimitry Andric 82206c3fb27SDimitry Andric static Matcher matcher() { 82306c3fb27SDimitry Andric auto Target = 82406c3fb27SDimitry Andric unaryOperator( 82506c3fb27SDimitry Andric hasOperatorName("*"), 82606c3fb27SDimitry Andric has(expr(ignoringParenImpCasts( 827*5f757f3fSDimitry Andric declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag))))) 82806c3fb27SDimitry Andric .bind(OperatorTag); 82906c3fb27SDimitry Andric 83006c3fb27SDimitry Andric return expr(isInUnspecifiedLvalueContext(Target)); 83106c3fb27SDimitry Andric } 83206c3fb27SDimitry Andric 83306c3fb27SDimitry Andric DeclUseList getClaimedVarUseSites() const override { 83406c3fb27SDimitry Andric return {BaseDeclRefExpr}; 83506c3fb27SDimitry Andric } 83606c3fb27SDimitry Andric 83706c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const final { return Op; } 83806c3fb27SDimitry Andric 83906c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 84006c3fb27SDimitry Andric }; 84106c3fb27SDimitry Andric 84206c3fb27SDimitry Andric // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer 84306c3fb27SDimitry Andric // Context (see `isInUnspecifiedPointerContext`). 84406c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator. 84506c3fb27SDimitry Andric class UPCAddressofArraySubscriptGadget : public FixableGadget { 84606c3fb27SDimitry Andric private: 84706c3fb27SDimitry Andric static constexpr const char *const UPCAddressofArraySubscriptTag = 84806c3fb27SDimitry Andric "AddressofArraySubscriptUnderUPC"; 84906c3fb27SDimitry Andric const UnaryOperator *Node; // the `&DRE[any]` node 85006c3fb27SDimitry Andric 85106c3fb27SDimitry Andric public: 85206c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result) 85306c3fb27SDimitry Andric : FixableGadget(Kind::ULCArraySubscript), 85406c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<UnaryOperator>( 85506c3fb27SDimitry Andric UPCAddressofArraySubscriptTag)) { 85606c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 85706c3fb27SDimitry Andric } 85806c3fb27SDimitry Andric 85906c3fb27SDimitry Andric static bool classof(const Gadget *G) { 86006c3fb27SDimitry Andric return G->getKind() == Kind::UPCAddressofArraySubscript; 86106c3fb27SDimitry Andric } 86206c3fb27SDimitry Andric 86306c3fb27SDimitry Andric static Matcher matcher() { 86406c3fb27SDimitry Andric return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 86506c3fb27SDimitry Andric unaryOperator(hasOperatorName("&"), 86606c3fb27SDimitry Andric hasUnaryOperand(arraySubscriptExpr( 867*5f757f3fSDimitry Andric hasBase(ignoringParenImpCasts(declRefExpr( 868*5f757f3fSDimitry Andric toSupportedVariable())))))) 86906c3fb27SDimitry Andric .bind(UPCAddressofArraySubscriptTag))))); 87006c3fb27SDimitry Andric } 87106c3fb27SDimitry Andric 87206c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &) const override; 87306c3fb27SDimitry Andric 87406c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 87506c3fb27SDimitry Andric 87606c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 87706c3fb27SDimitry Andric const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr()); 87806c3fb27SDimitry Andric const auto *DRE = 87906c3fb27SDimitry Andric cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreImpCasts()); 88006c3fb27SDimitry Andric return {DRE}; 88106c3fb27SDimitry Andric } 88206c3fb27SDimitry Andric }; 883bdd1243dSDimitry Andric } // namespace 884bdd1243dSDimitry Andric 885bdd1243dSDimitry Andric namespace { 886bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect 88706c3fb27SDimitry Andric // declarations to its uses and make sure we've covered all uses with our 88806c3fb27SDimitry Andric // analysis before we try to fix the declaration. 889bdd1243dSDimitry Andric class DeclUseTracker { 890bdd1243dSDimitry Andric using UseSetTy = SmallSet<const DeclRefExpr *, 16>; 891bdd1243dSDimitry Andric using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>; 892bdd1243dSDimitry Andric 893bdd1243dSDimitry Andric // Allocate on the heap for easier move. 894bdd1243dSDimitry Andric std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()}; 895bdd1243dSDimitry Andric DefMapTy Defs{}; 896bdd1243dSDimitry Andric 897bdd1243dSDimitry Andric public: 898bdd1243dSDimitry Andric DeclUseTracker() = default; 899bdd1243dSDimitry Andric DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies. 90006c3fb27SDimitry Andric DeclUseTracker &operator=(const DeclUseTracker &) = delete; 901bdd1243dSDimitry Andric DeclUseTracker(DeclUseTracker &&) = default; 902bdd1243dSDimitry Andric DeclUseTracker &operator=(DeclUseTracker &&) = default; 903bdd1243dSDimitry Andric 904bdd1243dSDimitry Andric // Start tracking a freshly discovered DRE. 905bdd1243dSDimitry Andric void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); } 906bdd1243dSDimitry Andric 907bdd1243dSDimitry Andric // Stop tracking the DRE as it's been fully figured out. 908bdd1243dSDimitry Andric void claimUse(const DeclRefExpr *DRE) { 909bdd1243dSDimitry Andric assert(Uses->count(DRE) && 910bdd1243dSDimitry Andric "DRE not found or claimed by multiple matchers!"); 911bdd1243dSDimitry Andric Uses->erase(DRE); 912bdd1243dSDimitry Andric } 913bdd1243dSDimitry Andric 914bdd1243dSDimitry Andric // A variable is unclaimed if at least one use is unclaimed. 915bdd1243dSDimitry Andric bool hasUnclaimedUses(const VarDecl *VD) const { 916bdd1243dSDimitry Andric // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs? 917bdd1243dSDimitry Andric return any_of(*Uses, [VD](const DeclRefExpr *DRE) { 918bdd1243dSDimitry Andric return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl(); 919bdd1243dSDimitry Andric }); 920bdd1243dSDimitry Andric } 921bdd1243dSDimitry Andric 922*5f757f3fSDimitry Andric UseSetTy getUnclaimedUses(const VarDecl *VD) const { 923*5f757f3fSDimitry Andric UseSetTy ReturnSet; 924*5f757f3fSDimitry Andric for (auto use : *Uses) { 925*5f757f3fSDimitry Andric if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) { 926*5f757f3fSDimitry Andric ReturnSet.insert(use); 927*5f757f3fSDimitry Andric } 928*5f757f3fSDimitry Andric } 929*5f757f3fSDimitry Andric return ReturnSet; 930*5f757f3fSDimitry Andric } 931*5f757f3fSDimitry Andric 932bdd1243dSDimitry Andric void discoverDecl(const DeclStmt *DS) { 933bdd1243dSDimitry Andric for (const Decl *D : DS->decls()) { 934bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(D)) { 935bdd1243dSDimitry Andric // FIXME: Assertion temporarily disabled due to a bug in 936bdd1243dSDimitry Andric // ASTMatcher internal behavior in presence of GNU 937bdd1243dSDimitry Andric // statement-expressions. We need to properly investigate this 938bdd1243dSDimitry Andric // because it can screw up our algorithm in other ways. 939bdd1243dSDimitry Andric // assert(Defs.count(VD) == 0 && "Definition already discovered!"); 940bdd1243dSDimitry Andric Defs[VD] = DS; 941bdd1243dSDimitry Andric } 942bdd1243dSDimitry Andric } 943bdd1243dSDimitry Andric } 944bdd1243dSDimitry Andric 945bdd1243dSDimitry Andric const DeclStmt *lookupDecl(const VarDecl *VD) const { 946*5f757f3fSDimitry Andric return Defs.lookup(VD); 947bdd1243dSDimitry Andric } 948bdd1243dSDimitry Andric }; 949bdd1243dSDimitry Andric } // namespace 950bdd1243dSDimitry Andric 951bdd1243dSDimitry Andric namespace { 952bdd1243dSDimitry Andric // Strategy is a map from variables to the way we plan to emit fixes for 953bdd1243dSDimitry Andric // these variables. It is figured out gradually by trying different fixes 954bdd1243dSDimitry Andric // for different variables depending on gadgets in which these variables 955bdd1243dSDimitry Andric // participate. 956bdd1243dSDimitry Andric class Strategy { 957bdd1243dSDimitry Andric public: 958bdd1243dSDimitry Andric enum class Kind { 959bdd1243dSDimitry Andric Wontfix, // We don't plan to emit a fixit for this variable. 960bdd1243dSDimitry Andric Span, // We recommend replacing the variable with std::span. 961bdd1243dSDimitry Andric Iterator, // We recommend replacing the variable with std::span::iterator. 962bdd1243dSDimitry Andric Array, // We recommend replacing the variable with std::array. 963bdd1243dSDimitry Andric Vector // We recommend replacing the variable with std::vector. 964bdd1243dSDimitry Andric }; 965bdd1243dSDimitry Andric 966bdd1243dSDimitry Andric private: 967bdd1243dSDimitry Andric using MapTy = llvm::DenseMap<const VarDecl *, Kind>; 968bdd1243dSDimitry Andric 969bdd1243dSDimitry Andric MapTy Map; 970bdd1243dSDimitry Andric 971bdd1243dSDimitry Andric public: 972bdd1243dSDimitry Andric Strategy() = default; 973bdd1243dSDimitry Andric Strategy(const Strategy &) = delete; // Let's avoid copies. 97406c3fb27SDimitry Andric Strategy &operator=(const Strategy &) = delete; 975bdd1243dSDimitry Andric Strategy(Strategy &&) = default; 97606c3fb27SDimitry Andric Strategy &operator=(Strategy &&) = default; 977bdd1243dSDimitry Andric 97806c3fb27SDimitry Andric void set(const VarDecl *VD, Kind K) { Map[VD] = K; } 979bdd1243dSDimitry Andric 980bdd1243dSDimitry Andric Kind lookup(const VarDecl *VD) const { 981bdd1243dSDimitry Andric auto I = Map.find(VD); 982bdd1243dSDimitry Andric if (I == Map.end()) 983bdd1243dSDimitry Andric return Kind::Wontfix; 984bdd1243dSDimitry Andric 985bdd1243dSDimitry Andric return I->second; 986bdd1243dSDimitry Andric } 987bdd1243dSDimitry Andric }; 988bdd1243dSDimitry Andric } // namespace 989bdd1243dSDimitry Andric 99006c3fb27SDimitry Andric 99106c3fb27SDimitry Andric // Representing a pointer type expression of the form `++Ptr` in an Unspecified 99206c3fb27SDimitry Andric // Pointer Context (UPC): 99306c3fb27SDimitry Andric class UPCPreIncrementGadget : public FixableGadget { 99406c3fb27SDimitry Andric private: 99506c3fb27SDimitry Andric static constexpr const char *const UPCPreIncrementTag = 99606c3fb27SDimitry Andric "PointerPreIncrementUnderUPC"; 99706c3fb27SDimitry Andric const UnaryOperator *Node; // the `++Ptr` node 99806c3fb27SDimitry Andric 99906c3fb27SDimitry Andric public: 100006c3fb27SDimitry Andric UPCPreIncrementGadget(const MatchFinder::MatchResult &Result) 100106c3fb27SDimitry Andric : FixableGadget(Kind::UPCPreIncrement), 100206c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) { 100306c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 100406c3fb27SDimitry Andric } 100506c3fb27SDimitry Andric 100606c3fb27SDimitry Andric static bool classof(const Gadget *G) { 100706c3fb27SDimitry Andric return G->getKind() == Kind::UPCPreIncrement; 100806c3fb27SDimitry Andric } 100906c3fb27SDimitry Andric 101006c3fb27SDimitry Andric static Matcher matcher() { 101106c3fb27SDimitry Andric // Note here we match `++Ptr` for any expression `Ptr` of pointer type. 101206c3fb27SDimitry Andric // Although currently we can only provide fix-its when `Ptr` is a DRE, we 101306c3fb27SDimitry Andric // can have the matcher be general, so long as `getClaimedVarUseSites` does 101406c3fb27SDimitry Andric // things right. 101506c3fb27SDimitry Andric return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 101606c3fb27SDimitry Andric unaryOperator(isPreInc(), 1017*5f757f3fSDimitry Andric hasUnaryOperand(declRefExpr( 1018*5f757f3fSDimitry Andric toSupportedVariable())) 101906c3fb27SDimitry Andric ).bind(UPCPreIncrementTag))))); 102006c3fb27SDimitry Andric } 102106c3fb27SDimitry Andric 102206c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 102306c3fb27SDimitry Andric 102406c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 102506c3fb27SDimitry Andric 102606c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 102706c3fb27SDimitry Andric return {dyn_cast<DeclRefExpr>(Node->getSubExpr())}; 102806c3fb27SDimitry Andric } 102906c3fb27SDimitry Andric }; 103006c3fb27SDimitry Andric 1031*5f757f3fSDimitry Andric // Representing a pointer type expression of the form `Ptr += n` in an 1032*5f757f3fSDimitry Andric // Unspecified Untyped Context (UUC): 1033*5f757f3fSDimitry Andric class UUCAddAssignGadget : public FixableGadget { 1034*5f757f3fSDimitry Andric private: 1035*5f757f3fSDimitry Andric static constexpr const char *const UUCAddAssignTag = 1036*5f757f3fSDimitry Andric "PointerAddAssignUnderUUC"; 1037*5f757f3fSDimitry Andric static constexpr const char *const OffsetTag = "Offset"; 1038*5f757f3fSDimitry Andric 1039*5f757f3fSDimitry Andric const BinaryOperator *Node; // the `Ptr += n` node 1040*5f757f3fSDimitry Andric const Expr *Offset = nullptr; 1041*5f757f3fSDimitry Andric 1042*5f757f3fSDimitry Andric public: 1043*5f757f3fSDimitry Andric UUCAddAssignGadget(const MatchFinder::MatchResult &Result) 1044*5f757f3fSDimitry Andric : FixableGadget(Kind::UUCAddAssign), 1045*5f757f3fSDimitry Andric Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)), 1046*5f757f3fSDimitry Andric Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) { 1047*5f757f3fSDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 1048*5f757f3fSDimitry Andric } 1049*5f757f3fSDimitry Andric 1050*5f757f3fSDimitry Andric static bool classof(const Gadget *G) { 1051*5f757f3fSDimitry Andric return G->getKind() == Kind::UUCAddAssign; 1052*5f757f3fSDimitry Andric } 1053*5f757f3fSDimitry Andric 1054*5f757f3fSDimitry Andric static Matcher matcher() { 1055*5f757f3fSDimitry Andric return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts( 1056*5f757f3fSDimitry Andric binaryOperator(hasOperatorName("+="), 1057*5f757f3fSDimitry Andric hasLHS(declRefExpr(toSupportedVariable())), 1058*5f757f3fSDimitry Andric hasRHS(expr().bind(OffsetTag))) 1059*5f757f3fSDimitry Andric .bind(UUCAddAssignTag))))); 1060*5f757f3fSDimitry Andric } 1061*5f757f3fSDimitry Andric 1062*5f757f3fSDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 1063*5f757f3fSDimitry Andric 1064*5f757f3fSDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 1065*5f757f3fSDimitry Andric 1066*5f757f3fSDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 1067*5f757f3fSDimitry Andric return {dyn_cast<DeclRefExpr>(Node->getLHS())}; 1068*5f757f3fSDimitry Andric } 1069*5f757f3fSDimitry Andric }; 1070*5f757f3fSDimitry Andric 107106c3fb27SDimitry Andric // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 + 107206c3fb27SDimitry Andric // ptr)`: 107306c3fb27SDimitry Andric class DerefSimplePtrArithFixableGadget : public FixableGadget { 107406c3fb27SDimitry Andric static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 107506c3fb27SDimitry Andric static constexpr const char *const DerefOpTag = "DerefOp"; 107606c3fb27SDimitry Andric static constexpr const char *const AddOpTag = "AddOp"; 107706c3fb27SDimitry Andric static constexpr const char *const OffsetTag = "Offset"; 107806c3fb27SDimitry Andric 107906c3fb27SDimitry Andric const DeclRefExpr *BaseDeclRefExpr = nullptr; 108006c3fb27SDimitry Andric const UnaryOperator *DerefOp = nullptr; 108106c3fb27SDimitry Andric const BinaryOperator *AddOp = nullptr; 108206c3fb27SDimitry Andric const IntegerLiteral *Offset = nullptr; 108306c3fb27SDimitry Andric 108406c3fb27SDimitry Andric public: 108506c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result) 108606c3fb27SDimitry Andric : FixableGadget(Kind::DerefSimplePtrArithFixable), 108706c3fb27SDimitry Andric BaseDeclRefExpr( 108806c3fb27SDimitry Andric Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 108906c3fb27SDimitry Andric DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)), 109006c3fb27SDimitry Andric AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)), 109106c3fb27SDimitry Andric Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {} 109206c3fb27SDimitry Andric 109306c3fb27SDimitry Andric static Matcher matcher() { 109406c3fb27SDimitry Andric // clang-format off 109506c3fb27SDimitry Andric auto ThePtr = expr(hasPointerType(), 1096*5f757f3fSDimitry Andric ignoringImpCasts(declRefExpr(toSupportedVariable()). 1097*5f757f3fSDimitry Andric bind(BaseDeclRefExprTag))); 109806c3fb27SDimitry Andric auto PlusOverPtrAndInteger = expr(anyOf( 109906c3fb27SDimitry Andric binaryOperator(hasOperatorName("+"), hasLHS(ThePtr), 110006c3fb27SDimitry Andric hasRHS(integerLiteral().bind(OffsetTag))) 110106c3fb27SDimitry Andric .bind(AddOpTag), 110206c3fb27SDimitry Andric binaryOperator(hasOperatorName("+"), hasRHS(ThePtr), 110306c3fb27SDimitry Andric hasLHS(integerLiteral().bind(OffsetTag))) 110406c3fb27SDimitry Andric .bind(AddOpTag))); 110506c3fb27SDimitry Andric return isInUnspecifiedLvalueContext(unaryOperator( 110606c3fb27SDimitry Andric hasOperatorName("*"), 110706c3fb27SDimitry Andric hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger))) 110806c3fb27SDimitry Andric .bind(DerefOpTag)); 110906c3fb27SDimitry Andric // clang-format on 111006c3fb27SDimitry Andric } 111106c3fb27SDimitry Andric 111206c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &s) const final; 111306c3fb27SDimitry Andric 111406c3fb27SDimitry Andric // TODO remove this method from FixableGadget interface 111506c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const final { return nullptr; } 111606c3fb27SDimitry Andric 111706c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const final { 111806c3fb27SDimitry Andric return {BaseDeclRefExpr}; 111906c3fb27SDimitry Andric } 112006c3fb27SDimitry Andric }; 112106c3fb27SDimitry Andric 1122bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits. 112306c3fb27SDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker> 112406c3fb27SDimitry Andric findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler, 112506c3fb27SDimitry Andric bool EmitSuggestions) { 1126bdd1243dSDimitry Andric 1127bdd1243dSDimitry Andric struct GadgetFinderCallback : MatchFinder::MatchCallback { 1128bdd1243dSDimitry Andric FixableGadgetList FixableGadgets; 1129bdd1243dSDimitry Andric WarningGadgetList WarningGadgets; 1130bdd1243dSDimitry Andric DeclUseTracker Tracker; 1131bdd1243dSDimitry Andric 1132bdd1243dSDimitry Andric void run(const MatchFinder::MatchResult &Result) override { 1133bdd1243dSDimitry Andric // In debug mode, assert that we've found exactly one gadget. 1134bdd1243dSDimitry Andric // This helps us avoid conflicts in .bind() tags. 1135bdd1243dSDimitry Andric #if NDEBUG 1136bdd1243dSDimitry Andric #define NEXT return 1137bdd1243dSDimitry Andric #else 1138bdd1243dSDimitry Andric [[maybe_unused]] int numFound = 0; 1139bdd1243dSDimitry Andric #define NEXT ++numFound 1140bdd1243dSDimitry Andric #endif 1141bdd1243dSDimitry Andric 1142bdd1243dSDimitry Andric if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) { 1143bdd1243dSDimitry Andric Tracker.discoverUse(DRE); 1144bdd1243dSDimitry Andric NEXT; 1145bdd1243dSDimitry Andric } 1146bdd1243dSDimitry Andric 1147bdd1243dSDimitry Andric if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) { 1148bdd1243dSDimitry Andric Tracker.discoverDecl(DS); 1149bdd1243dSDimitry Andric NEXT; 1150bdd1243dSDimitry Andric } 1151bdd1243dSDimitry Andric 1152bdd1243dSDimitry Andric // Figure out which matcher we've found, and call the appropriate 1153bdd1243dSDimitry Andric // subclass constructor. 1154bdd1243dSDimitry Andric // FIXME: Can we do this more logarithmically? 1155bdd1243dSDimitry Andric #define FIXABLE_GADGET(name) \ 1156bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1157bdd1243dSDimitry Andric FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1158bdd1243dSDimitry Andric NEXT; \ 1159bdd1243dSDimitry Andric } 1160bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1161bdd1243dSDimitry Andric #define WARNING_GADGET(name) \ 1162bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1163bdd1243dSDimitry Andric WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1164bdd1243dSDimitry Andric NEXT; \ 1165bdd1243dSDimitry Andric } 1166bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1167bdd1243dSDimitry Andric 1168bdd1243dSDimitry Andric assert(numFound >= 1 && "Gadgets not found in match result!"); 1169bdd1243dSDimitry Andric assert(numFound <= 1 && "Conflicting bind tags in gadgets!"); 1170bdd1243dSDimitry Andric } 1171bdd1243dSDimitry Andric }; 1172bdd1243dSDimitry Andric 1173bdd1243dSDimitry Andric MatchFinder M; 1174bdd1243dSDimitry Andric GadgetFinderCallback CB; 1175bdd1243dSDimitry Andric 1176bdd1243dSDimitry Andric // clang-format off 1177bdd1243dSDimitry Andric M.addMatcher( 117806c3fb27SDimitry Andric stmt( 117906c3fb27SDimitry Andric forEachDescendantEvaluatedStmt(stmt(anyOf( 1180bdd1243dSDimitry Andric // Add Gadget::matcher() for every gadget in the registry. 118106c3fb27SDimitry Andric #define WARNING_GADGET(x) \ 118206c3fb27SDimitry Andric allOf(x ## Gadget::matcher().bind(#x), \ 118306c3fb27SDimitry Andric notInSafeBufferOptOut(&Handler)), 118406c3fb27SDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 118506c3fb27SDimitry Andric // Avoid a hanging comma. 118606c3fb27SDimitry Andric unless(stmt()) 118706c3fb27SDimitry Andric ))) 118806c3fb27SDimitry Andric ), 118906c3fb27SDimitry Andric &CB 119006c3fb27SDimitry Andric ); 119106c3fb27SDimitry Andric // clang-format on 119206c3fb27SDimitry Andric 119306c3fb27SDimitry Andric if (EmitSuggestions) { 119406c3fb27SDimitry Andric // clang-format off 119506c3fb27SDimitry Andric M.addMatcher( 119606c3fb27SDimitry Andric stmt( 119706c3fb27SDimitry Andric forEachDescendantStmt(stmt(eachOf( 119806c3fb27SDimitry Andric #define FIXABLE_GADGET(x) \ 1199bdd1243dSDimitry Andric x ## Gadget::matcher().bind(#x), 1200bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1201bdd1243dSDimitry Andric // In parallel, match all DeclRefExprs so that to find out 1202bdd1243dSDimitry Andric // whether there are any uncovered by gadgets. 1203bdd1243dSDimitry Andric declRefExpr(anyOf(hasPointerType(), hasArrayType()), 1204*5f757f3fSDimitry Andric to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"), 1205bdd1243dSDimitry Andric // Also match DeclStmts because we'll need them when fixing 1206bdd1243dSDimitry Andric // their underlying VarDecls that otherwise don't have 1207bdd1243dSDimitry Andric // any backreferences to DeclStmts. 1208bdd1243dSDimitry Andric declStmt().bind("any_ds") 120906c3fb27SDimitry Andric ))) 121006c3fb27SDimitry Andric ), 1211bdd1243dSDimitry Andric &CB 1212bdd1243dSDimitry Andric ); 1213bdd1243dSDimitry Andric // clang-format on 121406c3fb27SDimitry Andric } 1215bdd1243dSDimitry Andric 1216bdd1243dSDimitry Andric M.match(*D->getBody(), D->getASTContext()); 121706c3fb27SDimitry Andric return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets), 121806c3fb27SDimitry Andric std::move(CB.Tracker)}; 1219bdd1243dSDimitry Andric } 1220bdd1243dSDimitry Andric 122106c3fb27SDimitry Andric // Compares AST nodes by source locations. 122206c3fb27SDimitry Andric template <typename NodeTy> struct CompareNode { 122306c3fb27SDimitry Andric bool operator()(const NodeTy *N1, const NodeTy *N2) const { 122406c3fb27SDimitry Andric return N1->getBeginLoc().getRawEncoding() < 122506c3fb27SDimitry Andric N2->getBeginLoc().getRawEncoding(); 122606c3fb27SDimitry Andric } 122706c3fb27SDimitry Andric }; 122806c3fb27SDimitry Andric 1229bdd1243dSDimitry Andric struct WarningGadgetSets { 123006c3fb27SDimitry Andric std::map<const VarDecl *, std::set<const WarningGadget *>, 123106c3fb27SDimitry Andric // To keep keys sorted by their locations in the map so that the 123206c3fb27SDimitry Andric // order is deterministic: 123306c3fb27SDimitry Andric CompareNode<VarDecl>> 123406c3fb27SDimitry Andric byVar; 1235bdd1243dSDimitry Andric // These Gadgets are not related to pointer variables (e. g. temporaries). 123606c3fb27SDimitry Andric llvm::SmallVector<const WarningGadget *, 16> noVar; 1237bdd1243dSDimitry Andric }; 1238bdd1243dSDimitry Andric 1239bdd1243dSDimitry Andric static WarningGadgetSets 124006c3fb27SDimitry Andric groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) { 1241bdd1243dSDimitry Andric WarningGadgetSets result; 1242bdd1243dSDimitry Andric // If some gadgets cover more than one 1243bdd1243dSDimitry Andric // variable, they'll appear more than once in the map. 1244bdd1243dSDimitry Andric for (auto &G : AllUnsafeOperations) { 1245bdd1243dSDimitry Andric DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites(); 1246bdd1243dSDimitry Andric 1247bdd1243dSDimitry Andric bool AssociatedWithVarDecl = false; 1248bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : ClaimedVarUseSites) { 1249bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 125006c3fb27SDimitry Andric result.byVar[VD].insert(G.get()); 1251bdd1243dSDimitry Andric AssociatedWithVarDecl = true; 1252bdd1243dSDimitry Andric } 1253bdd1243dSDimitry Andric } 1254bdd1243dSDimitry Andric 1255bdd1243dSDimitry Andric if (!AssociatedWithVarDecl) { 125606c3fb27SDimitry Andric result.noVar.push_back(G.get()); 1257bdd1243dSDimitry Andric continue; 1258bdd1243dSDimitry Andric } 1259bdd1243dSDimitry Andric } 1260bdd1243dSDimitry Andric return result; 1261bdd1243dSDimitry Andric } 1262bdd1243dSDimitry Andric 1263bdd1243dSDimitry Andric struct FixableGadgetSets { 1264*5f757f3fSDimitry Andric std::map<const VarDecl *, std::set<const FixableGadget *>, 1265*5f757f3fSDimitry Andric // To keep keys sorted by their locations in the map so that the 1266*5f757f3fSDimitry Andric // order is deterministic: 1267*5f757f3fSDimitry Andric CompareNode<VarDecl>> 1268*5f757f3fSDimitry Andric byVar; 1269bdd1243dSDimitry Andric }; 1270bdd1243dSDimitry Andric 1271bdd1243dSDimitry Andric static FixableGadgetSets 1272bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) { 1273bdd1243dSDimitry Andric FixableGadgetSets FixablesForUnsafeVars; 1274bdd1243dSDimitry Andric for (auto &F : AllFixableOperations) { 1275bdd1243dSDimitry Andric DeclUseList DREs = F->getClaimedVarUseSites(); 1276bdd1243dSDimitry Andric 1277bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : DREs) { 1278bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 127906c3fb27SDimitry Andric FixablesForUnsafeVars.byVar[VD].insert(F.get()); 1280bdd1243dSDimitry Andric } 1281bdd1243dSDimitry Andric } 1282bdd1243dSDimitry Andric } 1283bdd1243dSDimitry Andric return FixablesForUnsafeVars; 1284bdd1243dSDimitry Andric } 1285bdd1243dSDimitry Andric 128606c3fb27SDimitry Andric bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts, 128706c3fb27SDimitry Andric const SourceManager &SM) { 128806c3fb27SDimitry Andric // A simple interval overlap detection algorithm. Sorts all ranges by their 128906c3fb27SDimitry Andric // begin location then finds the first overlap in one pass. 129006c3fb27SDimitry Andric std::vector<const FixItHint *> All; // a copy of `FixIts` 129106c3fb27SDimitry Andric 129206c3fb27SDimitry Andric for (const FixItHint &H : FixIts) 129306c3fb27SDimitry Andric All.push_back(&H); 129406c3fb27SDimitry Andric std::sort(All.begin(), All.end(), 129506c3fb27SDimitry Andric [&SM](const FixItHint *H1, const FixItHint *H2) { 129606c3fb27SDimitry Andric return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(), 129706c3fb27SDimitry Andric H2->RemoveRange.getBegin()); 129806c3fb27SDimitry Andric }); 129906c3fb27SDimitry Andric 130006c3fb27SDimitry Andric const FixItHint *CurrHint = nullptr; 130106c3fb27SDimitry Andric 130206c3fb27SDimitry Andric for (const FixItHint *Hint : All) { 130306c3fb27SDimitry Andric if (!CurrHint || 130406c3fb27SDimitry Andric SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(), 130506c3fb27SDimitry Andric Hint->RemoveRange.getBegin())) { 130606c3fb27SDimitry Andric // Either to initialize `CurrHint` or `CurrHint` does not 130706c3fb27SDimitry Andric // overlap with `Hint`: 130806c3fb27SDimitry Andric CurrHint = Hint; 130906c3fb27SDimitry Andric } else 131006c3fb27SDimitry Andric // In case `Hint` overlaps the `CurrHint`, we found at least one 131106c3fb27SDimitry Andric // conflict: 131206c3fb27SDimitry Andric return true; 131306c3fb27SDimitry Andric } 131406c3fb27SDimitry Andric return false; 131506c3fb27SDimitry Andric } 131606c3fb27SDimitry Andric 131706c3fb27SDimitry Andric std::optional<FixItList> 131806c3fb27SDimitry Andric PointerAssignmentGadget::getFixits(const Strategy &S) const { 131906c3fb27SDimitry Andric const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl()); 132006c3fb27SDimitry Andric const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl()); 132106c3fb27SDimitry Andric switch (S.lookup(LeftVD)) { 132206c3fb27SDimitry Andric case Strategy::Kind::Span: 132306c3fb27SDimitry Andric if (S.lookup(RightVD) == Strategy::Kind::Span) 132406c3fb27SDimitry Andric return FixItList{}; 132506c3fb27SDimitry Andric return std::nullopt; 132606c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 132706c3fb27SDimitry Andric return std::nullopt; 132806c3fb27SDimitry Andric case Strategy::Kind::Iterator: 132906c3fb27SDimitry Andric case Strategy::Kind::Array: 133006c3fb27SDimitry Andric case Strategy::Kind::Vector: 133106c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 133206c3fb27SDimitry Andric } 133306c3fb27SDimitry Andric return std::nullopt; 133406c3fb27SDimitry Andric } 133506c3fb27SDimitry Andric 133606c3fb27SDimitry Andric std::optional<FixItList> 133706c3fb27SDimitry Andric PointerInitGadget::getFixits(const Strategy &S) const { 133806c3fb27SDimitry Andric const auto *LeftVD = PtrInitLHS; 133906c3fb27SDimitry Andric const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl()); 134006c3fb27SDimitry Andric switch (S.lookup(LeftVD)) { 134106c3fb27SDimitry Andric case Strategy::Kind::Span: 134206c3fb27SDimitry Andric if (S.lookup(RightVD) == Strategy::Kind::Span) 134306c3fb27SDimitry Andric return FixItList{}; 134406c3fb27SDimitry Andric return std::nullopt; 134506c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 134606c3fb27SDimitry Andric return std::nullopt; 134706c3fb27SDimitry Andric case Strategy::Kind::Iterator: 134806c3fb27SDimitry Andric case Strategy::Kind::Array: 134906c3fb27SDimitry Andric case Strategy::Kind::Vector: 135006c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 135106c3fb27SDimitry Andric } 135206c3fb27SDimitry Andric return std::nullopt; 135306c3fb27SDimitry Andric } 135406c3fb27SDimitry Andric 1355*5f757f3fSDimitry Andric static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, 1356*5f757f3fSDimitry Andric const ASTContext &Ctx) { 1357*5f757f3fSDimitry Andric if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) { 1358*5f757f3fSDimitry Andric if (ConstVal->isNegative()) 1359*5f757f3fSDimitry Andric return false; 1360*5f757f3fSDimitry Andric } else if (!Expr->getType()->isUnsignedIntegerType()) 1361*5f757f3fSDimitry Andric return false; 1362*5f757f3fSDimitry Andric return true; 1363*5f757f3fSDimitry Andric } 1364*5f757f3fSDimitry Andric 136506c3fb27SDimitry Andric std::optional<FixItList> 136606c3fb27SDimitry Andric ULCArraySubscriptGadget::getFixits(const Strategy &S) const { 136706c3fb27SDimitry Andric if (const auto *DRE = 136806c3fb27SDimitry Andric dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) 136906c3fb27SDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 137006c3fb27SDimitry Andric switch (S.lookup(VD)) { 137106c3fb27SDimitry Andric case Strategy::Kind::Span: { 1372*5f757f3fSDimitry Andric 137306c3fb27SDimitry Andric // If the index has a negative constant value, we give up as no valid 137406c3fb27SDimitry Andric // fix-it can be generated: 137506c3fb27SDimitry Andric const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in! 137606c3fb27SDimitry Andric VD->getASTContext(); 1377*5f757f3fSDimitry Andric if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx)) 137806c3fb27SDimitry Andric return std::nullopt; 137906c3fb27SDimitry Andric // no-op is a good fix-it, otherwise 138006c3fb27SDimitry Andric return FixItList{}; 138106c3fb27SDimitry Andric } 138206c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 138306c3fb27SDimitry Andric case Strategy::Kind::Iterator: 138406c3fb27SDimitry Andric case Strategy::Kind::Array: 138506c3fb27SDimitry Andric case Strategy::Kind::Vector: 138606c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 138706c3fb27SDimitry Andric } 138806c3fb27SDimitry Andric } 138906c3fb27SDimitry Andric return std::nullopt; 139006c3fb27SDimitry Andric } 139106c3fb27SDimitry Andric 139206c3fb27SDimitry Andric static std::optional<FixItList> // forward declaration 139306c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node); 139406c3fb27SDimitry Andric 139506c3fb27SDimitry Andric std::optional<FixItList> 139606c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget::getFixits(const Strategy &S) const { 139706c3fb27SDimitry Andric auto DREs = getClaimedVarUseSites(); 139806c3fb27SDimitry Andric const auto *VD = cast<VarDecl>(DREs.front()->getDecl()); 139906c3fb27SDimitry Andric 140006c3fb27SDimitry Andric switch (S.lookup(VD)) { 140106c3fb27SDimitry Andric case Strategy::Kind::Span: 140206c3fb27SDimitry Andric return fixUPCAddressofArraySubscriptWithSpan(Node); 140306c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 140406c3fb27SDimitry Andric case Strategy::Kind::Iterator: 140506c3fb27SDimitry Andric case Strategy::Kind::Array: 140606c3fb27SDimitry Andric case Strategy::Kind::Vector: 140706c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 140806c3fb27SDimitry Andric } 140906c3fb27SDimitry Andric return std::nullopt; // something went wrong, no fix-it 141006c3fb27SDimitry Andric } 141106c3fb27SDimitry Andric 141206c3fb27SDimitry Andric // FIXME: this function should be customizable through format 141306c3fb27SDimitry Andric static StringRef getEndOfLine() { 141406c3fb27SDimitry Andric static const char *const EOL = "\n"; 141506c3fb27SDimitry Andric return EOL; 141606c3fb27SDimitry Andric } 141706c3fb27SDimitry Andric 141806c3fb27SDimitry Andric // Returns the text indicating that the user needs to provide input there: 141906c3fb27SDimitry Andric std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") { 142006c3fb27SDimitry Andric std::string s = std::string("<# "); 142106c3fb27SDimitry Andric s += HintTextToUser; 142206c3fb27SDimitry Andric s += " #>"; 142306c3fb27SDimitry Andric return s; 142406c3fb27SDimitry Andric } 142506c3fb27SDimitry Andric 142606c3fb27SDimitry Andric // Return the text representation of the given `APInt Val`: 142706c3fb27SDimitry Andric static std::string getAPIntText(APInt Val) { 142806c3fb27SDimitry Andric SmallVector<char> Txt; 142906c3fb27SDimitry Andric Val.toString(Txt, 10, true); 143006c3fb27SDimitry Andric // APInt::toString does not add '\0' to the end of the string for us: 143106c3fb27SDimitry Andric Txt.push_back('\0'); 143206c3fb27SDimitry Andric return Txt.data(); 143306c3fb27SDimitry Andric } 143406c3fb27SDimitry Andric 143506c3fb27SDimitry Andric // Return the source location of the last character of the AST `Node`. 143606c3fb27SDimitry Andric template <typename NodeTy> 143706c3fb27SDimitry Andric static std::optional<SourceLocation> 143806c3fb27SDimitry Andric getEndCharLoc(const NodeTy *Node, const SourceManager &SM, 143906c3fb27SDimitry Andric const LangOptions &LangOpts) { 144006c3fb27SDimitry Andric unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts); 144106c3fb27SDimitry Andric SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1); 144206c3fb27SDimitry Andric 144306c3fb27SDimitry Andric if (Loc.isValid()) 144406c3fb27SDimitry Andric return Loc; 144506c3fb27SDimitry Andric 144606c3fb27SDimitry Andric return std::nullopt; 144706c3fb27SDimitry Andric } 144806c3fb27SDimitry Andric 144906c3fb27SDimitry Andric // Return the source location just past the last character of the AST `Node`. 145006c3fb27SDimitry Andric template <typename NodeTy> 145106c3fb27SDimitry Andric static std::optional<SourceLocation> getPastLoc(const NodeTy *Node, 145206c3fb27SDimitry Andric const SourceManager &SM, 145306c3fb27SDimitry Andric const LangOptions &LangOpts) { 145406c3fb27SDimitry Andric SourceLocation Loc = 145506c3fb27SDimitry Andric Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts); 145606c3fb27SDimitry Andric if (Loc.isValid()) 145706c3fb27SDimitry Andric return Loc; 145806c3fb27SDimitry Andric return std::nullopt; 145906c3fb27SDimitry Andric } 146006c3fb27SDimitry Andric 146106c3fb27SDimitry Andric // Return text representation of an `Expr`. 146206c3fb27SDimitry Andric static std::optional<StringRef> getExprText(const Expr *E, 146306c3fb27SDimitry Andric const SourceManager &SM, 146406c3fb27SDimitry Andric const LangOptions &LangOpts) { 146506c3fb27SDimitry Andric std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts); 146606c3fb27SDimitry Andric 146706c3fb27SDimitry Andric if (LastCharLoc) 146806c3fb27SDimitry Andric return Lexer::getSourceText( 146906c3fb27SDimitry Andric CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM, 147006c3fb27SDimitry Andric LangOpts); 147106c3fb27SDimitry Andric 147206c3fb27SDimitry Andric return std::nullopt; 147306c3fb27SDimitry Andric } 147406c3fb27SDimitry Andric 147506c3fb27SDimitry Andric // Returns the literal text in `SourceRange SR`, if `SR` is a valid range. 147606c3fb27SDimitry Andric static std::optional<StringRef> getRangeText(SourceRange SR, 147706c3fb27SDimitry Andric const SourceManager &SM, 147806c3fb27SDimitry Andric const LangOptions &LangOpts) { 147906c3fb27SDimitry Andric bool Invalid = false; 1480*5f757f3fSDimitry Andric CharSourceRange CSR = CharSourceRange::getCharRange(SR); 148106c3fb27SDimitry Andric StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid); 148206c3fb27SDimitry Andric 148306c3fb27SDimitry Andric if (!Invalid) 148406c3fb27SDimitry Andric return Text; 148506c3fb27SDimitry Andric return std::nullopt; 148606c3fb27SDimitry Andric } 148706c3fb27SDimitry Andric 1488*5f757f3fSDimitry Andric // Returns the begin location of the identifier of the given variable 1489*5f757f3fSDimitry Andric // declaration. 1490*5f757f3fSDimitry Andric static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) { 1491*5f757f3fSDimitry Andric // According to the implementation of `VarDecl`, `VD->getLocation()` actually 1492*5f757f3fSDimitry Andric // returns the begin location of the identifier of the declaration: 1493*5f757f3fSDimitry Andric return VD->getLocation(); 1494*5f757f3fSDimitry Andric } 1495*5f757f3fSDimitry Andric 1496*5f757f3fSDimitry Andric // Returns the literal text of the identifier of the given variable declaration. 1497*5f757f3fSDimitry Andric static std::optional<StringRef> 1498*5f757f3fSDimitry Andric getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM, 1499*5f757f3fSDimitry Andric const LangOptions &LangOpts) { 1500*5f757f3fSDimitry Andric SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD); 1501*5f757f3fSDimitry Andric SourceLocation ParmIdentEndLoc = 1502*5f757f3fSDimitry Andric Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts); 1503*5f757f3fSDimitry Andric 1504*5f757f3fSDimitry Andric if (ParmIdentEndLoc.isMacroID() && 1505*5f757f3fSDimitry Andric !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts)) 1506*5f757f3fSDimitry Andric return std::nullopt; 1507*5f757f3fSDimitry Andric return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts); 1508*5f757f3fSDimitry Andric } 1509*5f757f3fSDimitry Andric 1510*5f757f3fSDimitry Andric // We cannot fix a variable declaration if it has some other specifiers than the 1511*5f757f3fSDimitry Andric // type specifier. Because the source ranges of those specifiers could overlap 1512*5f757f3fSDimitry Andric // with the source range that is being replaced using fix-its. Especially when 1513*5f757f3fSDimitry Andric // we often cannot obtain accurate source ranges of cv-qualified type 1514*5f757f3fSDimitry Andric // specifiers. 1515*5f757f3fSDimitry Andric // FIXME: also deal with type attributes 1516*5f757f3fSDimitry Andric static bool hasUnsupportedSpecifiers(const VarDecl *VD, 1517*5f757f3fSDimitry Andric const SourceManager &SM) { 1518*5f757f3fSDimitry Andric // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the 1519*5f757f3fSDimitry Andric // source range of `VD`: 1520*5f757f3fSDimitry Andric bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool { 1521*5f757f3fSDimitry Andric return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(), 1522*5f757f3fSDimitry Andric VD->getBeginLoc())) && 1523*5f757f3fSDimitry Andric !(SM.isBeforeInTranslationUnit(VD->getEndLoc(), 1524*5f757f3fSDimitry Andric At->getRange().getBegin())); 1525*5f757f3fSDimitry Andric }); 1526*5f757f3fSDimitry Andric return VD->isInlineSpecified() || VD->isConstexpr() || 1527*5f757f3fSDimitry Andric VD->hasConstantInitialization() || !VD->hasLocalStorage() || 1528*5f757f3fSDimitry Andric AttrRangeOverlapping; 1529*5f757f3fSDimitry Andric } 1530*5f757f3fSDimitry Andric 1531*5f757f3fSDimitry Andric // Returns the `SourceRange` of `D`. The reason why this function exists is 1532*5f757f3fSDimitry Andric // that `D->getSourceRange()` may return a range where the end location is the 1533*5f757f3fSDimitry Andric // starting location of the last token. The end location of the source range 1534*5f757f3fSDimitry Andric // returned by this function is the last location of the last token. 1535*5f757f3fSDimitry Andric static SourceRange getSourceRangeToTokenEnd(const Decl *D, 1536*5f757f3fSDimitry Andric const SourceManager &SM, 1537*5f757f3fSDimitry Andric const LangOptions &LangOpts) { 1538*5f757f3fSDimitry Andric SourceLocation Begin = D->getBeginLoc(); 1539*5f757f3fSDimitry Andric SourceLocation 1540*5f757f3fSDimitry Andric End = // `D->getEndLoc` should always return the starting location of the 1541*5f757f3fSDimitry Andric // last token, so we should get the end of the token 1542*5f757f3fSDimitry Andric Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts); 1543*5f757f3fSDimitry Andric 1544*5f757f3fSDimitry Andric return SourceRange(Begin, End); 1545*5f757f3fSDimitry Andric } 1546*5f757f3fSDimitry Andric 154706c3fb27SDimitry Andric // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer 154806c3fb27SDimitry Andric // type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not 154906c3fb27SDimitry Andric // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me 155006c3fb27SDimitry Andric // :( ), `Qualifiers` of the pointee type is returned separately through the 155106c3fb27SDimitry Andric // output parameter `QualifiersToAppend`. 155206c3fb27SDimitry Andric static std::optional<std::string> 1553*5f757f3fSDimitry Andric getPointeeTypeText(const VarDecl *VD, const SourceManager &SM, 155406c3fb27SDimitry Andric const LangOptions &LangOpts, 155506c3fb27SDimitry Andric std::optional<Qualifiers> *QualifiersToAppend) { 155606c3fb27SDimitry Andric QualType Ty = VD->getType(); 155706c3fb27SDimitry Andric QualType PteTy; 155806c3fb27SDimitry Andric 155906c3fb27SDimitry Andric assert(Ty->isPointerType() && !Ty->isFunctionPointerType() && 156006c3fb27SDimitry Andric "Expecting a VarDecl of type of pointer to object type"); 156106c3fb27SDimitry Andric PteTy = Ty->getPointeeType(); 1562*5f757f3fSDimitry Andric 1563*5f757f3fSDimitry Andric TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc(); 1564*5f757f3fSDimitry Andric TypeLoc PteTyLoc; 1565*5f757f3fSDimitry Andric 1566*5f757f3fSDimitry Andric // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns 1567*5f757f3fSDimitry Andric // the `TypeLoc` of the pointee type: 1568*5f757f3fSDimitry Andric switch (TyLoc.getTypeLocClass()) { 1569*5f757f3fSDimitry Andric case TypeLoc::ConstantArray: 1570*5f757f3fSDimitry Andric case TypeLoc::IncompleteArray: 1571*5f757f3fSDimitry Andric case TypeLoc::VariableArray: 1572*5f757f3fSDimitry Andric case TypeLoc::DependentSizedArray: 1573*5f757f3fSDimitry Andric case TypeLoc::Decayed: 1574*5f757f3fSDimitry Andric assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a " 1575*5f757f3fSDimitry Andric "pointer type unless it decays."); 1576*5f757f3fSDimitry Andric PteTyLoc = TyLoc.getNextTypeLoc(); 1577*5f757f3fSDimitry Andric break; 1578*5f757f3fSDimitry Andric case TypeLoc::Pointer: 1579*5f757f3fSDimitry Andric PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc(); 1580*5f757f3fSDimitry Andric break; 1581*5f757f3fSDimitry Andric default: 1582*5f757f3fSDimitry Andric return std::nullopt; 1583*5f757f3fSDimitry Andric } 1584*5f757f3fSDimitry Andric if (PteTyLoc.isNull()) 1585*5f757f3fSDimitry Andric // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g., 1586*5f757f3fSDimitry Andric // when the pointer type is `auto`. 158706c3fb27SDimitry Andric return std::nullopt; 158806c3fb27SDimitry Andric 1589*5f757f3fSDimitry Andric SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD); 159006c3fb27SDimitry Andric 1591*5f757f3fSDimitry Andric if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) { 159206c3fb27SDimitry Andric // We are expecting these locations to be valid. But in some cases, they are 159306c3fb27SDimitry Andric // not all valid. It is a Clang bug to me and we are not responsible for 159406c3fb27SDimitry Andric // fixing it. So we will just give up for now when it happens. 159506c3fb27SDimitry Andric return std::nullopt; 159606c3fb27SDimitry Andric } 159706c3fb27SDimitry Andric 159806c3fb27SDimitry Andric // Note that TypeLoc.getEndLoc() returns the begin location of the last token: 159906c3fb27SDimitry Andric SourceLocation PteEndOfTokenLoc = 160006c3fb27SDimitry Andric Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts); 160106c3fb27SDimitry Andric 1602*5f757f3fSDimitry Andric if (!PteEndOfTokenLoc.isValid()) 1603*5f757f3fSDimitry Andric // Sometimes we cannot get the end location of the pointee type, e.g., when 1604*5f757f3fSDimitry Andric // there are macros involved. 1605*5f757f3fSDimitry Andric return std::nullopt; 1606*5f757f3fSDimitry Andric if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) { 160706c3fb27SDimitry Andric // We only deal with the cases where the source text of the pointee type 160806c3fb27SDimitry Andric // appears on the left-hand side of the variable identifier completely, 160906c3fb27SDimitry Andric // including the following forms: 161006c3fb27SDimitry Andric // `T ident`, 161106c3fb27SDimitry Andric // `T ident[]`, where `T` is any type. 161206c3fb27SDimitry Andric // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`. 161306c3fb27SDimitry Andric return std::nullopt; 161406c3fb27SDimitry Andric } 161506c3fb27SDimitry Andric if (PteTy.hasQualifiers()) { 161606c3fb27SDimitry Andric // TypeLoc does not provide source ranges for qualifiers (it says it's 161706c3fb27SDimitry Andric // intentional but seems fishy to me), so we cannot get the full text 161806c3fb27SDimitry Andric // `PteTy` via source ranges. 161906c3fb27SDimitry Andric *QualifiersToAppend = PteTy.getQualifiers(); 162006c3fb27SDimitry Andric } 162106c3fb27SDimitry Andric return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts) 162206c3fb27SDimitry Andric ->str(); 162306c3fb27SDimitry Andric } 162406c3fb27SDimitry Andric 162506c3fb27SDimitry Andric // Returns the text of the name (with qualifiers) of a `FunctionDecl`. 162606c3fb27SDimitry Andric static std::optional<StringRef> getFunNameText(const FunctionDecl *FD, 162706c3fb27SDimitry Andric const SourceManager &SM, 162806c3fb27SDimitry Andric const LangOptions &LangOpts) { 162906c3fb27SDimitry Andric SourceLocation BeginLoc = FD->getQualifier() 163006c3fb27SDimitry Andric ? FD->getQualifierLoc().getBeginLoc() 163106c3fb27SDimitry Andric : FD->getNameInfo().getBeginLoc(); 163206c3fb27SDimitry Andric // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the 163306c3fb27SDimitry Andric // last token: 163406c3fb27SDimitry Andric SourceLocation EndLoc = Lexer::getLocForEndOfToken( 163506c3fb27SDimitry Andric FD->getNameInfo().getEndLoc(), 0, SM, LangOpts); 163606c3fb27SDimitry Andric SourceRange NameRange{BeginLoc, EndLoc}; 163706c3fb27SDimitry Andric 163806c3fb27SDimitry Andric return getRangeText(NameRange, SM, LangOpts); 163906c3fb27SDimitry Andric } 164006c3fb27SDimitry Andric 1641*5f757f3fSDimitry Andric // Returns the text representing a `std::span` type where the element type is 1642*5f757f3fSDimitry Andric // represented by `EltTyText`. 1643*5f757f3fSDimitry Andric // 1644*5f757f3fSDimitry Andric // Note the optional parameter `Qualifiers`: one needs to pass qualifiers 1645*5f757f3fSDimitry Andric // explicitly if the element type needs to be qualified. 1646*5f757f3fSDimitry Andric static std::string 1647*5f757f3fSDimitry Andric getSpanTypeText(StringRef EltTyText, 1648*5f757f3fSDimitry Andric std::optional<Qualifiers> Quals = std::nullopt) { 1649*5f757f3fSDimitry Andric const char *const SpanOpen = "std::span<"; 1650*5f757f3fSDimitry Andric 1651*5f757f3fSDimitry Andric if (Quals) 1652*5f757f3fSDimitry Andric return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>'; 1653*5f757f3fSDimitry Andric return SpanOpen + EltTyText.str() + '>'; 1654*5f757f3fSDimitry Andric } 1655*5f757f3fSDimitry Andric 165606c3fb27SDimitry Andric std::optional<FixItList> 165706c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget::getFixits(const Strategy &s) const { 165806c3fb27SDimitry Andric const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl()); 165906c3fb27SDimitry Andric 166006c3fb27SDimitry Andric if (VD && s.lookup(VD) == Strategy::Kind::Span) { 166106c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 166206c3fb27SDimitry Andric // std::span can't represent elements before its begin() 166306c3fb27SDimitry Andric if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx)) 166406c3fb27SDimitry Andric if (ConstVal->isNegative()) 166506c3fb27SDimitry Andric return std::nullopt; 166606c3fb27SDimitry Andric 166706c3fb27SDimitry Andric // note that the expr may (oddly) has multiple layers of parens 166806c3fb27SDimitry Andric // example: 166906c3fb27SDimitry Andric // *((..(pointer + 123)..)) 167006c3fb27SDimitry Andric // goal: 167106c3fb27SDimitry Andric // pointer[123] 167206c3fb27SDimitry Andric // Fix-It: 167306c3fb27SDimitry Andric // remove '*(' 167406c3fb27SDimitry Andric // replace ' + ' with '[' 167506c3fb27SDimitry Andric // replace ')' with ']' 167606c3fb27SDimitry Andric 167706c3fb27SDimitry Andric // example: 167806c3fb27SDimitry Andric // *((..(123 + pointer)..)) 167906c3fb27SDimitry Andric // goal: 168006c3fb27SDimitry Andric // 123[pointer] 168106c3fb27SDimitry Andric // Fix-It: 168206c3fb27SDimitry Andric // remove '*(' 168306c3fb27SDimitry Andric // replace ' + ' with '[' 168406c3fb27SDimitry Andric // replace ')' with ']' 168506c3fb27SDimitry Andric 168606c3fb27SDimitry Andric const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS(); 168706c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 168806c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 168906c3fb27SDimitry Andric CharSourceRange StarWithTrailWhitespace = 169006c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(), 169106c3fb27SDimitry Andric LHS->getBeginLoc()); 169206c3fb27SDimitry Andric 169306c3fb27SDimitry Andric std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts); 169406c3fb27SDimitry Andric if (!LHSLocation) 169506c3fb27SDimitry Andric return std::nullopt; 169606c3fb27SDimitry Andric 169706c3fb27SDimitry Andric CharSourceRange PlusWithSurroundingWhitespace = 169806c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc()); 169906c3fb27SDimitry Andric 170006c3fb27SDimitry Andric std::optional<SourceLocation> AddOpLocation = 170106c3fb27SDimitry Andric getPastLoc(AddOp, SM, LangOpts); 170206c3fb27SDimitry Andric std::optional<SourceLocation> DerefOpLocation = 170306c3fb27SDimitry Andric getPastLoc(DerefOp, SM, LangOpts); 170406c3fb27SDimitry Andric 170506c3fb27SDimitry Andric if (!AddOpLocation || !DerefOpLocation) 170606c3fb27SDimitry Andric return std::nullopt; 170706c3fb27SDimitry Andric 170806c3fb27SDimitry Andric CharSourceRange ClosingParenWithPrecWhitespace = 170906c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation); 171006c3fb27SDimitry Andric 171106c3fb27SDimitry Andric return FixItList{ 171206c3fb27SDimitry Andric {FixItHint::CreateRemoval(StarWithTrailWhitespace), 171306c3fb27SDimitry Andric FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["), 171406c3fb27SDimitry Andric FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}}; 171506c3fb27SDimitry Andric } 171606c3fb27SDimitry Andric return std::nullopt; // something wrong or unsupported, give up 171706c3fb27SDimitry Andric } 171806c3fb27SDimitry Andric 171906c3fb27SDimitry Andric std::optional<FixItList> 172006c3fb27SDimitry Andric PointerDereferenceGadget::getFixits(const Strategy &S) const { 172106c3fb27SDimitry Andric const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl()); 172206c3fb27SDimitry Andric switch (S.lookup(VD)) { 172306c3fb27SDimitry Andric case Strategy::Kind::Span: { 172406c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 172506c3fb27SDimitry Andric SourceManager &SM = Ctx.getSourceManager(); 172606c3fb27SDimitry Andric // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0] 172706c3fb27SDimitry Andric // Deletes the *operand 172806c3fb27SDimitry Andric CharSourceRange derefRange = clang::CharSourceRange::getCharRange( 172906c3fb27SDimitry Andric Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1)); 173006c3fb27SDimitry Andric // Inserts the [0] 1731*5f757f3fSDimitry Andric if (auto LocPastOperand = 1732*5f757f3fSDimitry Andric getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) { 173306c3fb27SDimitry Andric return FixItList{{FixItHint::CreateRemoval(derefRange), 1734*5f757f3fSDimitry Andric FixItHint::CreateInsertion(*LocPastOperand, "[0]")}}; 173506c3fb27SDimitry Andric } 1736*5f757f3fSDimitry Andric break; 173706c3fb27SDimitry Andric } 173806c3fb27SDimitry Andric case Strategy::Kind::Iterator: 173906c3fb27SDimitry Andric case Strategy::Kind::Array: 174006c3fb27SDimitry Andric case Strategy::Kind::Vector: 174106c3fb27SDimitry Andric llvm_unreachable("Strategy not implemented yet!"); 174206c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 174306c3fb27SDimitry Andric llvm_unreachable("Invalid strategy!"); 174406c3fb27SDimitry Andric } 174506c3fb27SDimitry Andric 174606c3fb27SDimitry Andric return std::nullopt; 174706c3fb27SDimitry Andric } 174806c3fb27SDimitry Andric 174906c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form UPC(DRE) with 175006c3fb27SDimitry Andric // `DRE.data()` 175106c3fb27SDimitry Andric std::optional<FixItList> UPCStandalonePointerGadget::getFixits(const Strategy &S) 175206c3fb27SDimitry Andric const { 175306c3fb27SDimitry Andric const auto VD = cast<VarDecl>(Node->getDecl()); 175406c3fb27SDimitry Andric switch (S.lookup(VD)) { 175506c3fb27SDimitry Andric case Strategy::Kind::Span: { 175606c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 175706c3fb27SDimitry Andric SourceManager &SM = Ctx.getSourceManager(); 175806c3fb27SDimitry Andric // Inserts the .data() after the DRE 175906c3fb27SDimitry Andric std::optional<SourceLocation> EndOfOperand = 176006c3fb27SDimitry Andric getPastLoc(Node, SM, Ctx.getLangOpts()); 176106c3fb27SDimitry Andric 176206c3fb27SDimitry Andric if (EndOfOperand) 176306c3fb27SDimitry Andric return FixItList{{FixItHint::CreateInsertion( 176406c3fb27SDimitry Andric *EndOfOperand, ".data()")}}; 1765*5f757f3fSDimitry Andric // FIXME: Points inside a macro expansion. 1766*5f757f3fSDimitry Andric break; 176706c3fb27SDimitry Andric } 176806c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 176906c3fb27SDimitry Andric case Strategy::Kind::Iterator: 177006c3fb27SDimitry Andric case Strategy::Kind::Array: 177106c3fb27SDimitry Andric case Strategy::Kind::Vector: 177206c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 177306c3fb27SDimitry Andric } 177406c3fb27SDimitry Andric 177506c3fb27SDimitry Andric return std::nullopt; 177606c3fb27SDimitry Andric } 177706c3fb27SDimitry Andric 177806c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form `&DRE[e]` with 177906c3fb27SDimitry Andric // `&DRE.data()[e]`: 178006c3fb27SDimitry Andric static std::optional<FixItList> 178106c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) { 178206c3fb27SDimitry Andric const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr()); 178306c3fb27SDimitry Andric const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts()); 178406c3fb27SDimitry Andric // FIXME: this `getASTContext` call is costly, we should pass the 178506c3fb27SDimitry Andric // ASTContext in: 178606c3fb27SDimitry Andric const ASTContext &Ctx = DRE->getDecl()->getASTContext(); 178706c3fb27SDimitry Andric const Expr *Idx = ArraySub->getIdx(); 178806c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 178906c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 179006c3fb27SDimitry Andric std::stringstream SS; 179106c3fb27SDimitry Andric bool IdxIsLitZero = false; 179206c3fb27SDimitry Andric 179306c3fb27SDimitry Andric if (auto ICE = Idx->getIntegerConstantExpr(Ctx)) 179406c3fb27SDimitry Andric if ((*ICE).isZero()) 179506c3fb27SDimitry Andric IdxIsLitZero = true; 179606c3fb27SDimitry Andric std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts); 179706c3fb27SDimitry Andric if (!DreString) 179806c3fb27SDimitry Andric return std::nullopt; 179906c3fb27SDimitry Andric 180006c3fb27SDimitry Andric if (IdxIsLitZero) { 180106c3fb27SDimitry Andric // If the index is literal zero, we produce the most concise fix-it: 180206c3fb27SDimitry Andric SS << (*DreString).str() << ".data()"; 180306c3fb27SDimitry Andric } else { 180406c3fb27SDimitry Andric std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts); 180506c3fb27SDimitry Andric if (!IndexString) 180606c3fb27SDimitry Andric return std::nullopt; 180706c3fb27SDimitry Andric 180806c3fb27SDimitry Andric SS << "&" << (*DreString).str() << ".data()" 180906c3fb27SDimitry Andric << "[" << (*IndexString).str() << "]"; 181006c3fb27SDimitry Andric } 181106c3fb27SDimitry Andric return FixItList{ 181206c3fb27SDimitry Andric FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())}; 181306c3fb27SDimitry Andric } 181406c3fb27SDimitry Andric 1815*5f757f3fSDimitry Andric std::optional<FixItList> 1816*5f757f3fSDimitry Andric UUCAddAssignGadget::getFixits(const Strategy &S) const { 1817*5f757f3fSDimitry Andric DeclUseList DREs = getClaimedVarUseSites(); 1818*5f757f3fSDimitry Andric 1819*5f757f3fSDimitry Andric if (DREs.size() != 1) 1820*5f757f3fSDimitry Andric return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we 1821*5f757f3fSDimitry Andric // give up 1822*5f757f3fSDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) { 1823*5f757f3fSDimitry Andric if (S.lookup(VD) == Strategy::Kind::Span) { 1824*5f757f3fSDimitry Andric FixItList Fixes; 1825*5f757f3fSDimitry Andric 1826*5f757f3fSDimitry Andric const Stmt *AddAssignNode = getBaseStmt(); 1827*5f757f3fSDimitry Andric StringRef varName = VD->getName(); 1828*5f757f3fSDimitry Andric const ASTContext &Ctx = VD->getASTContext(); 1829*5f757f3fSDimitry Andric 1830*5f757f3fSDimitry Andric if (!isNonNegativeIntegerExpr(Offset, VD, Ctx)) 1831*5f757f3fSDimitry Andric return std::nullopt; 1832*5f757f3fSDimitry Andric 1833*5f757f3fSDimitry Andric // To transform UUC(p += n) to UUC(p = p.subspan(..)): 1834*5f757f3fSDimitry Andric bool NotParenExpr = 1835*5f757f3fSDimitry Andric (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc()); 1836*5f757f3fSDimitry Andric std::string SS = varName.str() + " = " + varName.str() + ".subspan"; 1837*5f757f3fSDimitry Andric if (NotParenExpr) 1838*5f757f3fSDimitry Andric SS += "("; 1839*5f757f3fSDimitry Andric 1840*5f757f3fSDimitry Andric std::optional<SourceLocation> AddAssignLocation = getEndCharLoc( 1841*5f757f3fSDimitry Andric AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts()); 1842*5f757f3fSDimitry Andric if (!AddAssignLocation) 1843*5f757f3fSDimitry Andric return std::nullopt; 1844*5f757f3fSDimitry Andric 1845*5f757f3fSDimitry Andric Fixes.push_back(FixItHint::CreateReplacement( 1846*5f757f3fSDimitry Andric SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()), 1847*5f757f3fSDimitry Andric SS)); 1848*5f757f3fSDimitry Andric if (NotParenExpr) 1849*5f757f3fSDimitry Andric Fixes.push_back(FixItHint::CreateInsertion( 1850*5f757f3fSDimitry Andric Offset->getEndLoc().getLocWithOffset(1), ")")); 1851*5f757f3fSDimitry Andric return Fixes; 1852*5f757f3fSDimitry Andric } 1853*5f757f3fSDimitry Andric } 1854*5f757f3fSDimitry Andric return std::nullopt; // Not in the cases that we can handle for now, give up. 1855*5f757f3fSDimitry Andric } 185606c3fb27SDimitry Andric 185706c3fb27SDimitry Andric std::optional<FixItList> UPCPreIncrementGadget::getFixits(const Strategy &S) const { 185806c3fb27SDimitry Andric DeclUseList DREs = getClaimedVarUseSites(); 185906c3fb27SDimitry Andric 186006c3fb27SDimitry Andric if (DREs.size() != 1) 186106c3fb27SDimitry Andric return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we 186206c3fb27SDimitry Andric // give up 186306c3fb27SDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) { 186406c3fb27SDimitry Andric if (S.lookup(VD) == Strategy::Kind::Span) { 186506c3fb27SDimitry Andric FixItList Fixes; 186606c3fb27SDimitry Andric std::stringstream SS; 186706c3fb27SDimitry Andric const Stmt *PreIncNode = getBaseStmt(); 186806c3fb27SDimitry Andric StringRef varName = VD->getName(); 186906c3fb27SDimitry Andric const ASTContext &Ctx = VD->getASTContext(); 187006c3fb27SDimitry Andric 187106c3fb27SDimitry Andric // To transform UPC(++p) to UPC((p = p.subspan(1)).data()): 187206c3fb27SDimitry Andric SS << "(" << varName.data() << " = " << varName.data() 187306c3fb27SDimitry Andric << ".subspan(1)).data()"; 187406c3fb27SDimitry Andric std::optional<SourceLocation> PreIncLocation = 187506c3fb27SDimitry Andric getEndCharLoc(PreIncNode, Ctx.getSourceManager(), Ctx.getLangOpts()); 187606c3fb27SDimitry Andric if (!PreIncLocation) 187706c3fb27SDimitry Andric return std::nullopt; 187806c3fb27SDimitry Andric 187906c3fb27SDimitry Andric Fixes.push_back(FixItHint::CreateReplacement( 188006c3fb27SDimitry Andric SourceRange(PreIncNode->getBeginLoc(), *PreIncLocation), SS.str())); 188106c3fb27SDimitry Andric return Fixes; 188206c3fb27SDimitry Andric } 188306c3fb27SDimitry Andric } 188406c3fb27SDimitry Andric return std::nullopt; // Not in the cases that we can handle for now, give up. 188506c3fb27SDimitry Andric } 188606c3fb27SDimitry Andric 188706c3fb27SDimitry Andric 188806c3fb27SDimitry Andric // For a non-null initializer `Init` of `T *` type, this function returns 188906c3fb27SDimitry Andric // `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it 189006c3fb27SDimitry Andric // to output stream. 189106c3fb27SDimitry Andric // In many cases, this function cannot figure out the actual extent `S`. It 189206c3fb27SDimitry Andric // then will use a place holder to replace `S` to ask users to fill `S` in. The 189306c3fb27SDimitry Andric // initializer shall be used to initialize a variable of type `std::span<T>`. 189406c3fb27SDimitry Andric // 189506c3fb27SDimitry Andric // FIXME: Support multi-level pointers 189606c3fb27SDimitry Andric // 189706c3fb27SDimitry Andric // Parameters: 189806c3fb27SDimitry Andric // `Init` a pointer to the initializer expression 189906c3fb27SDimitry Andric // `Ctx` a reference to the ASTContext 190006c3fb27SDimitry Andric static FixItList 1901*5f757f3fSDimitry Andric FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, 190206c3fb27SDimitry Andric const StringRef UserFillPlaceHolder) { 190306c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 190406c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 190506c3fb27SDimitry Andric 190606c3fb27SDimitry Andric // If `Init` has a constant value that is (or equivalent to) a 190706c3fb27SDimitry Andric // NULL pointer, we use the default constructor to initialize the span 190806c3fb27SDimitry Andric // object, i.e., a `std:span` variable declaration with no initializer. 190906c3fb27SDimitry Andric // So the fix-it is just to remove the initializer. 191006c3fb27SDimitry Andric if (Init->isNullPointerConstant(Ctx, 191106c3fb27SDimitry Andric // FIXME: Why does this function not ask for `const ASTContext 191206c3fb27SDimitry Andric // &`? It should. Maybe worth an NFC patch later. 191306c3fb27SDimitry Andric Expr::NullPointerConstantValueDependence:: 191406c3fb27SDimitry Andric NPC_ValueDependentIsNotNull)) { 191506c3fb27SDimitry Andric std::optional<SourceLocation> InitLocation = 191606c3fb27SDimitry Andric getEndCharLoc(Init, SM, LangOpts); 191706c3fb27SDimitry Andric if (!InitLocation) 191806c3fb27SDimitry Andric return {}; 191906c3fb27SDimitry Andric 192006c3fb27SDimitry Andric SourceRange SR(Init->getBeginLoc(), *InitLocation); 192106c3fb27SDimitry Andric 192206c3fb27SDimitry Andric return {FixItHint::CreateRemoval(SR)}; 192306c3fb27SDimitry Andric } 192406c3fb27SDimitry Andric 192506c3fb27SDimitry Andric FixItList FixIts{}; 192606c3fb27SDimitry Andric std::string ExtentText = UserFillPlaceHolder.data(); 192706c3fb27SDimitry Andric StringRef One = "1"; 192806c3fb27SDimitry Andric 192906c3fb27SDimitry Andric // Insert `{` before `Init`: 193006c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{")); 193106c3fb27SDimitry Andric // Try to get the data extent. Break into different cases: 193206c3fb27SDimitry Andric if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) { 193306c3fb27SDimitry Andric // In cases `Init` is `new T[n]` and there is no explicit cast over 193406c3fb27SDimitry Andric // `Init`, we know that `Init` must evaluates to a pointer to `n` objects 193506c3fb27SDimitry Andric // of `T`. So the extent is `n` unless `n` has side effects. Similar but 193606c3fb27SDimitry Andric // simpler for the case where `Init` is `new T`. 193706c3fb27SDimitry Andric if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) { 193806c3fb27SDimitry Andric if (!Ext->HasSideEffects(Ctx)) { 193906c3fb27SDimitry Andric std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts); 194006c3fb27SDimitry Andric if (!ExtentString) 194106c3fb27SDimitry Andric return {}; 194206c3fb27SDimitry Andric ExtentText = *ExtentString; 194306c3fb27SDimitry Andric } 194406c3fb27SDimitry Andric } else if (!CxxNew->isArray()) 194506c3fb27SDimitry Andric // Although the initializer is not allocating a buffer, the pointer 194606c3fb27SDimitry Andric // variable could still be used in buffer access operations. 194706c3fb27SDimitry Andric ExtentText = One; 194806c3fb27SDimitry Andric } else if (const auto *CArrTy = Ctx.getAsConstantArrayType( 194906c3fb27SDimitry Andric Init->IgnoreImpCasts()->getType())) { 195006c3fb27SDimitry Andric // In cases `Init` is of an array type after stripping off implicit casts, 195106c3fb27SDimitry Andric // the extent is the array size. Note that if the array size is not a 195206c3fb27SDimitry Andric // constant, we cannot use it as the extent. 195306c3fb27SDimitry Andric ExtentText = getAPIntText(CArrTy->getSize()); 195406c3fb27SDimitry Andric } else { 195506c3fb27SDimitry Andric // In cases `Init` is of the form `&Var` after stripping of implicit 195606c3fb27SDimitry Andric // casts, where `&` is the built-in operator, the extent is 1. 195706c3fb27SDimitry Andric if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts())) 195806c3fb27SDimitry Andric if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf && 195906c3fb27SDimitry Andric isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr())) 196006c3fb27SDimitry Andric ExtentText = One; 196106c3fb27SDimitry Andric // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`, 196206c3fb27SDimitry Andric // and explicit casting, etc. etc. 196306c3fb27SDimitry Andric } 196406c3fb27SDimitry Andric 196506c3fb27SDimitry Andric SmallString<32> StrBuffer{}; 196606c3fb27SDimitry Andric std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts); 196706c3fb27SDimitry Andric 196806c3fb27SDimitry Andric if (!LocPassInit) 196906c3fb27SDimitry Andric return {}; 197006c3fb27SDimitry Andric 197106c3fb27SDimitry Andric StrBuffer.append(", "); 197206c3fb27SDimitry Andric StrBuffer.append(ExtentText); 197306c3fb27SDimitry Andric StrBuffer.append("}"); 197406c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str())); 197506c3fb27SDimitry Andric return FixIts; 197606c3fb27SDimitry Andric } 197706c3fb27SDimitry Andric 1978*5f757f3fSDimitry Andric #ifndef NDEBUG 1979*5f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg) \ 1980*5f757f3fSDimitry Andric Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), "failed to produce fixit for declaration '" + (D)->getNameAsString() + "'" + (Msg)) 1981*5f757f3fSDimitry Andric #else 1982*5f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg) 1983*5f757f3fSDimitry Andric #endif 1984*5f757f3fSDimitry Andric 1985*5f757f3fSDimitry Andric // For the given variable declaration with a pointer-to-T type, returns the text 1986*5f757f3fSDimitry Andric // `std::span<T>`. If it is unable to generate the text, returns 1987*5f757f3fSDimitry Andric // `std::nullopt`. 1988*5f757f3fSDimitry Andric static std::optional<std::string> createSpanTypeForVarDecl(const VarDecl *VD, 1989*5f757f3fSDimitry Andric const ASTContext &Ctx) { 1990*5f757f3fSDimitry Andric assert(VD->getType()->isPointerType()); 1991*5f757f3fSDimitry Andric 1992*5f757f3fSDimitry Andric std::optional<Qualifiers> PteTyQualifiers = std::nullopt; 1993*5f757f3fSDimitry Andric std::optional<std::string> PteTyText = getPointeeTypeText( 1994*5f757f3fSDimitry Andric VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers); 1995*5f757f3fSDimitry Andric 1996*5f757f3fSDimitry Andric if (!PteTyText) 1997*5f757f3fSDimitry Andric return std::nullopt; 1998*5f757f3fSDimitry Andric 1999*5f757f3fSDimitry Andric std::string SpanTyText = "std::span<"; 2000*5f757f3fSDimitry Andric 2001*5f757f3fSDimitry Andric SpanTyText.append(*PteTyText); 2002*5f757f3fSDimitry Andric // Append qualifiers to span element type if any: 2003*5f757f3fSDimitry Andric if (PteTyQualifiers) { 2004*5f757f3fSDimitry Andric SpanTyText.append(" "); 2005*5f757f3fSDimitry Andric SpanTyText.append(PteTyQualifiers->getAsString()); 2006*5f757f3fSDimitry Andric } 2007*5f757f3fSDimitry Andric SpanTyText.append(">"); 2008*5f757f3fSDimitry Andric return SpanTyText; 2009*5f757f3fSDimitry Andric } 2010*5f757f3fSDimitry Andric 201106c3fb27SDimitry Andric // For a `VarDecl` of the form `T * var (= Init)?`, this 2012*5f757f3fSDimitry Andric // function generates fix-its that 2013*5f757f3fSDimitry Andric // 1) replace `T * var` with `std::span<T> var`; and 2014*5f757f3fSDimitry Andric // 2) change `Init` accordingly to a span constructor, if it exists. 201506c3fb27SDimitry Andric // 201606c3fb27SDimitry Andric // FIXME: support Multi-level pointers 201706c3fb27SDimitry Andric // 201806c3fb27SDimitry Andric // Parameters: 201906c3fb27SDimitry Andric // `D` a pointer the variable declaration node 202006c3fb27SDimitry Andric // `Ctx` a reference to the ASTContext 2021*5f757f3fSDimitry Andric // `UserFillPlaceHolder` the user-input placeholder text 202206c3fb27SDimitry Andric // Returns: 2023*5f757f3fSDimitry Andric // the non-empty fix-it list, if fix-its are successfuly generated; empty 2024*5f757f3fSDimitry Andric // list otherwise. 2025*5f757f3fSDimitry Andric static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, 2026*5f757f3fSDimitry Andric const StringRef UserFillPlaceHolder, 2027*5f757f3fSDimitry Andric UnsafeBufferUsageHandler &Handler) { 2028*5f757f3fSDimitry Andric if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager())) 2029*5f757f3fSDimitry Andric return {}; 203006c3fb27SDimitry Andric 203106c3fb27SDimitry Andric FixItList FixIts{}; 2032*5f757f3fSDimitry Andric std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx); 203306c3fb27SDimitry Andric 2034*5f757f3fSDimitry Andric if (!SpanTyText) { 2035*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type"); 2036*5f757f3fSDimitry Andric return {}; 2037*5f757f3fSDimitry Andric } 2038*5f757f3fSDimitry Andric 2039*5f757f3fSDimitry Andric // Will hold the text for `std::span<T> Ident`: 2040*5f757f3fSDimitry Andric std::stringstream SS; 2041*5f757f3fSDimitry Andric 2042*5f757f3fSDimitry Andric SS << *SpanTyText; 2043*5f757f3fSDimitry Andric // Append qualifiers to the type of `D`, if any: 2044*5f757f3fSDimitry Andric if (D->getType().hasQualifiers()) 2045*5f757f3fSDimitry Andric SS << " " << D->getType().getQualifiers().getAsString(); 2046*5f757f3fSDimitry Andric 2047*5f757f3fSDimitry Andric // The end of the range of the original source that will be replaced 2048*5f757f3fSDimitry Andric // by `std::span<T> ident`: 2049*5f757f3fSDimitry Andric SourceLocation EndLocForReplacement = D->getEndLoc(); 2050*5f757f3fSDimitry Andric std::optional<StringRef> IdentText = 2051*5f757f3fSDimitry Andric getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts()); 2052*5f757f3fSDimitry Andric 2053*5f757f3fSDimitry Andric if (!IdentText) { 2054*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier"); 2055*5f757f3fSDimitry Andric return {}; 2056*5f757f3fSDimitry Andric } 2057*5f757f3fSDimitry Andric // Fix the initializer if it exists: 205806c3fb27SDimitry Andric if (const Expr *Init = D->getInit()) { 205906c3fb27SDimitry Andric FixItList InitFixIts = 2060*5f757f3fSDimitry Andric FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder); 206106c3fb27SDimitry Andric if (InitFixIts.empty()) 206206c3fb27SDimitry Andric return {}; 206306c3fb27SDimitry Andric FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts.begin()), 206406c3fb27SDimitry Andric std::make_move_iterator(InitFixIts.end())); 2065*5f757f3fSDimitry Andric // If the declaration has the form `T *ident = init`, we want to replace 2066*5f757f3fSDimitry Andric // `T *ident = ` with `std::span<T> ident`: 2067*5f757f3fSDimitry Andric EndLocForReplacement = Init->getBeginLoc().getLocWithOffset(-1); 2068*5f757f3fSDimitry Andric } 2069*5f757f3fSDimitry Andric SS << " " << IdentText->str(); 2070*5f757f3fSDimitry Andric if (!EndLocForReplacement.isValid()) { 2071*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration"); 207206c3fb27SDimitry Andric return {}; 2073*5f757f3fSDimitry Andric } 207406c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateReplacement( 2075*5f757f3fSDimitry Andric SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str())); 207606c3fb27SDimitry Andric return FixIts; 207706c3fb27SDimitry Andric } 207806c3fb27SDimitry Andric 207906c3fb27SDimitry Andric static bool hasConflictingOverload(const FunctionDecl *FD) { 208006c3fb27SDimitry Andric return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult(); 208106c3fb27SDimitry Andric } 208206c3fb27SDimitry Andric 2083*5f757f3fSDimitry Andric // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new 2084*5f757f3fSDimitry Andric // types, this function produces fix-its to make the change self-contained. Let 2085*5f757f3fSDimitry Andric // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the 2086*5f757f3fSDimitry Andric // entity defined by the `FunctionDecl` after the change to the parameters. 2087*5f757f3fSDimitry Andric // Fix-its produced by this function are 208806c3fb27SDimitry Andric // 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration 208906c3fb27SDimitry Andric // of 'F'; 209006c3fb27SDimitry Andric // 2. Create a declaration of "NewF" next to each declaration of `F`; 209106c3fb27SDimitry Andric // 3. Create a definition of "F" (as its' original definition is now belongs 209206c3fb27SDimitry Andric // to "NewF") next to its original definition. The body of the creating 209306c3fb27SDimitry Andric // definition calls to "NewF". 209406c3fb27SDimitry Andric // 209506c3fb27SDimitry Andric // Example: 209606c3fb27SDimitry Andric // 209706c3fb27SDimitry Andric // void f(int *p); // original declaration 209806c3fb27SDimitry Andric // void f(int *p) { // original definition 209906c3fb27SDimitry Andric // p[5]; 210006c3fb27SDimitry Andric // } 210106c3fb27SDimitry Andric // 210206c3fb27SDimitry Andric // To change the parameter `p` to be of `std::span<int>` type, we 210306c3fb27SDimitry Andric // also add overloads: 210406c3fb27SDimitry Andric // 210506c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl 210606c3fb27SDimitry Andric // void f(std::span<int> p); // added overload decl 210706c3fb27SDimitry Andric // void f(std::span<int> p) { // original def where param is changed 210806c3fb27SDimitry Andric // p[5]; 210906c3fb27SDimitry Andric // } 211006c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p) { // added def 211106c3fb27SDimitry Andric // return f(std::span(p, <# size #>)); 211206c3fb27SDimitry Andric // } 211306c3fb27SDimitry Andric // 211406c3fb27SDimitry Andric static std::optional<FixItList> 2115*5f757f3fSDimitry Andric createOverloadsForFixedParams(const Strategy &S, const FunctionDecl *FD, 2116*5f757f3fSDimitry Andric const ASTContext &Ctx, 211706c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 211806c3fb27SDimitry Andric // FIXME: need to make this conflict checking better: 211906c3fb27SDimitry Andric if (hasConflictingOverload(FD)) 212006c3fb27SDimitry Andric return std::nullopt; 212106c3fb27SDimitry Andric 212206c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 212306c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 2124*5f757f3fSDimitry Andric const unsigned NumParms = FD->getNumParams(); 2125*5f757f3fSDimitry Andric std::vector<std::string> NewTysTexts(NumParms); 2126*5f757f3fSDimitry Andric std::vector<bool> ParmsMask(NumParms, false); 2127*5f757f3fSDimitry Andric bool AtLeastOneParmToFix = false; 2128*5f757f3fSDimitry Andric 2129*5f757f3fSDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 2130*5f757f3fSDimitry Andric const ParmVarDecl *PVD = FD->getParamDecl(i); 2131*5f757f3fSDimitry Andric 2132*5f757f3fSDimitry Andric if (S.lookup(PVD) == Strategy::Kind::Wontfix) 2133*5f757f3fSDimitry Andric continue; 2134*5f757f3fSDimitry Andric if (S.lookup(PVD) != Strategy::Kind::Span) 2135*5f757f3fSDimitry Andric // Not supported, not suppose to happen: 2136*5f757f3fSDimitry Andric return std::nullopt; 2137*5f757f3fSDimitry Andric 2138*5f757f3fSDimitry Andric std::optional<Qualifiers> PteTyQuals = std::nullopt; 2139*5f757f3fSDimitry Andric std::optional<std::string> PteTyText = 2140*5f757f3fSDimitry Andric getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals); 2141*5f757f3fSDimitry Andric 2142*5f757f3fSDimitry Andric if (!PteTyText) 2143*5f757f3fSDimitry Andric // something wrong in obtaining the text of the pointee type, give up 2144*5f757f3fSDimitry Andric return std::nullopt; 2145*5f757f3fSDimitry Andric // FIXME: whether we should create std::span type depends on the Strategy. 2146*5f757f3fSDimitry Andric NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals); 2147*5f757f3fSDimitry Andric ParmsMask[i] = true; 2148*5f757f3fSDimitry Andric AtLeastOneParmToFix = true; 2149*5f757f3fSDimitry Andric } 2150*5f757f3fSDimitry Andric if (!AtLeastOneParmToFix) 2151*5f757f3fSDimitry Andric // No need to create function overloads: 2152*5f757f3fSDimitry Andric return {}; 215306c3fb27SDimitry Andric // FIXME Respect indentation of the original code. 215406c3fb27SDimitry Andric 215506c3fb27SDimitry Andric // A lambda that creates the text representation of a function declaration 2156*5f757f3fSDimitry Andric // with the new type signatures: 215706c3fb27SDimitry Andric const auto NewOverloadSignatureCreator = 2158*5f757f3fSDimitry Andric [&SM, &LangOpts, &NewTysTexts, 2159*5f757f3fSDimitry Andric &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> { 216006c3fb27SDimitry Andric std::stringstream SS; 216106c3fb27SDimitry Andric 216206c3fb27SDimitry Andric SS << ";"; 216306c3fb27SDimitry Andric SS << getEndOfLine().str(); 216406c3fb27SDimitry Andric // Append: ret-type func-name "(" 216506c3fb27SDimitry Andric if (auto Prefix = getRangeText( 216606c3fb27SDimitry Andric SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()), 216706c3fb27SDimitry Andric SM, LangOpts)) 216806c3fb27SDimitry Andric SS << Prefix->str(); 216906c3fb27SDimitry Andric else 217006c3fb27SDimitry Andric return std::nullopt; // give up 217106c3fb27SDimitry Andric // Append: parameter-type-list 217206c3fb27SDimitry Andric const unsigned NumParms = FD->getNumParams(); 217306c3fb27SDimitry Andric 217406c3fb27SDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 217506c3fb27SDimitry Andric const ParmVarDecl *Parm = FD->getParamDecl(i); 217606c3fb27SDimitry Andric 217706c3fb27SDimitry Andric if (Parm->isImplicit()) 217806c3fb27SDimitry Andric continue; 2179*5f757f3fSDimitry Andric if (ParmsMask[i]) { 2180*5f757f3fSDimitry Andric // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its 2181*5f757f3fSDimitry Andric // new type: 2182*5f757f3fSDimitry Andric SS << NewTysTexts[i]; 218306c3fb27SDimitry Andric // print parameter name if provided: 218406c3fb27SDimitry Andric if (IdentifierInfo *II = Parm->getIdentifier()) 2185*5f757f3fSDimitry Andric SS << ' ' << II->getName().str(); 2186*5f757f3fSDimitry Andric } else if (auto ParmTypeText = getRangeText( 2187*5f757f3fSDimitry Andric getSourceRangeToTokenEnd(Parm, SM, LangOpts), 2188*5f757f3fSDimitry Andric SM, LangOpts)) { 218906c3fb27SDimitry Andric // print the whole `Parm` without modification: 219006c3fb27SDimitry Andric SS << ParmTypeText->str(); 219106c3fb27SDimitry Andric } else 219206c3fb27SDimitry Andric return std::nullopt; // something wrong, give up 219306c3fb27SDimitry Andric if (i != NumParms - 1) 219406c3fb27SDimitry Andric SS << ", "; 219506c3fb27SDimitry Andric } 219606c3fb27SDimitry Andric SS << ")"; 219706c3fb27SDimitry Andric return SS.str(); 219806c3fb27SDimitry Andric }; 219906c3fb27SDimitry Andric 220006c3fb27SDimitry Andric // A lambda that creates the text representation of a function definition with 220106c3fb27SDimitry Andric // the original signature: 220206c3fb27SDimitry Andric const auto OldOverloadDefCreator = 2203*5f757f3fSDimitry Andric [&Handler, &SM, &LangOpts, &NewTysTexts, 2204*5f757f3fSDimitry Andric &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> { 220506c3fb27SDimitry Andric std::stringstream SS; 220606c3fb27SDimitry Andric 220706c3fb27SDimitry Andric SS << getEndOfLine().str(); 220806c3fb27SDimitry Andric // Append: attr-name ret-type func-name "(" param-list ")" "{" 220906c3fb27SDimitry Andric if (auto FDPrefix = getRangeText( 221006c3fb27SDimitry Andric SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM, 221106c3fb27SDimitry Andric LangOpts)) 221206c3fb27SDimitry Andric SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ") 221306c3fb27SDimitry Andric << FDPrefix->str() << "{"; 221406c3fb27SDimitry Andric else 221506c3fb27SDimitry Andric return std::nullopt; 221606c3fb27SDimitry Andric // Append: "return" func-name "(" 221706c3fb27SDimitry Andric if (auto FunQualName = getFunNameText(FD, SM, LangOpts)) 221806c3fb27SDimitry Andric SS << "return " << FunQualName->str() << "("; 221906c3fb27SDimitry Andric else 222006c3fb27SDimitry Andric return std::nullopt; 222106c3fb27SDimitry Andric 222206c3fb27SDimitry Andric // Append: arg-list 222306c3fb27SDimitry Andric const unsigned NumParms = FD->getNumParams(); 222406c3fb27SDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 222506c3fb27SDimitry Andric const ParmVarDecl *Parm = FD->getParamDecl(i); 222606c3fb27SDimitry Andric 222706c3fb27SDimitry Andric if (Parm->isImplicit()) 222806c3fb27SDimitry Andric continue; 222906c3fb27SDimitry Andric // FIXME: If a parameter has no name, it is unused in the 223006c3fb27SDimitry Andric // definition. So we could just leave it as it is. 223106c3fb27SDimitry Andric if (!Parm->getIdentifier()) 223206c3fb27SDimitry Andric // If a parameter of a function definition has no name: 223306c3fb27SDimitry Andric return std::nullopt; 2234*5f757f3fSDimitry Andric if (ParmsMask[i]) 223506c3fb27SDimitry Andric // This is our spanified paramter! 2236*5f757f3fSDimitry Andric SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str() 2237*5f757f3fSDimitry Andric << ", " << getUserFillPlaceHolder("size") << ")"; 223806c3fb27SDimitry Andric else 223906c3fb27SDimitry Andric SS << Parm->getIdentifier()->getName().str(); 224006c3fb27SDimitry Andric if (i != NumParms - 1) 224106c3fb27SDimitry Andric SS << ", "; 224206c3fb27SDimitry Andric } 224306c3fb27SDimitry Andric // finish call and the body 224406c3fb27SDimitry Andric SS << ");}" << getEndOfLine().str(); 224506c3fb27SDimitry Andric // FIXME: 80-char line formatting? 224606c3fb27SDimitry Andric return SS.str(); 224706c3fb27SDimitry Andric }; 224806c3fb27SDimitry Andric 224906c3fb27SDimitry Andric FixItList FixIts{}; 225006c3fb27SDimitry Andric for (FunctionDecl *FReDecl : FD->redecls()) { 225106c3fb27SDimitry Andric std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts); 225206c3fb27SDimitry Andric 225306c3fb27SDimitry Andric if (!Loc) 225406c3fb27SDimitry Andric return {}; 225506c3fb27SDimitry Andric if (FReDecl->isThisDeclarationADefinition()) { 225606c3fb27SDimitry Andric assert(FReDecl == FD && "inconsistent function definition"); 225706c3fb27SDimitry Andric // Inserts a definition with the old signature to the end of 225806c3fb27SDimitry Andric // `FReDecl`: 2259*5f757f3fSDimitry Andric if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl)) 226006c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef)); 226106c3fb27SDimitry Andric else 226206c3fb27SDimitry Andric return {}; // give up 226306c3fb27SDimitry Andric } else { 226406c3fb27SDimitry Andric // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`: 226506c3fb27SDimitry Andric if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) { 226606c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion( 226706c3fb27SDimitry Andric FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt( 226806c3fb27SDimitry Andric FReDecl->getBeginLoc(), " "))); 226906c3fb27SDimitry Andric } 227006c3fb27SDimitry Andric // Inserts a declaration with the new signature to the end of `FReDecl`: 2271*5f757f3fSDimitry Andric if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl)) 227206c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl)); 227306c3fb27SDimitry Andric else 227406c3fb27SDimitry Andric return {}; 227506c3fb27SDimitry Andric } 227606c3fb27SDimitry Andric } 227706c3fb27SDimitry Andric return FixIts; 227806c3fb27SDimitry Andric } 227906c3fb27SDimitry Andric 2280*5f757f3fSDimitry Andric // To fix a `ParmVarDecl` to be of `std::span` type. 228106c3fb27SDimitry Andric static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, 228206c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 2283*5f757f3fSDimitry Andric if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) { 2284*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)"); 2285*5f757f3fSDimitry Andric return {}; 2286*5f757f3fSDimitry Andric } 2287*5f757f3fSDimitry Andric if (PVD->hasDefaultArg()) { 228806c3fb27SDimitry Andric // FIXME: generate fix-its for default values: 2289*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg"); 229006c3fb27SDimitry Andric return {}; 2291*5f757f3fSDimitry Andric } 229206c3fb27SDimitry Andric 229306c3fb27SDimitry Andric std::optional<Qualifiers> PteTyQualifiers = std::nullopt; 229406c3fb27SDimitry Andric std::optional<std::string> PteTyText = getPointeeTypeText( 229506c3fb27SDimitry Andric PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers); 229606c3fb27SDimitry Andric 2297*5f757f3fSDimitry Andric if (!PteTyText) { 2298*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type"); 229906c3fb27SDimitry Andric return {}; 2300*5f757f3fSDimitry Andric } 230106c3fb27SDimitry Andric 230206c3fb27SDimitry Andric std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName(); 230306c3fb27SDimitry Andric 2304*5f757f3fSDimitry Andric if (!PVDNameText) { 2305*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name"); 230606c3fb27SDimitry Andric return {}; 2307*5f757f3fSDimitry Andric } 230806c3fb27SDimitry Andric 230906c3fb27SDimitry Andric std::stringstream SS; 2310*5f757f3fSDimitry Andric std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx); 231106c3fb27SDimitry Andric 231206c3fb27SDimitry Andric if (PteTyQualifiers) 2313*5f757f3fSDimitry Andric // Append qualifiers if they exist: 2314*5f757f3fSDimitry Andric SS << getSpanTypeText(*PteTyText, PteTyQualifiers); 2315*5f757f3fSDimitry Andric else 2316*5f757f3fSDimitry Andric SS << getSpanTypeText(*PteTyText); 231706c3fb27SDimitry Andric // Append qualifiers to the type of the parameter: 231806c3fb27SDimitry Andric if (PVD->getType().hasQualifiers()) 2319*5f757f3fSDimitry Andric SS << ' ' << PVD->getType().getQualifiers().getAsString(); 232006c3fb27SDimitry Andric // Append parameter's name: 2321*5f757f3fSDimitry Andric SS << ' ' << PVDNameText->str(); 2322*5f757f3fSDimitry Andric // Add replacement fix-it: 2323*5f757f3fSDimitry Andric return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())}; 232406c3fb27SDimitry Andric } 232506c3fb27SDimitry Andric 232606c3fb27SDimitry Andric static FixItList fixVariableWithSpan(const VarDecl *VD, 232706c3fb27SDimitry Andric const DeclUseTracker &Tracker, 232806c3fb27SDimitry Andric ASTContext &Ctx, 232906c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 233006c3fb27SDimitry Andric const DeclStmt *DS = Tracker.lookupDecl(VD); 2331*5f757f3fSDimitry Andric if (!DS) { 2332*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : variables declared this way not implemented yet"); 2333*5f757f3fSDimitry Andric return {}; 2334*5f757f3fSDimitry Andric } 233506c3fb27SDimitry Andric if (!DS->isSingleDecl()) { 233606c3fb27SDimitry Andric // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` 2337*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls"); 233806c3fb27SDimitry Andric return {}; 233906c3fb27SDimitry Andric } 234006c3fb27SDimitry Andric // Currently DS is an unused variable but we'll need it when 234106c3fb27SDimitry Andric // non-single decls are implemented, where the pointee type name 234206c3fb27SDimitry Andric // and the '*' are spread around the place. 234306c3fb27SDimitry Andric (void)DS; 234406c3fb27SDimitry Andric 234506c3fb27SDimitry Andric // FIXME: handle cases where DS has multiple declarations 2346*5f757f3fSDimitry Andric return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler); 234706c3fb27SDimitry Andric } 234806c3fb27SDimitry Andric 234906c3fb27SDimitry Andric // TODO: we should be consistent to use `std::nullopt` to represent no-fix due 235006c3fb27SDimitry Andric // to any unexpected problem. 235106c3fb27SDimitry Andric static FixItList 235206c3fb27SDimitry Andric fixVariable(const VarDecl *VD, Strategy::Kind K, 235306c3fb27SDimitry Andric /* The function decl under analysis */ const Decl *D, 235406c3fb27SDimitry Andric const DeclUseTracker &Tracker, ASTContext &Ctx, 235506c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 235606c3fb27SDimitry Andric if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) { 235706c3fb27SDimitry Andric auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext()); 2358*5f757f3fSDimitry Andric if (!FD || FD != D) { 235906c3fb27SDimitry Andric // `FD != D` means that `PVD` belongs to a function that is not being 236006c3fb27SDimitry Andric // analyzed currently. Thus `FD` may not be complete. 2361*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed"); 236206c3fb27SDimitry Andric return {}; 2363*5f757f3fSDimitry Andric } 236406c3fb27SDimitry Andric 236506c3fb27SDimitry Andric // TODO If function has a try block we can't change params unless we check 236606c3fb27SDimitry Andric // also its catch block for their use. 236706c3fb27SDimitry Andric // FIXME We might support static class methods, some select methods, 236806c3fb27SDimitry Andric // operators and possibly lamdas. 236906c3fb27SDimitry Andric if (FD->isMain() || FD->isConstexpr() || 237006c3fb27SDimitry Andric FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate || 237106c3fb27SDimitry Andric FD->isVariadic() || 237206c3fb27SDimitry Andric // also covers call-operator of lamdas 237306c3fb27SDimitry Andric isa<CXXMethodDecl>(FD) || 237406c3fb27SDimitry Andric // skip when the function body is a try-block 237506c3fb27SDimitry Andric (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) || 2376*5f757f3fSDimitry Andric FD->isOverloadedOperator()) { 2377*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl"); 237806c3fb27SDimitry Andric return {}; // TODO test all these cases 237906c3fb27SDimitry Andric } 2380*5f757f3fSDimitry Andric } 238106c3fb27SDimitry Andric 238206c3fb27SDimitry Andric switch (K) { 238306c3fb27SDimitry Andric case Strategy::Kind::Span: { 238406c3fb27SDimitry Andric if (VD->getType()->isPointerType()) { 238506c3fb27SDimitry Andric if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) 238606c3fb27SDimitry Andric return fixParamWithSpan(PVD, Ctx, Handler); 238706c3fb27SDimitry Andric 238806c3fb27SDimitry Andric if (VD->isLocalVarDecl()) 238906c3fb27SDimitry Andric return fixVariableWithSpan(VD, Tracker, Ctx, Handler); 239006c3fb27SDimitry Andric } 2391*5f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer"); 239206c3fb27SDimitry Andric return {}; 239306c3fb27SDimitry Andric } 239406c3fb27SDimitry Andric case Strategy::Kind::Iterator: 239506c3fb27SDimitry Andric case Strategy::Kind::Array: 239606c3fb27SDimitry Andric case Strategy::Kind::Vector: 239706c3fb27SDimitry Andric llvm_unreachable("Strategy not implemented yet!"); 239806c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 239906c3fb27SDimitry Andric llvm_unreachable("Invalid strategy!"); 240006c3fb27SDimitry Andric } 240106c3fb27SDimitry Andric llvm_unreachable("Unknown strategy!"); 240206c3fb27SDimitry Andric } 240306c3fb27SDimitry Andric 240406c3fb27SDimitry Andric // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the 240506c3fb27SDimitry Andric // `RemoveRange` of 'h' overlaps with a macro use. 240606c3fb27SDimitry Andric static bool overlapWithMacro(const FixItList &FixIts) { 240706c3fb27SDimitry Andric // FIXME: For now we only check if the range (or the first token) is (part of) 240806c3fb27SDimitry Andric // a macro expansion. Ideally, we want to check for all tokens in the range. 240906c3fb27SDimitry Andric return llvm::any_of(FixIts, [](const FixItHint &Hint) { 241006c3fb27SDimitry Andric auto Range = Hint.RemoveRange; 241106c3fb27SDimitry Andric if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID()) 241206c3fb27SDimitry Andric // If the range (or the first token) is (part of) a macro expansion: 241306c3fb27SDimitry Andric return true; 241406c3fb27SDimitry Andric return false; 241506c3fb27SDimitry Andric }); 241606c3fb27SDimitry Andric } 241706c3fb27SDimitry Andric 2418*5f757f3fSDimitry Andric // Returns true iff `VD` is a parameter of the declaration `D`: 2419*5f757f3fSDimitry Andric static bool isParameterOf(const VarDecl *VD, const Decl *D) { 2420*5f757f3fSDimitry Andric return isa<ParmVarDecl>(VD) && 2421*5f757f3fSDimitry Andric VD->getDeclContext() == dyn_cast<DeclContext>(D); 242206c3fb27SDimitry Andric } 242306c3fb27SDimitry Andric 2424*5f757f3fSDimitry Andric // Erases variables in `FixItsForVariable`, if such a variable has an unfixable 2425*5f757f3fSDimitry Andric // group mate. A variable `v` is unfixable iff `FixItsForVariable` does not 2426*5f757f3fSDimitry Andric // contain `v`. 2427*5f757f3fSDimitry Andric static void eraseVarsForUnfixableGroupMates( 2428*5f757f3fSDimitry Andric std::map<const VarDecl *, FixItList> &FixItsForVariable, 2429*5f757f3fSDimitry Andric const VariableGroupsManager &VarGrpMgr) { 2430*5f757f3fSDimitry Andric // Variables will be removed from `FixItsForVariable`: 2431*5f757f3fSDimitry Andric SmallVector<const VarDecl *, 8> ToErase; 2432*5f757f3fSDimitry Andric 2433*5f757f3fSDimitry Andric for (const auto &[VD, Ignore] : FixItsForVariable) { 2434*5f757f3fSDimitry Andric VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD); 2435*5f757f3fSDimitry Andric if (llvm::any_of(Grp, 2436*5f757f3fSDimitry Andric [&FixItsForVariable](const VarDecl *GrpMember) -> bool { 2437*5f757f3fSDimitry Andric return !FixItsForVariable.count(GrpMember); 2438*5f757f3fSDimitry Andric })) { 2439*5f757f3fSDimitry Andric // At least one group member cannot be fixed, so we have to erase the 2440*5f757f3fSDimitry Andric // whole group: 2441*5f757f3fSDimitry Andric for (const VarDecl *Member : Grp) 2442*5f757f3fSDimitry Andric ToErase.push_back(Member); 2443*5f757f3fSDimitry Andric } 2444*5f757f3fSDimitry Andric } 2445*5f757f3fSDimitry Andric for (auto *VarToErase : ToErase) 2446*5f757f3fSDimitry Andric FixItsForVariable.erase(VarToErase); 2447*5f757f3fSDimitry Andric } 2448*5f757f3fSDimitry Andric 2449*5f757f3fSDimitry Andric // Returns the fix-its that create bounds-safe function overloads for the 2450*5f757f3fSDimitry Andric // function `D`, if `D`'s parameters will be changed to safe-types through 2451*5f757f3fSDimitry Andric // fix-its in `FixItsForVariable`. 2452*5f757f3fSDimitry Andric // 2453*5f757f3fSDimitry Andric // NOTE: In case `D`'s parameters will be changed but bounds-safe function 2454*5f757f3fSDimitry Andric // overloads cannot created, the whole group that contains the parameters will 2455*5f757f3fSDimitry Andric // be erased from `FixItsForVariable`. 2456*5f757f3fSDimitry Andric static FixItList createFunctionOverloadsForParms( 2457*5f757f3fSDimitry Andric std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */, 2458*5f757f3fSDimitry Andric const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, 2459*5f757f3fSDimitry Andric const Strategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler) { 2460*5f757f3fSDimitry Andric FixItList FixItsSharedByParms{}; 2461*5f757f3fSDimitry Andric 2462*5f757f3fSDimitry Andric std::optional<FixItList> OverloadFixes = 2463*5f757f3fSDimitry Andric createOverloadsForFixedParams(S, FD, Ctx, Handler); 2464*5f757f3fSDimitry Andric 2465*5f757f3fSDimitry Andric if (OverloadFixes) { 2466*5f757f3fSDimitry Andric FixItsSharedByParms.append(*OverloadFixes); 2467*5f757f3fSDimitry Andric } else { 2468*5f757f3fSDimitry Andric // Something wrong in generating `OverloadFixes`, need to remove the 2469*5f757f3fSDimitry Andric // whole group, where parameters are in, from `FixItsForVariable` (Note 2470*5f757f3fSDimitry Andric // that all parameters should be in the same group): 2471*5f757f3fSDimitry Andric for (auto *Member : VarGrpMgr.getGroupOfParms()) 2472*5f757f3fSDimitry Andric FixItsForVariable.erase(Member); 2473*5f757f3fSDimitry Andric } 2474*5f757f3fSDimitry Andric return FixItsSharedByParms; 2475*5f757f3fSDimitry Andric } 2476*5f757f3fSDimitry Andric 2477*5f757f3fSDimitry Andric // Constructs self-contained fix-its for each variable in `FixablesForAllVars`. 2478bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList> 247906c3fb27SDimitry Andric getFixIts(FixableGadgetSets &FixablesForAllVars, const Strategy &S, 248006c3fb27SDimitry Andric ASTContext &Ctx, 248106c3fb27SDimitry Andric /* The function decl under analysis */ const Decl *D, 248206c3fb27SDimitry Andric const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, 2483*5f757f3fSDimitry Andric const VariableGroupsManager &VarGrpMgr) { 2484*5f757f3fSDimitry Andric // `FixItsForVariable` will map each variable to a set of fix-its directly 2485*5f757f3fSDimitry Andric // associated to the variable itself. Fix-its of distinct variables in 2486*5f757f3fSDimitry Andric // `FixItsForVariable` are disjoint. 2487bdd1243dSDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariable; 2488*5f757f3fSDimitry Andric 2489*5f757f3fSDimitry Andric // Populate `FixItsForVariable` with fix-its directly associated with each 2490*5f757f3fSDimitry Andric // variable. Fix-its directly associated to a variable 'v' are the ones 2491*5f757f3fSDimitry Andric // produced by the `FixableGadget`s whose claimed variable is 'v'. 249206c3fb27SDimitry Andric for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) { 249306c3fb27SDimitry Andric FixItsForVariable[VD] = 249406c3fb27SDimitry Andric fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler); 249506c3fb27SDimitry Andric // If we fail to produce Fix-It for the declaration we have to skip the 249606c3fb27SDimitry Andric // variable entirely. 249706c3fb27SDimitry Andric if (FixItsForVariable[VD].empty()) { 249806c3fb27SDimitry Andric FixItsForVariable.erase(VD); 249906c3fb27SDimitry Andric continue; 250006c3fb27SDimitry Andric } 2501bdd1243dSDimitry Andric for (const auto &F : Fixables) { 250206c3fb27SDimitry Andric std::optional<FixItList> Fixits = F->getFixits(S); 250306c3fb27SDimitry Andric 2504*5f757f3fSDimitry Andric if (Fixits) { 2505bdd1243dSDimitry Andric FixItsForVariable[VD].insert(FixItsForVariable[VD].end(), 2506*5f757f3fSDimitry Andric Fixits->begin(), Fixits->end()); 2507*5f757f3fSDimitry Andric continue; 2508*5f757f3fSDimitry Andric } 2509*5f757f3fSDimitry Andric #ifndef NDEBUG 2510*5f757f3fSDimitry Andric Handler.addDebugNoteForVar( 2511*5f757f3fSDimitry Andric VD, F->getBaseStmt()->getBeginLoc(), 2512*5f757f3fSDimitry Andric ("gadget '" + F->getDebugName() + "' refused to produce a fix") 2513*5f757f3fSDimitry Andric .str()); 2514*5f757f3fSDimitry Andric #endif 251506c3fb27SDimitry Andric FixItsForVariable.erase(VD); 2516*5f757f3fSDimitry Andric break; 2517*5f757f3fSDimitry Andric } 2518*5f757f3fSDimitry Andric } 2519*5f757f3fSDimitry Andric 2520*5f757f3fSDimitry Andric // `FixItsForVariable` now contains only variables that can be 2521*5f757f3fSDimitry Andric // fixed. A variable can be fixed if its' declaration and all Fixables 2522*5f757f3fSDimitry Andric // associated to it can all be fixed. 2523*5f757f3fSDimitry Andric 2524*5f757f3fSDimitry Andric // To further remove from `FixItsForVariable` variables whose group mates 2525*5f757f3fSDimitry Andric // cannot be fixed... 2526*5f757f3fSDimitry Andric eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr); 2527*5f757f3fSDimitry Andric // Now `FixItsForVariable` gets further reduced: a variable is in 2528*5f757f3fSDimitry Andric // `FixItsForVariable` iff it can be fixed and all its group mates can be 2529*5f757f3fSDimitry Andric // fixed. 2530*5f757f3fSDimitry Andric 2531*5f757f3fSDimitry Andric // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`. 2532*5f757f3fSDimitry Andric // That is, when fixing multiple parameters in one step, these fix-its will 2533*5f757f3fSDimitry Andric // be applied only once (instead of being applied per parameter). 2534*5f757f3fSDimitry Andric FixItList FixItsSharedByParms{}; 2535*5f757f3fSDimitry Andric 2536*5f757f3fSDimitry Andric if (auto *FD = dyn_cast<FunctionDecl>(D)) 2537*5f757f3fSDimitry Andric FixItsSharedByParms = createFunctionOverloadsForParms( 2538*5f757f3fSDimitry Andric FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler); 2539*5f757f3fSDimitry Andric 2540*5f757f3fSDimitry Andric // The map that maps each variable `v` to fix-its for the whole group where 2541*5f757f3fSDimitry Andric // `v` is in: 2542*5f757f3fSDimitry Andric std::map<const VarDecl *, FixItList> FinalFixItsForVariable{ 2543*5f757f3fSDimitry Andric FixItsForVariable}; 2544*5f757f3fSDimitry Andric 2545*5f757f3fSDimitry Andric for (auto &[Var, Ignore] : FixItsForVariable) { 2546*5f757f3fSDimitry Andric bool AnyParm = false; 2547*5f757f3fSDimitry Andric const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm); 2548*5f757f3fSDimitry Andric 2549*5f757f3fSDimitry Andric for (const VarDecl *GrpMate : VarGroupForVD) { 2550*5f757f3fSDimitry Andric if (Var == GrpMate) 255106c3fb27SDimitry Andric continue; 2552*5f757f3fSDimitry Andric if (FixItsForVariable.count(GrpMate)) 2553*5f757f3fSDimitry Andric FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]); 255406c3fb27SDimitry Andric } 2555*5f757f3fSDimitry Andric if (AnyParm) { 2556*5f757f3fSDimitry Andric // This assertion should never fail. Otherwise we have a bug. 2557*5f757f3fSDimitry Andric assert(!FixItsSharedByParms.empty() && 2558*5f757f3fSDimitry Andric "Should not try to fix a parameter that does not belong to a " 2559*5f757f3fSDimitry Andric "FunctionDecl"); 2560*5f757f3fSDimitry Andric FinalFixItsForVariable[Var].append(FixItsSharedByParms); 2561*5f757f3fSDimitry Andric } 2562*5f757f3fSDimitry Andric } 2563*5f757f3fSDimitry Andric // Fix-its that will be applied in one step shall NOT: 2564*5f757f3fSDimitry Andric // 1. overlap with macros or/and templates; or 2565*5f757f3fSDimitry Andric // 2. conflict with each other. 2566*5f757f3fSDimitry Andric // Otherwise, the fix-its will be dropped. 2567*5f757f3fSDimitry Andric for (auto Iter = FinalFixItsForVariable.begin(); 2568*5f757f3fSDimitry Andric Iter != FinalFixItsForVariable.end();) 2569*5f757f3fSDimitry Andric if (overlapWithMacro(Iter->second) || 2570*5f757f3fSDimitry Andric clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) { 2571*5f757f3fSDimitry Andric Iter = FinalFixItsForVariable.erase(Iter); 2572*5f757f3fSDimitry Andric } else 2573*5f757f3fSDimitry Andric Iter++; 2574*5f757f3fSDimitry Andric return FinalFixItsForVariable; 257506c3fb27SDimitry Andric } 257606c3fb27SDimitry Andric 2577*5f757f3fSDimitry Andric template <typename VarDeclIterTy> 2578bdd1243dSDimitry Andric static Strategy 2579*5f757f3fSDimitry Andric getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) { 2580bdd1243dSDimitry Andric Strategy S; 2581bdd1243dSDimitry Andric for (const VarDecl *VD : UnsafeVars) { 2582bdd1243dSDimitry Andric S.set(VD, Strategy::Kind::Span); 2583bdd1243dSDimitry Andric } 2584bdd1243dSDimitry Andric return S; 2585bdd1243dSDimitry Andric } 2586bdd1243dSDimitry Andric 2587*5f757f3fSDimitry Andric // Manages variable groups: 2588*5f757f3fSDimitry Andric class VariableGroupsManagerImpl : public VariableGroupsManager { 2589*5f757f3fSDimitry Andric const std::vector<VarGrpTy> Groups; 2590*5f757f3fSDimitry Andric const std::map<const VarDecl *, unsigned> &VarGrpMap; 2591*5f757f3fSDimitry Andric const llvm::SetVector<const VarDecl *> &GrpsUnionForParms; 2592*5f757f3fSDimitry Andric 2593*5f757f3fSDimitry Andric public: 2594*5f757f3fSDimitry Andric VariableGroupsManagerImpl( 2595*5f757f3fSDimitry Andric const std::vector<VarGrpTy> &Groups, 2596*5f757f3fSDimitry Andric const std::map<const VarDecl *, unsigned> &VarGrpMap, 2597*5f757f3fSDimitry Andric const llvm::SetVector<const VarDecl *> &GrpsUnionForParms) 2598*5f757f3fSDimitry Andric : Groups(Groups), VarGrpMap(VarGrpMap), 2599*5f757f3fSDimitry Andric GrpsUnionForParms(GrpsUnionForParms) {} 2600*5f757f3fSDimitry Andric 2601*5f757f3fSDimitry Andric VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override { 2602*5f757f3fSDimitry Andric if (GrpsUnionForParms.contains(Var)) { 2603*5f757f3fSDimitry Andric if (HasParm) 2604*5f757f3fSDimitry Andric *HasParm = true; 2605*5f757f3fSDimitry Andric return GrpsUnionForParms.getArrayRef(); 2606*5f757f3fSDimitry Andric } 2607*5f757f3fSDimitry Andric if (HasParm) 2608*5f757f3fSDimitry Andric *HasParm = false; 2609*5f757f3fSDimitry Andric 2610*5f757f3fSDimitry Andric auto It = VarGrpMap.find(Var); 2611*5f757f3fSDimitry Andric 2612*5f757f3fSDimitry Andric if (It == VarGrpMap.end()) 2613*5f757f3fSDimitry Andric return std::nullopt; 2614*5f757f3fSDimitry Andric return Groups[It->second]; 2615*5f757f3fSDimitry Andric } 2616*5f757f3fSDimitry Andric 2617*5f757f3fSDimitry Andric VarGrpRef getGroupOfParms() const override { 2618*5f757f3fSDimitry Andric return GrpsUnionForParms.getArrayRef(); 2619*5f757f3fSDimitry Andric } 2620*5f757f3fSDimitry Andric }; 2621*5f757f3fSDimitry Andric 2622bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D, 262306c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler, 262406c3fb27SDimitry Andric bool EmitSuggestions) { 2625*5f757f3fSDimitry Andric #ifndef NDEBUG 2626*5f757f3fSDimitry Andric Handler.clearDebugNotes(); 2627*5f757f3fSDimitry Andric #endif 2628bdd1243dSDimitry Andric 2629*5f757f3fSDimitry Andric assert(D && D->getBody()); 263006c3fb27SDimitry Andric // We do not want to visit a Lambda expression defined inside a method independently. 263106c3fb27SDimitry Andric // Instead, it should be visited along with the outer method. 2632*5f757f3fSDimitry Andric // FIXME: do we want to do the same thing for `BlockDecl`s? 263306c3fb27SDimitry Andric if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) { 263406c3fb27SDimitry Andric if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass()) 263506c3fb27SDimitry Andric return; 2636bdd1243dSDimitry Andric } 2637bdd1243dSDimitry Andric 263806c3fb27SDimitry Andric // Do not emit fixit suggestions for functions declared in an 263906c3fb27SDimitry Andric // extern "C" block. 264006c3fb27SDimitry Andric if (const auto *FD = dyn_cast<FunctionDecl>(D)) { 264106c3fb27SDimitry Andric for (FunctionDecl *FReDecl : FD->redecls()) { 264206c3fb27SDimitry Andric if (FReDecl->isExternC()) { 264306c3fb27SDimitry Andric EmitSuggestions = false; 264406c3fb27SDimitry Andric break; 264506c3fb27SDimitry Andric } 264606c3fb27SDimitry Andric } 264706c3fb27SDimitry Andric } 264806c3fb27SDimitry Andric 264906c3fb27SDimitry Andric WarningGadgetSets UnsafeOps; 265006c3fb27SDimitry Andric FixableGadgetSets FixablesForAllVars; 265106c3fb27SDimitry Andric 265206c3fb27SDimitry Andric auto [FixableGadgets, WarningGadgets, Tracker] = 265306c3fb27SDimitry Andric findGadgets(D, Handler, EmitSuggestions); 265406c3fb27SDimitry Andric 265506c3fb27SDimitry Andric if (!EmitSuggestions) { 265606c3fb27SDimitry Andric // Our job is very easy without suggestions. Just warn about 265706c3fb27SDimitry Andric // every problematic operation and consider it done. No need to deal 265806c3fb27SDimitry Andric // with fixable gadgets, no need to group operations by variable. 265906c3fb27SDimitry Andric for (const auto &G : WarningGadgets) { 266006c3fb27SDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), 266106c3fb27SDimitry Andric /*IsRelatedToDecl=*/false); 266206c3fb27SDimitry Andric } 266306c3fb27SDimitry Andric 266406c3fb27SDimitry Andric // This return guarantees that most of the machine doesn't run when 266506c3fb27SDimitry Andric // suggestions aren't requested. 266606c3fb27SDimitry Andric assert(FixableGadgets.size() == 0 && 266706c3fb27SDimitry Andric "Fixable gadgets found but suggestions not requested!"); 266806c3fb27SDimitry Andric return; 266906c3fb27SDimitry Andric } 267006c3fb27SDimitry Andric 2671*5f757f3fSDimitry Andric // If no `WarningGadget`s ever matched, there is no unsafe operations in the 2672*5f757f3fSDimitry Andric // function under the analysis. No need to fix any Fixables. 2673*5f757f3fSDimitry Andric if (!WarningGadgets.empty()) { 2674*5f757f3fSDimitry Andric // Gadgets "claim" variables they're responsible for. Once this loop 2675*5f757f3fSDimitry Andric // finishes, the tracker will only track DREs that weren't claimed by any 2676*5f757f3fSDimitry Andric // gadgets, i.e. not understood by the analysis. 2677*5f757f3fSDimitry Andric for (const auto &G : FixableGadgets) { 2678*5f757f3fSDimitry Andric for (const auto *DRE : G->getClaimedVarUseSites()) { 2679*5f757f3fSDimitry Andric Tracker.claimUse(DRE); 2680*5f757f3fSDimitry Andric } 2681*5f757f3fSDimitry Andric } 2682*5f757f3fSDimitry Andric } 2683*5f757f3fSDimitry Andric 2684*5f757f3fSDimitry Andric // If no `WarningGadget`s ever matched, there is no unsafe operations in the 2685*5f757f3fSDimitry Andric // function under the analysis. Thus, it early returns here as there is 2686*5f757f3fSDimitry Andric // nothing needs to be fixed. 2687*5f757f3fSDimitry Andric // 2688*5f757f3fSDimitry Andric // Note this claim is based on the assumption that there is no unsafe 2689*5f757f3fSDimitry Andric // variable whose declaration is invisible from the analyzing function. 2690*5f757f3fSDimitry Andric // Otherwise, we need to consider if the uses of those unsafe varuables needs 2691*5f757f3fSDimitry Andric // fix. 2692*5f757f3fSDimitry Andric // So far, we are not fixing any global variables or class members. And, 2693*5f757f3fSDimitry Andric // lambdas will be analyzed along with the enclosing function. So this early 2694*5f757f3fSDimitry Andric // return is correct for now. 2695*5f757f3fSDimitry Andric if (WarningGadgets.empty()) 2696*5f757f3fSDimitry Andric return; 2697*5f757f3fSDimitry Andric 269806c3fb27SDimitry Andric UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); 269906c3fb27SDimitry Andric FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets)); 270006c3fb27SDimitry Andric 270106c3fb27SDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariableGroup; 270206c3fb27SDimitry Andric 2703bdd1243dSDimitry Andric // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. 270406c3fb27SDimitry Andric for (auto it = FixablesForAllVars.byVar.cbegin(); 270506c3fb27SDimitry Andric it != FixablesForAllVars.byVar.cend();) { 270606c3fb27SDimitry Andric // FIXME: need to deal with global variables later 2707*5f757f3fSDimitry Andric if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) { 2708*5f757f3fSDimitry Andric #ifndef NDEBUG 2709*5f757f3fSDimitry Andric Handler.addDebugNoteForVar( 2710*5f757f3fSDimitry Andric it->first, it->first->getBeginLoc(), 2711*5f757f3fSDimitry Andric ("failed to produce fixit for '" + it->first->getNameAsString() + 2712*5f757f3fSDimitry Andric "' : neither local nor a parameter")); 2713*5f757f3fSDimitry Andric #endif 2714*5f757f3fSDimitry Andric it = FixablesForAllVars.byVar.erase(it); 2715*5f757f3fSDimitry Andric } else if (it->first->getType().getCanonicalType()->isReferenceType()) { 2716*5f757f3fSDimitry Andric #ifndef NDEBUG 2717*5f757f3fSDimitry Andric Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(), 2718*5f757f3fSDimitry Andric ("failed to produce fixit for '" + 2719*5f757f3fSDimitry Andric it->first->getNameAsString() + 2720*5f757f3fSDimitry Andric "' : has a reference type")); 2721*5f757f3fSDimitry Andric #endif 2722*5f757f3fSDimitry Andric it = FixablesForAllVars.byVar.erase(it); 2723*5f757f3fSDimitry Andric } else if (Tracker.hasUnclaimedUses(it->first)) { 2724*5f757f3fSDimitry Andric #ifndef NDEBUG 2725*5f757f3fSDimitry Andric auto AllUnclaimed = Tracker.getUnclaimedUses(it->first); 2726*5f757f3fSDimitry Andric for (auto UnclaimedDRE : AllUnclaimed) { 2727*5f757f3fSDimitry Andric std::string UnclaimedUseTrace = 2728*5f757f3fSDimitry Andric getDREAncestorString(UnclaimedDRE, D->getASTContext()); 2729*5f757f3fSDimitry Andric 2730*5f757f3fSDimitry Andric Handler.addDebugNoteForVar( 2731*5f757f3fSDimitry Andric it->first, UnclaimedDRE->getBeginLoc(), 2732*5f757f3fSDimitry Andric ("failed to produce fixit for '" + it->first->getNameAsString() + 2733*5f757f3fSDimitry Andric "' : has an unclaimed use\nThe unclaimed DRE trace: " + 2734*5f757f3fSDimitry Andric UnclaimedUseTrace)); 2735*5f757f3fSDimitry Andric } 2736*5f757f3fSDimitry Andric #endif 2737*5f757f3fSDimitry Andric it = FixablesForAllVars.byVar.erase(it); 2738*5f757f3fSDimitry Andric } else if (it->first->isInitCapture()) { 2739*5f757f3fSDimitry Andric #ifndef NDEBUG 2740*5f757f3fSDimitry Andric Handler.addDebugNoteForVar( 2741*5f757f3fSDimitry Andric it->first, it->first->getBeginLoc(), 2742*5f757f3fSDimitry Andric ("failed to produce fixit for '" + it->first->getNameAsString() + 2743*5f757f3fSDimitry Andric "' : init capture")); 2744*5f757f3fSDimitry Andric #endif 274506c3fb27SDimitry Andric it = FixablesForAllVars.byVar.erase(it); 2746bdd1243dSDimitry Andric }else { 2747bdd1243dSDimitry Andric ++it; 2748bdd1243dSDimitry Andric } 2749bdd1243dSDimitry Andric } 2750bdd1243dSDimitry Andric 275106c3fb27SDimitry Andric // Fixpoint iteration for pointer assignments 2752*5f757f3fSDimitry Andric using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>; 275306c3fb27SDimitry Andric DepMapTy DependenciesMap{}; 275406c3fb27SDimitry Andric DepMapTy PtrAssignmentGraph{}; 275506c3fb27SDimitry Andric 275606c3fb27SDimitry Andric for (auto it : FixablesForAllVars.byVar) { 275706c3fb27SDimitry Andric for (const FixableGadget *fixable : it.second) { 275806c3fb27SDimitry Andric std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair = 275906c3fb27SDimitry Andric fixable->getStrategyImplications(); 276006c3fb27SDimitry Andric if (ImplPair) { 2761*5f757f3fSDimitry Andric std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair); 276206c3fb27SDimitry Andric PtrAssignmentGraph[Impl.first].insert(Impl.second); 276306c3fb27SDimitry Andric } 276406c3fb27SDimitry Andric } 276506c3fb27SDimitry Andric } 276606c3fb27SDimitry Andric 276706c3fb27SDimitry Andric /* 276806c3fb27SDimitry Andric The following code does a BFS traversal of the `PtrAssignmentGraph` 276906c3fb27SDimitry Andric considering all unsafe vars as starting nodes and constructs an undirected 277006c3fb27SDimitry Andric graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner 277106c3fb27SDimitry Andric elimiates all variables that are unreachable from any unsafe var. In other 277206c3fb27SDimitry Andric words, this removes all dependencies that don't include any unsafe variable 277306c3fb27SDimitry Andric and consequently don't need any fixit generation. 277406c3fb27SDimitry Andric Note: A careful reader would observe that the code traverses 277506c3fb27SDimitry Andric `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and 277606c3fb27SDimitry Andric `Adj` and not between `CurrentVar` and `Adj`. Both approaches would 277706c3fb27SDimitry Andric achieve the same result but the one used here dramatically cuts the 277806c3fb27SDimitry Andric amount of hoops the second part of the algorithm needs to jump, given that 277906c3fb27SDimitry Andric a lot of these connections become "direct". The reader is advised not to 278006c3fb27SDimitry Andric imagine how the graph is transformed because of using `Var` instead of 278106c3fb27SDimitry Andric `CurrentVar`. The reader can continue reading as if `CurrentVar` was used, 278206c3fb27SDimitry Andric and think about why it's equivalent later. 278306c3fb27SDimitry Andric */ 278406c3fb27SDimitry Andric std::set<const VarDecl *> VisitedVarsDirected{}; 278506c3fb27SDimitry Andric for (const auto &[Var, ignore] : UnsafeOps.byVar) { 278606c3fb27SDimitry Andric if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) { 278706c3fb27SDimitry Andric 278806c3fb27SDimitry Andric std::queue<const VarDecl*> QueueDirected{}; 278906c3fb27SDimitry Andric QueueDirected.push(Var); 279006c3fb27SDimitry Andric while(!QueueDirected.empty()) { 279106c3fb27SDimitry Andric const VarDecl* CurrentVar = QueueDirected.front(); 279206c3fb27SDimitry Andric QueueDirected.pop(); 279306c3fb27SDimitry Andric VisitedVarsDirected.insert(CurrentVar); 279406c3fb27SDimitry Andric auto AdjacentNodes = PtrAssignmentGraph[CurrentVar]; 279506c3fb27SDimitry Andric for (const VarDecl *Adj : AdjacentNodes) { 279606c3fb27SDimitry Andric if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) { 279706c3fb27SDimitry Andric QueueDirected.push(Adj); 279806c3fb27SDimitry Andric } 279906c3fb27SDimitry Andric DependenciesMap[Var].insert(Adj); 280006c3fb27SDimitry Andric DependenciesMap[Adj].insert(Var); 280106c3fb27SDimitry Andric } 280206c3fb27SDimitry Andric } 280306c3fb27SDimitry Andric } 280406c3fb27SDimitry Andric } 280506c3fb27SDimitry Andric 2806*5f757f3fSDimitry Andric // `Groups` stores the set of Connected Components in the graph. 2807*5f757f3fSDimitry Andric std::vector<VarGrpTy> Groups; 2808*5f757f3fSDimitry Andric // `VarGrpMap` maps variables that need fix to the groups (indexes) that the 2809*5f757f3fSDimitry Andric // variables belong to. Group indexes refer to the elements in `Groups`. 2810*5f757f3fSDimitry Andric // `VarGrpMap` is complete in that every variable that needs fix is in it. 2811*5f757f3fSDimitry Andric std::map<const VarDecl *, unsigned> VarGrpMap; 2812*5f757f3fSDimitry Andric // The union group over the ones in "Groups" that contain parameters of `D`: 2813*5f757f3fSDimitry Andric llvm::SetVector<const VarDecl *> 2814*5f757f3fSDimitry Andric GrpsUnionForParms; // these variables need to be fixed in one step 2815*5f757f3fSDimitry Andric 281606c3fb27SDimitry Andric // Group Connected Components for Unsafe Vars 281706c3fb27SDimitry Andric // (Dependencies based on pointer assignments) 281806c3fb27SDimitry Andric std::set<const VarDecl *> VisitedVars{}; 281906c3fb27SDimitry Andric for (const auto &[Var, ignore] : UnsafeOps.byVar) { 282006c3fb27SDimitry Andric if (VisitedVars.find(Var) == VisitedVars.end()) { 2821*5f757f3fSDimitry Andric VarGrpTy &VarGroup = Groups.emplace_back(); 282206c3fb27SDimitry Andric std::queue<const VarDecl*> Queue{}; 2823*5f757f3fSDimitry Andric 282406c3fb27SDimitry Andric Queue.push(Var); 282506c3fb27SDimitry Andric while(!Queue.empty()) { 282606c3fb27SDimitry Andric const VarDecl* CurrentVar = Queue.front(); 282706c3fb27SDimitry Andric Queue.pop(); 282806c3fb27SDimitry Andric VisitedVars.insert(CurrentVar); 282906c3fb27SDimitry Andric VarGroup.push_back(CurrentVar); 283006c3fb27SDimitry Andric auto AdjacentNodes = DependenciesMap[CurrentVar]; 283106c3fb27SDimitry Andric for (const VarDecl *Adj : AdjacentNodes) { 283206c3fb27SDimitry Andric if (VisitedVars.find(Adj) == VisitedVars.end()) { 283306c3fb27SDimitry Andric Queue.push(Adj); 283406c3fb27SDimitry Andric } 283506c3fb27SDimitry Andric } 283606c3fb27SDimitry Andric } 2837*5f757f3fSDimitry Andric 2838*5f757f3fSDimitry Andric bool HasParm = false; 2839*5f757f3fSDimitry Andric unsigned GrpIdx = Groups.size() - 1; 2840*5f757f3fSDimitry Andric 284106c3fb27SDimitry Andric for (const VarDecl *V : VarGroup) { 2842*5f757f3fSDimitry Andric VarGrpMap[V] = GrpIdx; 2843*5f757f3fSDimitry Andric if (!HasParm && isParameterOf(V, D)) 2844*5f757f3fSDimitry Andric HasParm = true; 284506c3fb27SDimitry Andric } 2846*5f757f3fSDimitry Andric if (HasParm) 2847*5f757f3fSDimitry Andric GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end()); 284806c3fb27SDimitry Andric } 284906c3fb27SDimitry Andric } 285006c3fb27SDimitry Andric 2851*5f757f3fSDimitry Andric // Remove a `FixableGadget` if the associated variable is not in the graph 2852*5f757f3fSDimitry Andric // computed above. We do not want to generate fix-its for such variables, 2853*5f757f3fSDimitry Andric // since they are neither warned nor reachable from a warned one. 2854*5f757f3fSDimitry Andric // 2855*5f757f3fSDimitry Andric // Note a variable is not warned if it is not directly used in any unsafe 2856*5f757f3fSDimitry Andric // operation. A variable `v` is NOT reachable from an unsafe variable, if it 2857*5f757f3fSDimitry Andric // does not exist another variable `u` such that `u` is warned and fixing `u` 2858*5f757f3fSDimitry Andric // (transitively) implicates fixing `v`. 2859*5f757f3fSDimitry Andric // 2860*5f757f3fSDimitry Andric // For example, 2861*5f757f3fSDimitry Andric // ``` 2862*5f757f3fSDimitry Andric // void f(int * p) { 2863*5f757f3fSDimitry Andric // int * a = p; *p = 0; 2864*5f757f3fSDimitry Andric // } 2865*5f757f3fSDimitry Andric // ``` 2866*5f757f3fSDimitry Andric // `*p = 0` is a fixable gadget associated with a variable `p` that is neither 2867*5f757f3fSDimitry Andric // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of 2868*5f757f3fSDimitry Andric // the function above, `p` becomes reachable from a warned variable. 2869*5f757f3fSDimitry Andric for (auto I = FixablesForAllVars.byVar.begin(); 2870*5f757f3fSDimitry Andric I != FixablesForAllVars.byVar.end();) { 2871*5f757f3fSDimitry Andric // Note `VisitedVars` contain all the variables in the graph: 2872*5f757f3fSDimitry Andric if (!VisitedVars.count((*I).first)) { 2873*5f757f3fSDimitry Andric // no such var in graph: 2874*5f757f3fSDimitry Andric I = FixablesForAllVars.byVar.erase(I); 2875*5f757f3fSDimitry Andric } else 2876*5f757f3fSDimitry Andric ++I; 2877*5f757f3fSDimitry Andric } 287806c3fb27SDimitry Andric 2879*5f757f3fSDimitry Andric // We assign strategies to variables that are 1) in the graph and 2) can be 2880*5f757f3fSDimitry Andric // fixed. Other variables have the default "Won't fix" strategy. 2881*5f757f3fSDimitry Andric Strategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range( 2882*5f757f3fSDimitry Andric VisitedVars, [&FixablesForAllVars](const VarDecl *V) { 2883*5f757f3fSDimitry Andric // If a warned variable has no "Fixable", it is considered unfixable: 2884*5f757f3fSDimitry Andric return FixablesForAllVars.byVar.count(V); 2885*5f757f3fSDimitry Andric })); 2886*5f757f3fSDimitry Andric VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms); 2887*5f757f3fSDimitry Andric 2888*5f757f3fSDimitry Andric if (isa<NamedDecl>(D)) 2889*5f757f3fSDimitry Andric // The only case where `D` is not a `NamedDecl` is when `D` is a 2890*5f757f3fSDimitry Andric // `BlockDecl`. Let's not fix variables in blocks for now 289106c3fb27SDimitry Andric FixItsForVariableGroup = 289206c3fb27SDimitry Andric getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D, 2893*5f757f3fSDimitry Andric Tracker, Handler, VarGrpMgr); 2894bdd1243dSDimitry Andric 2895bdd1243dSDimitry Andric for (const auto &G : UnsafeOps.noVar) { 2896bdd1243dSDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false); 2897bdd1243dSDimitry Andric } 2898bdd1243dSDimitry Andric 2899bdd1243dSDimitry Andric for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) { 290006c3fb27SDimitry Andric auto FixItsIt = FixItsForVariableGroup.find(VD); 2901*5f757f3fSDimitry Andric Handler.handleUnsafeVariableGroup(VD, VarGrpMgr, 290206c3fb27SDimitry Andric FixItsIt != FixItsForVariableGroup.end() 2903bdd1243dSDimitry Andric ? std::move(FixItsIt->second) 2904*5f757f3fSDimitry Andric : FixItList{}, 2905*5f757f3fSDimitry Andric D); 2906bdd1243dSDimitry Andric for (const auto &G : WarningGadgets) { 2907bdd1243dSDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true); 2908bdd1243dSDimitry Andric } 2909bdd1243dSDimitry Andric } 2910bdd1243dSDimitry Andric } 2911