1bdd1243dSDimitry Andric //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric 9bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsage.h" 10*06c3fb27SDimitry Andric #include "clang/AST/Decl.h" 11bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 12bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 13*06c3fb27SDimitry Andric #include "clang/Lex/Lexer.h" 14*06c3fb27SDimitry Andric #include "clang/Lex/Preprocessor.h" 15bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h" 16bdd1243dSDimitry Andric #include <memory> 17bdd1243dSDimitry Andric #include <optional> 18*06c3fb27SDimitry Andric #include <sstream> 19*06c3fb27SDimitry Andric #include <queue> 20bdd1243dSDimitry Andric 21bdd1243dSDimitry Andric using namespace llvm; 22bdd1243dSDimitry Andric using namespace clang; 23bdd1243dSDimitry Andric using namespace ast_matchers; 24bdd1243dSDimitry Andric 25bdd1243dSDimitry Andric namespace clang::ast_matchers { 26bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n" 27bdd1243dSDimitry Andric // except for those belonging to a different callable of "n". 28bdd1243dSDimitry Andric class MatchDescendantVisitor 29bdd1243dSDimitry Andric : public RecursiveASTVisitor<MatchDescendantVisitor> { 30bdd1243dSDimitry Andric public: 31bdd1243dSDimitry Andric typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase; 32bdd1243dSDimitry Andric 33bdd1243dSDimitry Andric // Creates an AST visitor that matches `Matcher` on all 34bdd1243dSDimitry Andric // descendants of a given node "n" except for the ones 35bdd1243dSDimitry Andric // belonging to a different callable of "n". 36bdd1243dSDimitry Andric MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, 37bdd1243dSDimitry Andric internal::ASTMatchFinder *Finder, 38bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *Builder, 39*06c3fb27SDimitry Andric internal::ASTMatchFinder::BindKind Bind, 40*06c3fb27SDimitry Andric const bool ignoreUnevaluatedContext) 41bdd1243dSDimitry Andric : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind), 42*06c3fb27SDimitry Andric Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {} 43bdd1243dSDimitry Andric 44bdd1243dSDimitry Andric // Returns true if a match is found in a subtree of `DynNode`, which belongs 45bdd1243dSDimitry Andric // to the same callable of `DynNode`. 46bdd1243dSDimitry Andric bool findMatch(const DynTypedNode &DynNode) { 47bdd1243dSDimitry Andric Matches = false; 48bdd1243dSDimitry Andric if (const Stmt *StmtNode = DynNode.get<Stmt>()) { 49bdd1243dSDimitry Andric TraverseStmt(const_cast<Stmt *>(StmtNode)); 50bdd1243dSDimitry Andric *Builder = ResultBindings; 51bdd1243dSDimitry Andric return Matches; 52bdd1243dSDimitry Andric } 53bdd1243dSDimitry Andric return false; 54bdd1243dSDimitry Andric } 55bdd1243dSDimitry Andric 56bdd1243dSDimitry Andric // The following are overriding methods from the base visitor class. 57bdd1243dSDimitry Andric // They are public only to allow CRTP to work. They are *not *part 58bdd1243dSDimitry Andric // of the public API of this class. 59bdd1243dSDimitry Andric 60bdd1243dSDimitry Andric // For the matchers so far used in safe buffers, we only need to match 61bdd1243dSDimitry Andric // `Stmt`s. To override more as needed. 62bdd1243dSDimitry Andric 63bdd1243dSDimitry Andric bool TraverseDecl(Decl *Node) { 64bdd1243dSDimitry Andric if (!Node) 65bdd1243dSDimitry Andric return true; 66bdd1243dSDimitry Andric if (!match(*Node)) 67bdd1243dSDimitry Andric return false; 68bdd1243dSDimitry Andric // To skip callables: 69bdd1243dSDimitry Andric if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node)) 70bdd1243dSDimitry Andric return true; 71bdd1243dSDimitry Andric // Traverse descendants 72bdd1243dSDimitry Andric return VisitorBase::TraverseDecl(Node); 73bdd1243dSDimitry Andric } 74bdd1243dSDimitry Andric 75*06c3fb27SDimitry Andric bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) { 76*06c3fb27SDimitry Andric // These are unevaluated, except the result expression. 77*06c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 78*06c3fb27SDimitry Andric return TraverseStmt(Node->getResultExpr()); 79*06c3fb27SDimitry Andric return VisitorBase::TraverseGenericSelectionExpr(Node); 80*06c3fb27SDimitry Andric } 81*06c3fb27SDimitry Andric 82*06c3fb27SDimitry Andric bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { 83*06c3fb27SDimitry Andric // Unevaluated context. 84*06c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 85*06c3fb27SDimitry Andric return true; 86*06c3fb27SDimitry Andric return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node); 87*06c3fb27SDimitry Andric } 88*06c3fb27SDimitry Andric 89*06c3fb27SDimitry Andric bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) { 90*06c3fb27SDimitry Andric // Unevaluated context. 91*06c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 92*06c3fb27SDimitry Andric return true; 93*06c3fb27SDimitry Andric return VisitorBase::TraverseTypeOfExprTypeLoc(Node); 94*06c3fb27SDimitry Andric } 95*06c3fb27SDimitry Andric 96*06c3fb27SDimitry Andric bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) { 97*06c3fb27SDimitry Andric // Unevaluated context. 98*06c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 99*06c3fb27SDimitry Andric return true; 100*06c3fb27SDimitry Andric return VisitorBase::TraverseDecltypeTypeLoc(Node); 101*06c3fb27SDimitry Andric } 102*06c3fb27SDimitry Andric 103*06c3fb27SDimitry Andric bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) { 104*06c3fb27SDimitry Andric // Unevaluated context. 105*06c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 106*06c3fb27SDimitry Andric return true; 107*06c3fb27SDimitry Andric return VisitorBase::TraverseCXXNoexceptExpr(Node); 108*06c3fb27SDimitry Andric } 109*06c3fb27SDimitry Andric 110*06c3fb27SDimitry Andric bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) { 111*06c3fb27SDimitry Andric // Unevaluated context. 112*06c3fb27SDimitry Andric if(ignoreUnevaluatedContext) 113*06c3fb27SDimitry Andric return true; 114*06c3fb27SDimitry Andric return VisitorBase::TraverseCXXTypeidExpr(Node); 115*06c3fb27SDimitry Andric } 116*06c3fb27SDimitry Andric 117bdd1243dSDimitry Andric bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) { 118bdd1243dSDimitry Andric if (!Node) 119bdd1243dSDimitry Andric return true; 120bdd1243dSDimitry Andric if (!match(*Node)) 121bdd1243dSDimitry Andric return false; 122bdd1243dSDimitry Andric return VisitorBase::TraverseStmt(Node); 123bdd1243dSDimitry Andric } 124bdd1243dSDimitry Andric 125bdd1243dSDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; } 126bdd1243dSDimitry Andric bool shouldVisitImplicitCode() const { 127bdd1243dSDimitry Andric // TODO: let's ignore implicit code for now 128bdd1243dSDimitry Andric return false; 129bdd1243dSDimitry Andric } 130bdd1243dSDimitry Andric 131bdd1243dSDimitry Andric private: 132bdd1243dSDimitry Andric // Sets 'Matched' to true if 'Matcher' matches 'Node' 133bdd1243dSDimitry Andric // 134bdd1243dSDimitry Andric // Returns 'true' if traversal should continue after this function 135bdd1243dSDimitry Andric // returns, i.e. if no match is found or 'Bind' is 'BK_All'. 136bdd1243dSDimitry Andric template <typename T> bool match(const T &Node) { 137bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder); 138bdd1243dSDimitry Andric 139bdd1243dSDimitry Andric if (Matcher->matches(DynTypedNode::create(Node), Finder, 140bdd1243dSDimitry Andric &RecursiveBuilder)) { 141bdd1243dSDimitry Andric ResultBindings.addMatch(RecursiveBuilder); 142bdd1243dSDimitry Andric Matches = true; 143bdd1243dSDimitry Andric if (Bind != internal::ASTMatchFinder::BK_All) 144bdd1243dSDimitry Andric return false; // Abort as soon as a match is found. 145bdd1243dSDimitry Andric } 146bdd1243dSDimitry Andric return true; 147bdd1243dSDimitry Andric } 148bdd1243dSDimitry Andric 149bdd1243dSDimitry Andric const internal::DynTypedMatcher *const Matcher; 150bdd1243dSDimitry Andric internal::ASTMatchFinder *const Finder; 151bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *const Builder; 152bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder ResultBindings; 153bdd1243dSDimitry Andric const internal::ASTMatchFinder::BindKind Bind; 154bdd1243dSDimitry Andric bool Matches; 155*06c3fb27SDimitry Andric bool ignoreUnevaluatedContext; 156bdd1243dSDimitry Andric }; 157bdd1243dSDimitry Andric 158*06c3fb27SDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that. 159*06c3fb27SDimitry Andric static auto hasPointerType() { 160*06c3fb27SDimitry Andric return hasType(hasCanonicalType(pointerType())); 161*06c3fb27SDimitry Andric } 162*06c3fb27SDimitry Andric 163*06c3fb27SDimitry Andric static auto hasArrayType() { 164*06c3fb27SDimitry Andric return hasType(hasCanonicalType(arrayType())); 165*06c3fb27SDimitry Andric } 166*06c3fb27SDimitry Andric 167*06c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>, innerMatcher) { 168bdd1243dSDimitry Andric const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 169bdd1243dSDimitry Andric 170*06c3fb27SDimitry Andric MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, true); 171bdd1243dSDimitry Andric return Visitor.findMatch(DynTypedNode::create(Node)); 172bdd1243dSDimitry Andric } 173*06c3fb27SDimitry Andric 174*06c3fb27SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>, innerMatcher) { 175*06c3fb27SDimitry Andric const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 176*06c3fb27SDimitry Andric 177*06c3fb27SDimitry Andric MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, false); 178*06c3fb27SDimitry Andric return Visitor.findMatch(DynTypedNode::create(Node)); 179*06c3fb27SDimitry Andric } 180*06c3fb27SDimitry Andric 181*06c3fb27SDimitry Andric // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region 182*06c3fb27SDimitry Andric AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *, 183*06c3fb27SDimitry Andric Handler) { 184*06c3fb27SDimitry Andric return !Handler->isSafeBufferOptOut(Node.getBeginLoc()); 185*06c3fb27SDimitry Andric } 186*06c3fb27SDimitry Andric 187*06c3fb27SDimitry Andric AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) { 188*06c3fb27SDimitry Andric return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder); 189*06c3fb27SDimitry Andric } 190*06c3fb27SDimitry Andric 191*06c3fb27SDimitry Andric // Matches a `UnaryOperator` whose operator is pre-increment: 192*06c3fb27SDimitry Andric AST_MATCHER(UnaryOperator, isPreInc) { 193*06c3fb27SDimitry Andric return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc; 194*06c3fb27SDimitry Andric } 195*06c3fb27SDimitry Andric 196*06c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher` 197*06c3fb27SDimitry Andric // matches 'e' and 'e' is in an Unspecified Lvalue Context. 198*06c3fb27SDimitry Andric static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) { 199*06c3fb27SDimitry Andric // clang-format off 200*06c3fb27SDimitry Andric return 201*06c3fb27SDimitry Andric expr(anyOf( 202*06c3fb27SDimitry Andric implicitCastExpr( 203*06c3fb27SDimitry Andric hasCastKind(CastKind::CK_LValueToRValue), 204*06c3fb27SDimitry Andric castSubExpr(innerMatcher)), 205*06c3fb27SDimitry Andric binaryOperator( 206*06c3fb27SDimitry Andric hasAnyOperatorName("="), 207*06c3fb27SDimitry Andric hasLHS(innerMatcher) 208*06c3fb27SDimitry Andric ) 209*06c3fb27SDimitry Andric )); 210*06c3fb27SDimitry Andric // clang-format on 211*06c3fb27SDimitry Andric } 212*06c3fb27SDimitry Andric 213*06c3fb27SDimitry Andric 214*06c3fb27SDimitry Andric // Returns a matcher that matches any expression `e` such that `InnerMatcher` 215*06c3fb27SDimitry Andric // matches `e` and `e` is in an Unspecified Pointer Context (UPC). 216*06c3fb27SDimitry Andric static internal::Matcher<Stmt> 217*06c3fb27SDimitry Andric isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) { 218*06c3fb27SDimitry Andric // A UPC can be 219*06c3fb27SDimitry Andric // 1. an argument of a function call (except the callee has [[unsafe_...]] 220*06c3fb27SDimitry Andric // attribute), or 221*06c3fb27SDimitry Andric // 2. the operand of a pointer-to-(integer or bool) cast operation; or 222*06c3fb27SDimitry Andric // 3. the operand of a comparator operation; or 223*06c3fb27SDimitry Andric // 4. the operand of a pointer subtraction operation 224*06c3fb27SDimitry Andric // (i.e., computing the distance between two pointers); or ... 225*06c3fb27SDimitry Andric 226*06c3fb27SDimitry Andric auto CallArgMatcher = 227*06c3fb27SDimitry Andric callExpr(forEachArgumentWithParam(InnerMatcher, 228*06c3fb27SDimitry Andric hasPointerType() /* array also decays to pointer type*/), 229*06c3fb27SDimitry Andric unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))))); 230*06c3fb27SDimitry Andric 231*06c3fb27SDimitry Andric auto CastOperandMatcher = 232*06c3fb27SDimitry Andric castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral), 233*06c3fb27SDimitry Andric hasCastKind(CastKind::CK_PointerToBoolean)), 234*06c3fb27SDimitry Andric castSubExpr(allOf(hasPointerType(), InnerMatcher))); 235*06c3fb27SDimitry Andric 236*06c3fb27SDimitry Andric auto CompOperandMatcher = 237*06c3fb27SDimitry Andric binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="), 238*06c3fb27SDimitry Andric eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)), 239*06c3fb27SDimitry Andric hasRHS(allOf(hasPointerType(), InnerMatcher)))); 240*06c3fb27SDimitry Andric 241*06c3fb27SDimitry Andric // A matcher that matches pointer subtractions: 242*06c3fb27SDimitry Andric auto PtrSubtractionMatcher = 243*06c3fb27SDimitry Andric binaryOperator(hasOperatorName("-"), 244*06c3fb27SDimitry Andric // Note that here we need both LHS and RHS to be 245*06c3fb27SDimitry Andric // pointer. Then the inner matcher can match any of 246*06c3fb27SDimitry Andric // them: 247*06c3fb27SDimitry Andric allOf(hasLHS(hasPointerType()), 248*06c3fb27SDimitry Andric hasRHS(hasPointerType())), 249*06c3fb27SDimitry Andric eachOf(hasLHS(InnerMatcher), 250*06c3fb27SDimitry Andric hasRHS(InnerMatcher))); 251*06c3fb27SDimitry Andric 252*06c3fb27SDimitry Andric return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher, 253*06c3fb27SDimitry Andric PtrSubtractionMatcher)); 254*06c3fb27SDimitry Andric // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we 255*06c3fb27SDimitry Andric // don't have to check that.) 256*06c3fb27SDimitry Andric } 257*06c3fb27SDimitry Andric 258*06c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher` 259*06c3fb27SDimitry Andric // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression 260*06c3fb27SDimitry Andric // 'e' isn't evaluated to an RValue). For example, consider the following code: 261*06c3fb27SDimitry Andric // int *p = new int[4]; 262*06c3fb27SDimitry Andric // int *q = new int[4]; 263*06c3fb27SDimitry Andric // if ((p = q)) {} 264*06c3fb27SDimitry Andric // p = q; 265*06c3fb27SDimitry Andric // The expression `p = q` in the conditional of the `if` statement 266*06c3fb27SDimitry Andric // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;` 267*06c3fb27SDimitry Andric // in the assignment statement is in an untyped context. 268*06c3fb27SDimitry Andric static internal::Matcher<Stmt> 269*06c3fb27SDimitry Andric isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) { 270*06c3fb27SDimitry Andric // An unspecified context can be 271*06c3fb27SDimitry Andric // 1. A compound statement, 272*06c3fb27SDimitry Andric // 2. The body of an if statement 273*06c3fb27SDimitry Andric // 3. Body of a loop 274*06c3fb27SDimitry Andric auto CompStmt = compoundStmt(forEach(InnerMatcher)); 275*06c3fb27SDimitry Andric auto IfStmtThen = ifStmt(hasThen(InnerMatcher)); 276*06c3fb27SDimitry Andric auto IfStmtElse = ifStmt(hasElse(InnerMatcher)); 277*06c3fb27SDimitry Andric // FIXME: Handle loop bodies. 278*06c3fb27SDimitry Andric return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse)); 279*06c3fb27SDimitry Andric } 280bdd1243dSDimitry Andric } // namespace clang::ast_matchers 281bdd1243dSDimitry Andric 282bdd1243dSDimitry Andric namespace { 283bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to 284bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs). 285bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>; 286bdd1243dSDimitry Andric 287bdd1243dSDimitry Andric // Convenience typedef. 288bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>; 289bdd1243dSDimitry Andric 290bdd1243dSDimitry Andric // Defined below. 291bdd1243dSDimitry Andric class Strategy; 292bdd1243dSDimitry Andric } // namespace 293bdd1243dSDimitry Andric 294bdd1243dSDimitry Andric namespace { 295bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to 296bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific 297bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object. 298bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand 299bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it. 300bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not 301bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify 302bdd1243dSDimitry Andric /// it warrants a fixit). 303bdd1243dSDimitry Andric class Gadget { 304bdd1243dSDimitry Andric public: 305bdd1243dSDimitry Andric enum class Kind { 306bdd1243dSDimitry Andric #define GADGET(x) x, 307bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 308bdd1243dSDimitry Andric }; 309bdd1243dSDimitry Andric 310bdd1243dSDimitry Andric /// Common type of ASTMatchers used for discovering gadgets. 311bdd1243dSDimitry Andric /// Useful for implementing the static matcher() methods 312bdd1243dSDimitry Andric /// that are expected from all non-abstract subclasses. 313bdd1243dSDimitry Andric using Matcher = decltype(stmt()); 314bdd1243dSDimitry Andric 315bdd1243dSDimitry Andric Gadget(Kind K) : K(K) {} 316bdd1243dSDimitry Andric 317bdd1243dSDimitry Andric Kind getKind() const { return K; } 318bdd1243dSDimitry Andric 319bdd1243dSDimitry Andric virtual bool isWarningGadget() const = 0; 320bdd1243dSDimitry Andric virtual const Stmt *getBaseStmt() const = 0; 321bdd1243dSDimitry Andric 322bdd1243dSDimitry Andric /// Returns the list of pointer-type variables on which this gadget performs 323bdd1243dSDimitry Andric /// its operation. Typically, there's only one variable. This isn't a list 324bdd1243dSDimitry Andric /// of all DeclRefExprs in the gadget's AST! 325bdd1243dSDimitry Andric virtual DeclUseList getClaimedVarUseSites() const = 0; 326bdd1243dSDimitry Andric 327bdd1243dSDimitry Andric virtual ~Gadget() = default; 328bdd1243dSDimitry Andric 329bdd1243dSDimitry Andric private: 330bdd1243dSDimitry Andric Kind K; 331bdd1243dSDimitry Andric }; 332bdd1243dSDimitry Andric 333bdd1243dSDimitry Andric 334bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants 335bdd1243dSDimitry Andric /// an immediate warning. 336bdd1243dSDimitry Andric class WarningGadget : public Gadget { 337bdd1243dSDimitry Andric public: 338bdd1243dSDimitry Andric WarningGadget(Kind K) : Gadget(K) {} 339bdd1243dSDimitry Andric 340bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return G->isWarningGadget(); } 341bdd1243dSDimitry Andric bool isWarningGadget() const final { return true; } 342bdd1243dSDimitry Andric }; 343bdd1243dSDimitry Andric 344bdd1243dSDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but need to be 345bdd1243dSDimitry Andric /// properly recognized in order to emit fixes. For example, if a raw pointer-type 346bdd1243dSDimitry Andric /// variable is replaced by a safe C++ container, every use of such variable must be 347bdd1243dSDimitry Andric /// carefully considered and possibly updated. 348bdd1243dSDimitry Andric class FixableGadget : public Gadget { 349bdd1243dSDimitry Andric public: 350bdd1243dSDimitry Andric FixableGadget(Kind K) : Gadget(K) {} 351bdd1243dSDimitry Andric 352bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return !G->isWarningGadget(); } 353bdd1243dSDimitry Andric bool isWarningGadget() const final { return false; } 354bdd1243dSDimitry Andric 355bdd1243dSDimitry Andric /// Returns a fixit that would fix the current gadget according to 356*06c3fb27SDimitry Andric /// the current strategy. Returns std::nullopt if the fix cannot be produced; 357bdd1243dSDimitry Andric /// returns an empty list if no fixes are necessary. 358bdd1243dSDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &) const { 359bdd1243dSDimitry Andric return std::nullopt; 360bdd1243dSDimitry Andric } 361*06c3fb27SDimitry Andric 362*06c3fb27SDimitry Andric /// Returns a list of two elements where the first element is the LHS of a pointer assignment 363*06c3fb27SDimitry Andric /// statement and the second element is the RHS. This two-element list represents the fact that 364*06c3fb27SDimitry Andric /// the LHS buffer gets its bounds information from the RHS buffer. This information will be used 365*06c3fb27SDimitry Andric /// later to group all those variables whose types must be modified together to prevent type 366*06c3fb27SDimitry Andric /// mismatches. 367*06c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 368*06c3fb27SDimitry Andric getStrategyImplications() const { 369*06c3fb27SDimitry Andric return std::nullopt; 370*06c3fb27SDimitry Andric } 371bdd1243dSDimitry Andric }; 372bdd1243dSDimitry Andric 373bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>; 374bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>; 375bdd1243dSDimitry Andric 376bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer 377bdd1243dSDimitry Andric /// out of bounds. 378bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget { 379bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 380bdd1243dSDimitry Andric const UnaryOperator *Op; 381bdd1243dSDimitry Andric 382bdd1243dSDimitry Andric public: 383bdd1243dSDimitry Andric IncrementGadget(const MatchFinder::MatchResult &Result) 384bdd1243dSDimitry Andric : WarningGadget(Kind::Increment), 385bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 386bdd1243dSDimitry Andric 387bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 388bdd1243dSDimitry Andric return G->getKind() == Kind::Increment; 389bdd1243dSDimitry Andric } 390bdd1243dSDimitry Andric 391bdd1243dSDimitry Andric static Matcher matcher() { 392bdd1243dSDimitry Andric return stmt(unaryOperator( 393bdd1243dSDimitry Andric hasOperatorName("++"), 394bdd1243dSDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType())) 395bdd1243dSDimitry Andric ).bind(OpTag)); 396bdd1243dSDimitry Andric } 397bdd1243dSDimitry Andric 398bdd1243dSDimitry Andric const UnaryOperator *getBaseStmt() const override { return Op; } 399bdd1243dSDimitry Andric 400bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 401bdd1243dSDimitry Andric SmallVector<const DeclRefExpr *, 2> Uses; 402bdd1243dSDimitry Andric if (const auto *DRE = 403bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 404bdd1243dSDimitry Andric Uses.push_back(DRE); 405bdd1243dSDimitry Andric } 406bdd1243dSDimitry Andric 407bdd1243dSDimitry Andric return std::move(Uses); 408bdd1243dSDimitry Andric } 409bdd1243dSDimitry Andric }; 410bdd1243dSDimitry Andric 411bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer 412bdd1243dSDimitry Andric /// out of bounds. 413bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget { 414bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 415bdd1243dSDimitry Andric const UnaryOperator *Op; 416bdd1243dSDimitry Andric 417bdd1243dSDimitry Andric public: 418bdd1243dSDimitry Andric DecrementGadget(const MatchFinder::MatchResult &Result) 419bdd1243dSDimitry Andric : WarningGadget(Kind::Decrement), 420bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 421bdd1243dSDimitry Andric 422bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 423bdd1243dSDimitry Andric return G->getKind() == Kind::Decrement; 424bdd1243dSDimitry Andric } 425bdd1243dSDimitry Andric 426bdd1243dSDimitry Andric static Matcher matcher() { 427bdd1243dSDimitry Andric return stmt(unaryOperator( 428bdd1243dSDimitry Andric hasOperatorName("--"), 429bdd1243dSDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType())) 430bdd1243dSDimitry Andric ).bind(OpTag)); 431bdd1243dSDimitry Andric } 432bdd1243dSDimitry Andric 433bdd1243dSDimitry Andric const UnaryOperator *getBaseStmt() const override { return Op; } 434bdd1243dSDimitry Andric 435bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 436bdd1243dSDimitry Andric if (const auto *DRE = 437bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 438bdd1243dSDimitry Andric return {DRE}; 439bdd1243dSDimitry Andric } 440bdd1243dSDimitry Andric 441bdd1243dSDimitry Andric return {}; 442bdd1243dSDimitry Andric } 443bdd1243dSDimitry Andric }; 444bdd1243dSDimitry Andric 445bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as 446bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array. 447bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget { 448*06c3fb27SDimitry Andric static constexpr const char *const ArraySubscrTag = "ArraySubscript"; 449bdd1243dSDimitry Andric const ArraySubscriptExpr *ASE; 450bdd1243dSDimitry Andric 451bdd1243dSDimitry Andric public: 452bdd1243dSDimitry Andric ArraySubscriptGadget(const MatchFinder::MatchResult &Result) 453bdd1243dSDimitry Andric : WarningGadget(Kind::ArraySubscript), 454bdd1243dSDimitry Andric ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {} 455bdd1243dSDimitry Andric 456bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 457bdd1243dSDimitry Andric return G->getKind() == Kind::ArraySubscript; 458bdd1243dSDimitry Andric } 459bdd1243dSDimitry Andric 460bdd1243dSDimitry Andric static Matcher matcher() { 461bdd1243dSDimitry Andric // FIXME: What if the index is integer literal 0? Should this be 462bdd1243dSDimitry Andric // a safe gadget in this case? 463bdd1243dSDimitry Andric // clang-format off 464bdd1243dSDimitry Andric return stmt(arraySubscriptExpr( 465bdd1243dSDimitry Andric hasBase(ignoringParenImpCasts( 466bdd1243dSDimitry Andric anyOf(hasPointerType(), hasArrayType()))), 467*06c3fb27SDimitry Andric unless(hasIndex( 468*06c3fb27SDimitry Andric anyOf(integerLiteral(equals(0)), arrayInitIndexExpr()) 469*06c3fb27SDimitry Andric ))) 470bdd1243dSDimitry Andric .bind(ArraySubscrTag)); 471bdd1243dSDimitry Andric // clang-format on 472bdd1243dSDimitry Andric } 473bdd1243dSDimitry Andric 474bdd1243dSDimitry Andric const ArraySubscriptExpr *getBaseStmt() const override { return ASE; } 475bdd1243dSDimitry Andric 476bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 477bdd1243dSDimitry Andric if (const auto *DRE = 478bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) { 479bdd1243dSDimitry Andric return {DRE}; 480bdd1243dSDimitry Andric } 481bdd1243dSDimitry Andric 482bdd1243dSDimitry Andric return {}; 483bdd1243dSDimitry Andric } 484bdd1243dSDimitry Andric }; 485bdd1243dSDimitry Andric 486bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms: 487bdd1243dSDimitry Andric /// \code 488bdd1243dSDimitry Andric /// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n 489bdd1243dSDimitry Andric /// \endcode 490bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget { 491bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticTag = "ptrAdd"; 492bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr"; 493bdd1243dSDimitry Andric const BinaryOperator *PA; // pointer arithmetic expression 494bdd1243dSDimitry Andric const Expr *Ptr; // the pointer expression in `PA` 495bdd1243dSDimitry Andric 496bdd1243dSDimitry Andric public: 497bdd1243dSDimitry Andric PointerArithmeticGadget(const MatchFinder::MatchResult &Result) 498bdd1243dSDimitry Andric : WarningGadget(Kind::PointerArithmetic), 499bdd1243dSDimitry Andric PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)), 500bdd1243dSDimitry Andric Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {} 501bdd1243dSDimitry Andric 502bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 503bdd1243dSDimitry Andric return G->getKind() == Kind::PointerArithmetic; 504bdd1243dSDimitry Andric } 505bdd1243dSDimitry Andric 506bdd1243dSDimitry Andric static Matcher matcher() { 507*06c3fb27SDimitry Andric auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType())); 508*06c3fb27SDimitry Andric auto PtrAtRight = 509*06c3fb27SDimitry Andric allOf(hasOperatorName("+"), 510bdd1243dSDimitry Andric hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 511bdd1243dSDimitry Andric hasLHS(HasIntegerType)); 512*06c3fb27SDimitry Andric auto PtrAtLeft = 513*06c3fb27SDimitry Andric allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"), 514bdd1243dSDimitry Andric hasOperatorName("+="), hasOperatorName("-=")), 515bdd1243dSDimitry Andric hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 516bdd1243dSDimitry Andric hasRHS(HasIntegerType)); 517bdd1243dSDimitry Andric 518*06c3fb27SDimitry Andric return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight)) 519*06c3fb27SDimitry Andric .bind(PointerArithmeticTag)); 520bdd1243dSDimitry Andric } 521bdd1243dSDimitry Andric 522bdd1243dSDimitry Andric const Stmt *getBaseStmt() const override { return PA; } 523bdd1243dSDimitry Andric 524bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 525*06c3fb27SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) { 526bdd1243dSDimitry Andric return {DRE}; 527bdd1243dSDimitry Andric } 528bdd1243dSDimitry Andric 529bdd1243dSDimitry Andric return {}; 530bdd1243dSDimitry Andric } 531bdd1243dSDimitry Andric // FIXME: pointer adding zero should be fine 532bdd1243dSDimitry Andric // FIXME: this gadge will need a fix-it 533bdd1243dSDimitry Andric }; 534*06c3fb27SDimitry Andric 535*06c3fb27SDimitry Andric /// A pointer initialization expression of the form: 536*06c3fb27SDimitry Andric /// \code 537*06c3fb27SDimitry Andric /// int *p = q; 538*06c3fb27SDimitry Andric /// \endcode 539*06c3fb27SDimitry Andric class PointerInitGadget : public FixableGadget { 540*06c3fb27SDimitry Andric private: 541*06c3fb27SDimitry Andric static constexpr const char *const PointerInitLHSTag = "ptrInitLHS"; 542*06c3fb27SDimitry Andric static constexpr const char *const PointerInitRHSTag = "ptrInitRHS"; 543*06c3fb27SDimitry Andric const VarDecl * PtrInitLHS; // the LHS pointer expression in `PI` 544*06c3fb27SDimitry Andric const DeclRefExpr * PtrInitRHS; // the RHS pointer expression in `PI` 545*06c3fb27SDimitry Andric 546*06c3fb27SDimitry Andric public: 547*06c3fb27SDimitry Andric PointerInitGadget(const MatchFinder::MatchResult &Result) 548*06c3fb27SDimitry Andric : FixableGadget(Kind::PointerInit), 549*06c3fb27SDimitry Andric PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)), 550*06c3fb27SDimitry Andric PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {} 551*06c3fb27SDimitry Andric 552*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 553*06c3fb27SDimitry Andric return G->getKind() == Kind::PointerInit; 554*06c3fb27SDimitry Andric } 555*06c3fb27SDimitry Andric 556*06c3fb27SDimitry Andric static Matcher matcher() { 557*06c3fb27SDimitry Andric auto PtrInitStmt = declStmt(hasSingleDecl(varDecl( 558*06c3fb27SDimitry Andric hasInitializer(ignoringImpCasts(declRefExpr( 559*06c3fb27SDimitry Andric hasPointerType()). 560*06c3fb27SDimitry Andric bind(PointerInitRHSTag)))). 561*06c3fb27SDimitry Andric bind(PointerInitLHSTag))); 562*06c3fb27SDimitry Andric 563*06c3fb27SDimitry Andric return stmt(PtrInitStmt); 564*06c3fb27SDimitry Andric } 565*06c3fb27SDimitry Andric 566*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 567*06c3fb27SDimitry Andric 568*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return nullptr; } 569*06c3fb27SDimitry Andric 570*06c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 571*06c3fb27SDimitry Andric return DeclUseList{PtrInitRHS}; 572*06c3fb27SDimitry Andric } 573*06c3fb27SDimitry Andric 574*06c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 575*06c3fb27SDimitry Andric getStrategyImplications() const override { 576*06c3fb27SDimitry Andric return std::make_pair(PtrInitLHS, 577*06c3fb27SDimitry Andric cast<VarDecl>(PtrInitRHS->getDecl())); 578*06c3fb27SDimitry Andric } 579*06c3fb27SDimitry Andric }; 580*06c3fb27SDimitry Andric 581*06c3fb27SDimitry Andric /// A pointer assignment expression of the form: 582*06c3fb27SDimitry Andric /// \code 583*06c3fb27SDimitry Andric /// p = q; 584*06c3fb27SDimitry Andric /// \endcode 585*06c3fb27SDimitry Andric class PointerAssignmentGadget : public FixableGadget { 586*06c3fb27SDimitry Andric private: 587*06c3fb27SDimitry Andric static constexpr const char *const PointerAssignLHSTag = "ptrLHS"; 588*06c3fb27SDimitry Andric static constexpr const char *const PointerAssignRHSTag = "ptrRHS"; 589*06c3fb27SDimitry Andric const DeclRefExpr * PtrLHS; // the LHS pointer expression in `PA` 590*06c3fb27SDimitry Andric const DeclRefExpr * PtrRHS; // the RHS pointer expression in `PA` 591*06c3fb27SDimitry Andric 592*06c3fb27SDimitry Andric public: 593*06c3fb27SDimitry Andric PointerAssignmentGadget(const MatchFinder::MatchResult &Result) 594*06c3fb27SDimitry Andric : FixableGadget(Kind::PointerAssignment), 595*06c3fb27SDimitry Andric PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)), 596*06c3fb27SDimitry Andric PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {} 597*06c3fb27SDimitry Andric 598*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 599*06c3fb27SDimitry Andric return G->getKind() == Kind::PointerAssignment; 600*06c3fb27SDimitry Andric } 601*06c3fb27SDimitry Andric 602*06c3fb27SDimitry Andric static Matcher matcher() { 603*06c3fb27SDimitry Andric auto PtrAssignExpr = binaryOperator(allOf(hasOperatorName("="), 604*06c3fb27SDimitry Andric hasRHS(ignoringParenImpCasts(declRefExpr(hasPointerType(), 605*06c3fb27SDimitry Andric to(varDecl())). 606*06c3fb27SDimitry Andric bind(PointerAssignRHSTag))), 607*06c3fb27SDimitry Andric hasLHS(declRefExpr(hasPointerType(), 608*06c3fb27SDimitry Andric to(varDecl())). 609*06c3fb27SDimitry Andric bind(PointerAssignLHSTag)))); 610*06c3fb27SDimitry Andric 611*06c3fb27SDimitry Andric return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr)); 612*06c3fb27SDimitry Andric } 613*06c3fb27SDimitry Andric 614*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 615*06c3fb27SDimitry Andric 616*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return nullptr; } 617*06c3fb27SDimitry Andric 618*06c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 619*06c3fb27SDimitry Andric return DeclUseList{PtrLHS, PtrRHS}; 620*06c3fb27SDimitry Andric } 621*06c3fb27SDimitry Andric 622*06c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 623*06c3fb27SDimitry Andric getStrategyImplications() const override { 624*06c3fb27SDimitry Andric return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()), 625*06c3fb27SDimitry Andric cast<VarDecl>(PtrRHS->getDecl())); 626*06c3fb27SDimitry Andric } 627*06c3fb27SDimitry Andric }; 628*06c3fb27SDimitry Andric 629*06c3fb27SDimitry Andric /// A call of a function or method that performs unchecked buffer operations 630*06c3fb27SDimitry Andric /// over one of its pointer parameters. 631*06c3fb27SDimitry Andric class UnsafeBufferUsageAttrGadget : public WarningGadget { 632*06c3fb27SDimitry Andric constexpr static const char *const OpTag = "call_expr"; 633*06c3fb27SDimitry Andric const CallExpr *Op; 634*06c3fb27SDimitry Andric 635*06c3fb27SDimitry Andric public: 636*06c3fb27SDimitry Andric UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result) 637*06c3fb27SDimitry Andric : WarningGadget(Kind::UnsafeBufferUsageAttr), 638*06c3fb27SDimitry Andric Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {} 639*06c3fb27SDimitry Andric 640*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 641*06c3fb27SDimitry Andric return G->getKind() == Kind::UnsafeBufferUsageAttr; 642*06c3fb27SDimitry Andric } 643*06c3fb27SDimitry Andric 644*06c3fb27SDimitry Andric static Matcher matcher() { 645*06c3fb27SDimitry Andric return stmt(callExpr(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)))) 646*06c3fb27SDimitry Andric .bind(OpTag)); 647*06c3fb27SDimitry Andric } 648*06c3fb27SDimitry Andric const Stmt *getBaseStmt() const override { return Op; } 649*06c3fb27SDimitry Andric 650*06c3fb27SDimitry Andric DeclUseList getClaimedVarUseSites() const override { return {}; } 651*06c3fb27SDimitry Andric }; 652*06c3fb27SDimitry Andric 653*06c3fb27SDimitry Andric // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue 654*06c3fb27SDimitry Andric // Context (see `isInUnspecifiedLvalueContext`). 655*06c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator. 656*06c3fb27SDimitry Andric class ULCArraySubscriptGadget : public FixableGadget { 657*06c3fb27SDimitry Andric private: 658*06c3fb27SDimitry Andric static constexpr const char *const ULCArraySubscriptTag = 659*06c3fb27SDimitry Andric "ArraySubscriptUnderULC"; 660*06c3fb27SDimitry Andric const ArraySubscriptExpr *Node; 661*06c3fb27SDimitry Andric 662*06c3fb27SDimitry Andric public: 663*06c3fb27SDimitry Andric ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result) 664*06c3fb27SDimitry Andric : FixableGadget(Kind::ULCArraySubscript), 665*06c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) { 666*06c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 667*06c3fb27SDimitry Andric } 668*06c3fb27SDimitry Andric 669*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 670*06c3fb27SDimitry Andric return G->getKind() == Kind::ULCArraySubscript; 671*06c3fb27SDimitry Andric } 672*06c3fb27SDimitry Andric 673*06c3fb27SDimitry Andric static Matcher matcher() { 674*06c3fb27SDimitry Andric auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 675*06c3fb27SDimitry Andric auto BaseIsArrayOrPtrDRE = 676*06c3fb27SDimitry Andric hasBase(ignoringParenImpCasts(declRefExpr(ArrayOrPtr))); 677*06c3fb27SDimitry Andric auto Target = 678*06c3fb27SDimitry Andric arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag); 679*06c3fb27SDimitry Andric 680*06c3fb27SDimitry Andric return expr(isInUnspecifiedLvalueContext(Target)); 681*06c3fb27SDimitry Andric } 682*06c3fb27SDimitry Andric 683*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 684*06c3fb27SDimitry Andric 685*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 686*06c3fb27SDimitry Andric 687*06c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 688*06c3fb27SDimitry Andric if (const auto *DRE = 689*06c3fb27SDimitry Andric dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) { 690*06c3fb27SDimitry Andric return {DRE}; 691*06c3fb27SDimitry Andric } 692*06c3fb27SDimitry Andric return {}; 693*06c3fb27SDimitry Andric } 694*06c3fb27SDimitry Andric }; 695*06c3fb27SDimitry Andric 696*06c3fb27SDimitry Andric // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the 697*06c3fb27SDimitry Andric // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits 698*06c3fb27SDimitry Andric // fixit of the form `UPC(DRE.data())`. 699*06c3fb27SDimitry Andric class UPCStandalonePointerGadget : public FixableGadget { 700*06c3fb27SDimitry Andric private: 701*06c3fb27SDimitry Andric static constexpr const char *const DeclRefExprTag = "StandalonePointer"; 702*06c3fb27SDimitry Andric const DeclRefExpr *Node; 703*06c3fb27SDimitry Andric 704*06c3fb27SDimitry Andric public: 705*06c3fb27SDimitry Andric UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result) 706*06c3fb27SDimitry Andric : FixableGadget(Kind::UPCStandalonePointer), 707*06c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) { 708*06c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 709*06c3fb27SDimitry Andric } 710*06c3fb27SDimitry Andric 711*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 712*06c3fb27SDimitry Andric return G->getKind() == Kind::UPCStandalonePointer; 713*06c3fb27SDimitry Andric } 714*06c3fb27SDimitry Andric 715*06c3fb27SDimitry Andric static Matcher matcher() { 716*06c3fb27SDimitry Andric auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 717*06c3fb27SDimitry Andric auto target = expr( 718*06c3fb27SDimitry Andric ignoringParenImpCasts(declRefExpr(allOf(ArrayOrPtr, to(varDecl()))).bind(DeclRefExprTag))); 719*06c3fb27SDimitry Andric return stmt(isInUnspecifiedPointerContext(target)); 720*06c3fb27SDimitry Andric } 721*06c3fb27SDimitry Andric 722*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 723*06c3fb27SDimitry Andric 724*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 725*06c3fb27SDimitry Andric 726*06c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 727*06c3fb27SDimitry Andric return {Node}; 728*06c3fb27SDimitry Andric } 729*06c3fb27SDimitry Andric }; 730*06c3fb27SDimitry Andric 731*06c3fb27SDimitry Andric class PointerDereferenceGadget : public FixableGadget { 732*06c3fb27SDimitry Andric static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 733*06c3fb27SDimitry Andric static constexpr const char *const OperatorTag = "op"; 734*06c3fb27SDimitry Andric 735*06c3fb27SDimitry Andric const DeclRefExpr *BaseDeclRefExpr = nullptr; 736*06c3fb27SDimitry Andric const UnaryOperator *Op = nullptr; 737*06c3fb27SDimitry Andric 738*06c3fb27SDimitry Andric public: 739*06c3fb27SDimitry Andric PointerDereferenceGadget(const MatchFinder::MatchResult &Result) 740*06c3fb27SDimitry Andric : FixableGadget(Kind::PointerDereference), 741*06c3fb27SDimitry Andric BaseDeclRefExpr( 742*06c3fb27SDimitry Andric Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 743*06c3fb27SDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {} 744*06c3fb27SDimitry Andric 745*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 746*06c3fb27SDimitry Andric return G->getKind() == Kind::PointerDereference; 747*06c3fb27SDimitry Andric } 748*06c3fb27SDimitry Andric 749*06c3fb27SDimitry Andric static Matcher matcher() { 750*06c3fb27SDimitry Andric auto Target = 751*06c3fb27SDimitry Andric unaryOperator( 752*06c3fb27SDimitry Andric hasOperatorName("*"), 753*06c3fb27SDimitry Andric has(expr(ignoringParenImpCasts( 754*06c3fb27SDimitry Andric declRefExpr(to(varDecl())).bind(BaseDeclRefExprTag))))) 755*06c3fb27SDimitry Andric .bind(OperatorTag); 756*06c3fb27SDimitry Andric 757*06c3fb27SDimitry Andric return expr(isInUnspecifiedLvalueContext(Target)); 758*06c3fb27SDimitry Andric } 759*06c3fb27SDimitry Andric 760*06c3fb27SDimitry Andric DeclUseList getClaimedVarUseSites() const override { 761*06c3fb27SDimitry Andric return {BaseDeclRefExpr}; 762*06c3fb27SDimitry Andric } 763*06c3fb27SDimitry Andric 764*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const final { return Op; } 765*06c3fb27SDimitry Andric 766*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 767*06c3fb27SDimitry Andric }; 768*06c3fb27SDimitry Andric 769*06c3fb27SDimitry Andric // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer 770*06c3fb27SDimitry Andric // Context (see `isInUnspecifiedPointerContext`). 771*06c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator. 772*06c3fb27SDimitry Andric class UPCAddressofArraySubscriptGadget : public FixableGadget { 773*06c3fb27SDimitry Andric private: 774*06c3fb27SDimitry Andric static constexpr const char *const UPCAddressofArraySubscriptTag = 775*06c3fb27SDimitry Andric "AddressofArraySubscriptUnderUPC"; 776*06c3fb27SDimitry Andric const UnaryOperator *Node; // the `&DRE[any]` node 777*06c3fb27SDimitry Andric 778*06c3fb27SDimitry Andric public: 779*06c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result) 780*06c3fb27SDimitry Andric : FixableGadget(Kind::ULCArraySubscript), 781*06c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<UnaryOperator>( 782*06c3fb27SDimitry Andric UPCAddressofArraySubscriptTag)) { 783*06c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 784*06c3fb27SDimitry Andric } 785*06c3fb27SDimitry Andric 786*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 787*06c3fb27SDimitry Andric return G->getKind() == Kind::UPCAddressofArraySubscript; 788*06c3fb27SDimitry Andric } 789*06c3fb27SDimitry Andric 790*06c3fb27SDimitry Andric static Matcher matcher() { 791*06c3fb27SDimitry Andric return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 792*06c3fb27SDimitry Andric unaryOperator(hasOperatorName("&"), 793*06c3fb27SDimitry Andric hasUnaryOperand(arraySubscriptExpr( 794*06c3fb27SDimitry Andric hasBase(ignoringParenImpCasts(declRefExpr()))))) 795*06c3fb27SDimitry Andric .bind(UPCAddressofArraySubscriptTag))))); 796*06c3fb27SDimitry Andric } 797*06c3fb27SDimitry Andric 798*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &) const override; 799*06c3fb27SDimitry Andric 800*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 801*06c3fb27SDimitry Andric 802*06c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 803*06c3fb27SDimitry Andric const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr()); 804*06c3fb27SDimitry Andric const auto *DRE = 805*06c3fb27SDimitry Andric cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreImpCasts()); 806*06c3fb27SDimitry Andric return {DRE}; 807*06c3fb27SDimitry Andric } 808*06c3fb27SDimitry Andric }; 809bdd1243dSDimitry Andric } // namespace 810bdd1243dSDimitry Andric 811bdd1243dSDimitry Andric namespace { 812bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect 813*06c3fb27SDimitry Andric // declarations to its uses and make sure we've covered all uses with our 814*06c3fb27SDimitry Andric // analysis before we try to fix the declaration. 815bdd1243dSDimitry Andric class DeclUseTracker { 816bdd1243dSDimitry Andric using UseSetTy = SmallSet<const DeclRefExpr *, 16>; 817bdd1243dSDimitry Andric using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>; 818bdd1243dSDimitry Andric 819bdd1243dSDimitry Andric // Allocate on the heap for easier move. 820bdd1243dSDimitry Andric std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()}; 821bdd1243dSDimitry Andric DefMapTy Defs{}; 822bdd1243dSDimitry Andric 823bdd1243dSDimitry Andric public: 824bdd1243dSDimitry Andric DeclUseTracker() = default; 825bdd1243dSDimitry Andric DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies. 826*06c3fb27SDimitry Andric DeclUseTracker &operator=(const DeclUseTracker &) = delete; 827bdd1243dSDimitry Andric DeclUseTracker(DeclUseTracker &&) = default; 828bdd1243dSDimitry Andric DeclUseTracker &operator=(DeclUseTracker &&) = default; 829bdd1243dSDimitry Andric 830bdd1243dSDimitry Andric // Start tracking a freshly discovered DRE. 831bdd1243dSDimitry Andric void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); } 832bdd1243dSDimitry Andric 833bdd1243dSDimitry Andric // Stop tracking the DRE as it's been fully figured out. 834bdd1243dSDimitry Andric void claimUse(const DeclRefExpr *DRE) { 835bdd1243dSDimitry Andric assert(Uses->count(DRE) && 836bdd1243dSDimitry Andric "DRE not found or claimed by multiple matchers!"); 837bdd1243dSDimitry Andric Uses->erase(DRE); 838bdd1243dSDimitry Andric } 839bdd1243dSDimitry Andric 840bdd1243dSDimitry Andric // A variable is unclaimed if at least one use is unclaimed. 841bdd1243dSDimitry Andric bool hasUnclaimedUses(const VarDecl *VD) const { 842bdd1243dSDimitry Andric // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs? 843bdd1243dSDimitry Andric return any_of(*Uses, [VD](const DeclRefExpr *DRE) { 844bdd1243dSDimitry Andric return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl(); 845bdd1243dSDimitry Andric }); 846bdd1243dSDimitry Andric } 847bdd1243dSDimitry Andric 848bdd1243dSDimitry Andric void discoverDecl(const DeclStmt *DS) { 849bdd1243dSDimitry Andric for (const Decl *D : DS->decls()) { 850bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(D)) { 851bdd1243dSDimitry Andric // FIXME: Assertion temporarily disabled due to a bug in 852bdd1243dSDimitry Andric // ASTMatcher internal behavior in presence of GNU 853bdd1243dSDimitry Andric // statement-expressions. We need to properly investigate this 854bdd1243dSDimitry Andric // because it can screw up our algorithm in other ways. 855bdd1243dSDimitry Andric // assert(Defs.count(VD) == 0 && "Definition already discovered!"); 856bdd1243dSDimitry Andric Defs[VD] = DS; 857bdd1243dSDimitry Andric } 858bdd1243dSDimitry Andric } 859bdd1243dSDimitry Andric } 860bdd1243dSDimitry Andric 861bdd1243dSDimitry Andric const DeclStmt *lookupDecl(const VarDecl *VD) const { 862bdd1243dSDimitry Andric auto It = Defs.find(VD); 863bdd1243dSDimitry Andric assert(It != Defs.end() && "Definition never discovered!"); 864bdd1243dSDimitry Andric return It->second; 865bdd1243dSDimitry Andric } 866bdd1243dSDimitry Andric }; 867bdd1243dSDimitry Andric } // namespace 868bdd1243dSDimitry Andric 869bdd1243dSDimitry Andric namespace { 870bdd1243dSDimitry Andric // Strategy is a map from variables to the way we plan to emit fixes for 871bdd1243dSDimitry Andric // these variables. It is figured out gradually by trying different fixes 872bdd1243dSDimitry Andric // for different variables depending on gadgets in which these variables 873bdd1243dSDimitry Andric // participate. 874bdd1243dSDimitry Andric class Strategy { 875bdd1243dSDimitry Andric public: 876bdd1243dSDimitry Andric enum class Kind { 877bdd1243dSDimitry Andric Wontfix, // We don't plan to emit a fixit for this variable. 878bdd1243dSDimitry Andric Span, // We recommend replacing the variable with std::span. 879bdd1243dSDimitry Andric Iterator, // We recommend replacing the variable with std::span::iterator. 880bdd1243dSDimitry Andric Array, // We recommend replacing the variable with std::array. 881bdd1243dSDimitry Andric Vector // We recommend replacing the variable with std::vector. 882bdd1243dSDimitry Andric }; 883bdd1243dSDimitry Andric 884bdd1243dSDimitry Andric private: 885bdd1243dSDimitry Andric using MapTy = llvm::DenseMap<const VarDecl *, Kind>; 886bdd1243dSDimitry Andric 887bdd1243dSDimitry Andric MapTy Map; 888bdd1243dSDimitry Andric 889bdd1243dSDimitry Andric public: 890bdd1243dSDimitry Andric Strategy() = default; 891bdd1243dSDimitry Andric Strategy(const Strategy &) = delete; // Let's avoid copies. 892*06c3fb27SDimitry Andric Strategy &operator=(const Strategy &) = delete; 893bdd1243dSDimitry Andric Strategy(Strategy &&) = default; 894*06c3fb27SDimitry Andric Strategy &operator=(Strategy &&) = default; 895bdd1243dSDimitry Andric 896*06c3fb27SDimitry Andric void set(const VarDecl *VD, Kind K) { Map[VD] = K; } 897bdd1243dSDimitry Andric 898bdd1243dSDimitry Andric Kind lookup(const VarDecl *VD) const { 899bdd1243dSDimitry Andric auto I = Map.find(VD); 900bdd1243dSDimitry Andric if (I == Map.end()) 901bdd1243dSDimitry Andric return Kind::Wontfix; 902bdd1243dSDimitry Andric 903bdd1243dSDimitry Andric return I->second; 904bdd1243dSDimitry Andric } 905bdd1243dSDimitry Andric }; 906bdd1243dSDimitry Andric } // namespace 907bdd1243dSDimitry Andric 908*06c3fb27SDimitry Andric 909*06c3fb27SDimitry Andric // Representing a pointer type expression of the form `++Ptr` in an Unspecified 910*06c3fb27SDimitry Andric // Pointer Context (UPC): 911*06c3fb27SDimitry Andric class UPCPreIncrementGadget : public FixableGadget { 912*06c3fb27SDimitry Andric private: 913*06c3fb27SDimitry Andric static constexpr const char *const UPCPreIncrementTag = 914*06c3fb27SDimitry Andric "PointerPreIncrementUnderUPC"; 915*06c3fb27SDimitry Andric const UnaryOperator *Node; // the `++Ptr` node 916*06c3fb27SDimitry Andric 917*06c3fb27SDimitry Andric public: 918*06c3fb27SDimitry Andric UPCPreIncrementGadget(const MatchFinder::MatchResult &Result) 919*06c3fb27SDimitry Andric : FixableGadget(Kind::UPCPreIncrement), 920*06c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) { 921*06c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 922*06c3fb27SDimitry Andric } 923*06c3fb27SDimitry Andric 924*06c3fb27SDimitry Andric static bool classof(const Gadget *G) { 925*06c3fb27SDimitry Andric return G->getKind() == Kind::UPCPreIncrement; 926*06c3fb27SDimitry Andric } 927*06c3fb27SDimitry Andric 928*06c3fb27SDimitry Andric static Matcher matcher() { 929*06c3fb27SDimitry Andric // Note here we match `++Ptr` for any expression `Ptr` of pointer type. 930*06c3fb27SDimitry Andric // Although currently we can only provide fix-its when `Ptr` is a DRE, we 931*06c3fb27SDimitry Andric // can have the matcher be general, so long as `getClaimedVarUseSites` does 932*06c3fb27SDimitry Andric // things right. 933*06c3fb27SDimitry Andric return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 934*06c3fb27SDimitry Andric unaryOperator(isPreInc(), 935*06c3fb27SDimitry Andric hasUnaryOperand(declRefExpr()) 936*06c3fb27SDimitry Andric ).bind(UPCPreIncrementTag))))); 937*06c3fb27SDimitry Andric } 938*06c3fb27SDimitry Andric 939*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &S) const override; 940*06c3fb27SDimitry Andric 941*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const override { return Node; } 942*06c3fb27SDimitry Andric 943*06c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 944*06c3fb27SDimitry Andric return {dyn_cast<DeclRefExpr>(Node->getSubExpr())}; 945*06c3fb27SDimitry Andric } 946*06c3fb27SDimitry Andric }; 947*06c3fb27SDimitry Andric 948*06c3fb27SDimitry Andric // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 + 949*06c3fb27SDimitry Andric // ptr)`: 950*06c3fb27SDimitry Andric class DerefSimplePtrArithFixableGadget : public FixableGadget { 951*06c3fb27SDimitry Andric static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 952*06c3fb27SDimitry Andric static constexpr const char *const DerefOpTag = "DerefOp"; 953*06c3fb27SDimitry Andric static constexpr const char *const AddOpTag = "AddOp"; 954*06c3fb27SDimitry Andric static constexpr const char *const OffsetTag = "Offset"; 955*06c3fb27SDimitry Andric 956*06c3fb27SDimitry Andric const DeclRefExpr *BaseDeclRefExpr = nullptr; 957*06c3fb27SDimitry Andric const UnaryOperator *DerefOp = nullptr; 958*06c3fb27SDimitry Andric const BinaryOperator *AddOp = nullptr; 959*06c3fb27SDimitry Andric const IntegerLiteral *Offset = nullptr; 960*06c3fb27SDimitry Andric 961*06c3fb27SDimitry Andric public: 962*06c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result) 963*06c3fb27SDimitry Andric : FixableGadget(Kind::DerefSimplePtrArithFixable), 964*06c3fb27SDimitry Andric BaseDeclRefExpr( 965*06c3fb27SDimitry Andric Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 966*06c3fb27SDimitry Andric DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)), 967*06c3fb27SDimitry Andric AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)), 968*06c3fb27SDimitry Andric Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {} 969*06c3fb27SDimitry Andric 970*06c3fb27SDimitry Andric static Matcher matcher() { 971*06c3fb27SDimitry Andric // clang-format off 972*06c3fb27SDimitry Andric auto ThePtr = expr(hasPointerType(), 973*06c3fb27SDimitry Andric ignoringImpCasts(declRefExpr(to(varDecl())).bind(BaseDeclRefExprTag))); 974*06c3fb27SDimitry Andric auto PlusOverPtrAndInteger = expr(anyOf( 975*06c3fb27SDimitry Andric binaryOperator(hasOperatorName("+"), hasLHS(ThePtr), 976*06c3fb27SDimitry Andric hasRHS(integerLiteral().bind(OffsetTag))) 977*06c3fb27SDimitry Andric .bind(AddOpTag), 978*06c3fb27SDimitry Andric binaryOperator(hasOperatorName("+"), hasRHS(ThePtr), 979*06c3fb27SDimitry Andric hasLHS(integerLiteral().bind(OffsetTag))) 980*06c3fb27SDimitry Andric .bind(AddOpTag))); 981*06c3fb27SDimitry Andric return isInUnspecifiedLvalueContext(unaryOperator( 982*06c3fb27SDimitry Andric hasOperatorName("*"), 983*06c3fb27SDimitry Andric hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger))) 984*06c3fb27SDimitry Andric .bind(DerefOpTag)); 985*06c3fb27SDimitry Andric // clang-format on 986*06c3fb27SDimitry Andric } 987*06c3fb27SDimitry Andric 988*06c3fb27SDimitry Andric virtual std::optional<FixItList> getFixits(const Strategy &s) const final; 989*06c3fb27SDimitry Andric 990*06c3fb27SDimitry Andric // TODO remove this method from FixableGadget interface 991*06c3fb27SDimitry Andric virtual const Stmt *getBaseStmt() const final { return nullptr; } 992*06c3fb27SDimitry Andric 993*06c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const final { 994*06c3fb27SDimitry Andric return {BaseDeclRefExpr}; 995*06c3fb27SDimitry Andric } 996*06c3fb27SDimitry Andric }; 997*06c3fb27SDimitry Andric 998bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits. 999*06c3fb27SDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker> 1000*06c3fb27SDimitry Andric findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler, 1001*06c3fb27SDimitry Andric bool EmitSuggestions) { 1002bdd1243dSDimitry Andric 1003bdd1243dSDimitry Andric struct GadgetFinderCallback : MatchFinder::MatchCallback { 1004bdd1243dSDimitry Andric FixableGadgetList FixableGadgets; 1005bdd1243dSDimitry Andric WarningGadgetList WarningGadgets; 1006bdd1243dSDimitry Andric DeclUseTracker Tracker; 1007bdd1243dSDimitry Andric 1008bdd1243dSDimitry Andric void run(const MatchFinder::MatchResult &Result) override { 1009bdd1243dSDimitry Andric // In debug mode, assert that we've found exactly one gadget. 1010bdd1243dSDimitry Andric // This helps us avoid conflicts in .bind() tags. 1011bdd1243dSDimitry Andric #if NDEBUG 1012bdd1243dSDimitry Andric #define NEXT return 1013bdd1243dSDimitry Andric #else 1014bdd1243dSDimitry Andric [[maybe_unused]] int numFound = 0; 1015bdd1243dSDimitry Andric #define NEXT ++numFound 1016bdd1243dSDimitry Andric #endif 1017bdd1243dSDimitry Andric 1018bdd1243dSDimitry Andric if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) { 1019bdd1243dSDimitry Andric Tracker.discoverUse(DRE); 1020bdd1243dSDimitry Andric NEXT; 1021bdd1243dSDimitry Andric } 1022bdd1243dSDimitry Andric 1023bdd1243dSDimitry Andric if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) { 1024bdd1243dSDimitry Andric Tracker.discoverDecl(DS); 1025bdd1243dSDimitry Andric NEXT; 1026bdd1243dSDimitry Andric } 1027bdd1243dSDimitry Andric 1028bdd1243dSDimitry Andric // Figure out which matcher we've found, and call the appropriate 1029bdd1243dSDimitry Andric // subclass constructor. 1030bdd1243dSDimitry Andric // FIXME: Can we do this more logarithmically? 1031bdd1243dSDimitry Andric #define FIXABLE_GADGET(name) \ 1032bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1033bdd1243dSDimitry Andric FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1034bdd1243dSDimitry Andric NEXT; \ 1035bdd1243dSDimitry Andric } 1036bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1037bdd1243dSDimitry Andric #define WARNING_GADGET(name) \ 1038bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1039bdd1243dSDimitry Andric WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1040bdd1243dSDimitry Andric NEXT; \ 1041bdd1243dSDimitry Andric } 1042bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1043bdd1243dSDimitry Andric 1044bdd1243dSDimitry Andric assert(numFound >= 1 && "Gadgets not found in match result!"); 1045bdd1243dSDimitry Andric assert(numFound <= 1 && "Conflicting bind tags in gadgets!"); 1046bdd1243dSDimitry Andric } 1047bdd1243dSDimitry Andric }; 1048bdd1243dSDimitry Andric 1049bdd1243dSDimitry Andric MatchFinder M; 1050bdd1243dSDimitry Andric GadgetFinderCallback CB; 1051bdd1243dSDimitry Andric 1052bdd1243dSDimitry Andric // clang-format off 1053bdd1243dSDimitry Andric M.addMatcher( 1054*06c3fb27SDimitry Andric stmt( 1055*06c3fb27SDimitry Andric forEachDescendantEvaluatedStmt(stmt(anyOf( 1056bdd1243dSDimitry Andric // Add Gadget::matcher() for every gadget in the registry. 1057*06c3fb27SDimitry Andric #define WARNING_GADGET(x) \ 1058*06c3fb27SDimitry Andric allOf(x ## Gadget::matcher().bind(#x), \ 1059*06c3fb27SDimitry Andric notInSafeBufferOptOut(&Handler)), 1060*06c3fb27SDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1061*06c3fb27SDimitry Andric // Avoid a hanging comma. 1062*06c3fb27SDimitry Andric unless(stmt()) 1063*06c3fb27SDimitry Andric ))) 1064*06c3fb27SDimitry Andric ), 1065*06c3fb27SDimitry Andric &CB 1066*06c3fb27SDimitry Andric ); 1067*06c3fb27SDimitry Andric // clang-format on 1068*06c3fb27SDimitry Andric 1069*06c3fb27SDimitry Andric if (EmitSuggestions) { 1070*06c3fb27SDimitry Andric // clang-format off 1071*06c3fb27SDimitry Andric M.addMatcher( 1072*06c3fb27SDimitry Andric stmt( 1073*06c3fb27SDimitry Andric forEachDescendantStmt(stmt(eachOf( 1074*06c3fb27SDimitry Andric #define FIXABLE_GADGET(x) \ 1075bdd1243dSDimitry Andric x ## Gadget::matcher().bind(#x), 1076bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1077bdd1243dSDimitry Andric // In parallel, match all DeclRefExprs so that to find out 1078bdd1243dSDimitry Andric // whether there are any uncovered by gadgets. 1079bdd1243dSDimitry Andric declRefExpr(anyOf(hasPointerType(), hasArrayType()), 1080bdd1243dSDimitry Andric to(varDecl())).bind("any_dre"), 1081bdd1243dSDimitry Andric // Also match DeclStmts because we'll need them when fixing 1082bdd1243dSDimitry Andric // their underlying VarDecls that otherwise don't have 1083bdd1243dSDimitry Andric // any backreferences to DeclStmts. 1084bdd1243dSDimitry Andric declStmt().bind("any_ds") 1085*06c3fb27SDimitry Andric ))) 1086*06c3fb27SDimitry Andric ), 1087bdd1243dSDimitry Andric &CB 1088bdd1243dSDimitry Andric ); 1089bdd1243dSDimitry Andric // clang-format on 1090*06c3fb27SDimitry Andric } 1091bdd1243dSDimitry Andric 1092bdd1243dSDimitry Andric M.match(*D->getBody(), D->getASTContext()); 1093bdd1243dSDimitry Andric 1094bdd1243dSDimitry Andric // Gadgets "claim" variables they're responsible for. Once this loop finishes, 1095bdd1243dSDimitry Andric // the tracker will only track DREs that weren't claimed by any gadgets, 1096bdd1243dSDimitry Andric // i.e. not understood by the analysis. 1097bdd1243dSDimitry Andric for (const auto &G : CB.FixableGadgets) { 1098bdd1243dSDimitry Andric for (const auto *DRE : G->getClaimedVarUseSites()) { 1099bdd1243dSDimitry Andric CB.Tracker.claimUse(DRE); 1100bdd1243dSDimitry Andric } 1101bdd1243dSDimitry Andric } 1102bdd1243dSDimitry Andric 1103*06c3fb27SDimitry Andric return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets), 1104*06c3fb27SDimitry Andric std::move(CB.Tracker)}; 1105bdd1243dSDimitry Andric } 1106bdd1243dSDimitry Andric 1107*06c3fb27SDimitry Andric // Compares AST nodes by source locations. 1108*06c3fb27SDimitry Andric template <typename NodeTy> struct CompareNode { 1109*06c3fb27SDimitry Andric bool operator()(const NodeTy *N1, const NodeTy *N2) const { 1110*06c3fb27SDimitry Andric return N1->getBeginLoc().getRawEncoding() < 1111*06c3fb27SDimitry Andric N2->getBeginLoc().getRawEncoding(); 1112*06c3fb27SDimitry Andric } 1113*06c3fb27SDimitry Andric }; 1114*06c3fb27SDimitry Andric 1115bdd1243dSDimitry Andric struct WarningGadgetSets { 1116*06c3fb27SDimitry Andric std::map<const VarDecl *, std::set<const WarningGadget *>, 1117*06c3fb27SDimitry Andric // To keep keys sorted by their locations in the map so that the 1118*06c3fb27SDimitry Andric // order is deterministic: 1119*06c3fb27SDimitry Andric CompareNode<VarDecl>> 1120*06c3fb27SDimitry Andric byVar; 1121bdd1243dSDimitry Andric // These Gadgets are not related to pointer variables (e. g. temporaries). 1122*06c3fb27SDimitry Andric llvm::SmallVector<const WarningGadget *, 16> noVar; 1123bdd1243dSDimitry Andric }; 1124bdd1243dSDimitry Andric 1125bdd1243dSDimitry Andric static WarningGadgetSets 1126*06c3fb27SDimitry Andric groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) { 1127bdd1243dSDimitry Andric WarningGadgetSets result; 1128bdd1243dSDimitry Andric // If some gadgets cover more than one 1129bdd1243dSDimitry Andric // variable, they'll appear more than once in the map. 1130bdd1243dSDimitry Andric for (auto &G : AllUnsafeOperations) { 1131bdd1243dSDimitry Andric DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites(); 1132bdd1243dSDimitry Andric 1133bdd1243dSDimitry Andric bool AssociatedWithVarDecl = false; 1134bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : ClaimedVarUseSites) { 1135bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 1136*06c3fb27SDimitry Andric result.byVar[VD].insert(G.get()); 1137bdd1243dSDimitry Andric AssociatedWithVarDecl = true; 1138bdd1243dSDimitry Andric } 1139bdd1243dSDimitry Andric } 1140bdd1243dSDimitry Andric 1141bdd1243dSDimitry Andric if (!AssociatedWithVarDecl) { 1142*06c3fb27SDimitry Andric result.noVar.push_back(G.get()); 1143bdd1243dSDimitry Andric continue; 1144bdd1243dSDimitry Andric } 1145bdd1243dSDimitry Andric } 1146bdd1243dSDimitry Andric return result; 1147bdd1243dSDimitry Andric } 1148bdd1243dSDimitry Andric 1149bdd1243dSDimitry Andric struct FixableGadgetSets { 1150*06c3fb27SDimitry Andric std::map<const VarDecl *, std::set<const FixableGadget *>> byVar; 1151bdd1243dSDimitry Andric }; 1152bdd1243dSDimitry Andric 1153bdd1243dSDimitry Andric static FixableGadgetSets 1154bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) { 1155bdd1243dSDimitry Andric FixableGadgetSets FixablesForUnsafeVars; 1156bdd1243dSDimitry Andric for (auto &F : AllFixableOperations) { 1157bdd1243dSDimitry Andric DeclUseList DREs = F->getClaimedVarUseSites(); 1158bdd1243dSDimitry Andric 1159bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : DREs) { 1160bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 1161*06c3fb27SDimitry Andric FixablesForUnsafeVars.byVar[VD].insert(F.get()); 1162bdd1243dSDimitry Andric } 1163bdd1243dSDimitry Andric } 1164bdd1243dSDimitry Andric } 1165bdd1243dSDimitry Andric return FixablesForUnsafeVars; 1166bdd1243dSDimitry Andric } 1167bdd1243dSDimitry Andric 1168*06c3fb27SDimitry Andric bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts, 1169*06c3fb27SDimitry Andric const SourceManager &SM) { 1170*06c3fb27SDimitry Andric // A simple interval overlap detection algorithm. Sorts all ranges by their 1171*06c3fb27SDimitry Andric // begin location then finds the first overlap in one pass. 1172*06c3fb27SDimitry Andric std::vector<const FixItHint *> All; // a copy of `FixIts` 1173*06c3fb27SDimitry Andric 1174*06c3fb27SDimitry Andric for (const FixItHint &H : FixIts) 1175*06c3fb27SDimitry Andric All.push_back(&H); 1176*06c3fb27SDimitry Andric std::sort(All.begin(), All.end(), 1177*06c3fb27SDimitry Andric [&SM](const FixItHint *H1, const FixItHint *H2) { 1178*06c3fb27SDimitry Andric return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(), 1179*06c3fb27SDimitry Andric H2->RemoveRange.getBegin()); 1180*06c3fb27SDimitry Andric }); 1181*06c3fb27SDimitry Andric 1182*06c3fb27SDimitry Andric const FixItHint *CurrHint = nullptr; 1183*06c3fb27SDimitry Andric 1184*06c3fb27SDimitry Andric for (const FixItHint *Hint : All) { 1185*06c3fb27SDimitry Andric if (!CurrHint || 1186*06c3fb27SDimitry Andric SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(), 1187*06c3fb27SDimitry Andric Hint->RemoveRange.getBegin())) { 1188*06c3fb27SDimitry Andric // Either to initialize `CurrHint` or `CurrHint` does not 1189*06c3fb27SDimitry Andric // overlap with `Hint`: 1190*06c3fb27SDimitry Andric CurrHint = Hint; 1191*06c3fb27SDimitry Andric } else 1192*06c3fb27SDimitry Andric // In case `Hint` overlaps the `CurrHint`, we found at least one 1193*06c3fb27SDimitry Andric // conflict: 1194*06c3fb27SDimitry Andric return true; 1195*06c3fb27SDimitry Andric } 1196*06c3fb27SDimitry Andric return false; 1197*06c3fb27SDimitry Andric } 1198*06c3fb27SDimitry Andric 1199*06c3fb27SDimitry Andric std::optional<FixItList> 1200*06c3fb27SDimitry Andric PointerAssignmentGadget::getFixits(const Strategy &S) const { 1201*06c3fb27SDimitry Andric const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl()); 1202*06c3fb27SDimitry Andric const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl()); 1203*06c3fb27SDimitry Andric switch (S.lookup(LeftVD)) { 1204*06c3fb27SDimitry Andric case Strategy::Kind::Span: 1205*06c3fb27SDimitry Andric if (S.lookup(RightVD) == Strategy::Kind::Span) 1206*06c3fb27SDimitry Andric return FixItList{}; 1207*06c3fb27SDimitry Andric return std::nullopt; 1208*06c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 1209*06c3fb27SDimitry Andric return std::nullopt; 1210*06c3fb27SDimitry Andric case Strategy::Kind::Iterator: 1211*06c3fb27SDimitry Andric case Strategy::Kind::Array: 1212*06c3fb27SDimitry Andric case Strategy::Kind::Vector: 1213*06c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 1214*06c3fb27SDimitry Andric } 1215*06c3fb27SDimitry Andric return std::nullopt; 1216*06c3fb27SDimitry Andric } 1217*06c3fb27SDimitry Andric 1218*06c3fb27SDimitry Andric std::optional<FixItList> 1219*06c3fb27SDimitry Andric PointerInitGadget::getFixits(const Strategy &S) const { 1220*06c3fb27SDimitry Andric const auto *LeftVD = PtrInitLHS; 1221*06c3fb27SDimitry Andric const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl()); 1222*06c3fb27SDimitry Andric switch (S.lookup(LeftVD)) { 1223*06c3fb27SDimitry Andric case Strategy::Kind::Span: 1224*06c3fb27SDimitry Andric if (S.lookup(RightVD) == Strategy::Kind::Span) 1225*06c3fb27SDimitry Andric return FixItList{}; 1226*06c3fb27SDimitry Andric return std::nullopt; 1227*06c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 1228*06c3fb27SDimitry Andric return std::nullopt; 1229*06c3fb27SDimitry Andric case Strategy::Kind::Iterator: 1230*06c3fb27SDimitry Andric case Strategy::Kind::Array: 1231*06c3fb27SDimitry Andric case Strategy::Kind::Vector: 1232*06c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 1233*06c3fb27SDimitry Andric } 1234*06c3fb27SDimitry Andric return std::nullopt; 1235*06c3fb27SDimitry Andric } 1236*06c3fb27SDimitry Andric 1237*06c3fb27SDimitry Andric std::optional<FixItList> 1238*06c3fb27SDimitry Andric ULCArraySubscriptGadget::getFixits(const Strategy &S) const { 1239*06c3fb27SDimitry Andric if (const auto *DRE = 1240*06c3fb27SDimitry Andric dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) 1241*06c3fb27SDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 1242*06c3fb27SDimitry Andric switch (S.lookup(VD)) { 1243*06c3fb27SDimitry Andric case Strategy::Kind::Span: { 1244*06c3fb27SDimitry Andric // If the index has a negative constant value, we give up as no valid 1245*06c3fb27SDimitry Andric // fix-it can be generated: 1246*06c3fb27SDimitry Andric const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in! 1247*06c3fb27SDimitry Andric VD->getASTContext(); 1248*06c3fb27SDimitry Andric if (auto ConstVal = Node->getIdx()->getIntegerConstantExpr(Ctx)) { 1249*06c3fb27SDimitry Andric if (ConstVal->isNegative()) 1250*06c3fb27SDimitry Andric return std::nullopt; 1251*06c3fb27SDimitry Andric } else if (!Node->getIdx()->getType()->isUnsignedIntegerType()) 1252*06c3fb27SDimitry Andric return std::nullopt; 1253*06c3fb27SDimitry Andric // no-op is a good fix-it, otherwise 1254*06c3fb27SDimitry Andric return FixItList{}; 1255*06c3fb27SDimitry Andric } 1256*06c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 1257*06c3fb27SDimitry Andric case Strategy::Kind::Iterator: 1258*06c3fb27SDimitry Andric case Strategy::Kind::Array: 1259*06c3fb27SDimitry Andric case Strategy::Kind::Vector: 1260*06c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 1261*06c3fb27SDimitry Andric } 1262*06c3fb27SDimitry Andric } 1263*06c3fb27SDimitry Andric return std::nullopt; 1264*06c3fb27SDimitry Andric } 1265*06c3fb27SDimitry Andric 1266*06c3fb27SDimitry Andric static std::optional<FixItList> // forward declaration 1267*06c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node); 1268*06c3fb27SDimitry Andric 1269*06c3fb27SDimitry Andric std::optional<FixItList> 1270*06c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget::getFixits(const Strategy &S) const { 1271*06c3fb27SDimitry Andric auto DREs = getClaimedVarUseSites(); 1272*06c3fb27SDimitry Andric const auto *VD = cast<VarDecl>(DREs.front()->getDecl()); 1273*06c3fb27SDimitry Andric 1274*06c3fb27SDimitry Andric switch (S.lookup(VD)) { 1275*06c3fb27SDimitry Andric case Strategy::Kind::Span: 1276*06c3fb27SDimitry Andric return fixUPCAddressofArraySubscriptWithSpan(Node); 1277*06c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 1278*06c3fb27SDimitry Andric case Strategy::Kind::Iterator: 1279*06c3fb27SDimitry Andric case Strategy::Kind::Array: 1280*06c3fb27SDimitry Andric case Strategy::Kind::Vector: 1281*06c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 1282*06c3fb27SDimitry Andric } 1283*06c3fb27SDimitry Andric return std::nullopt; // something went wrong, no fix-it 1284*06c3fb27SDimitry Andric } 1285*06c3fb27SDimitry Andric 1286*06c3fb27SDimitry Andric // FIXME: this function should be customizable through format 1287*06c3fb27SDimitry Andric static StringRef getEndOfLine() { 1288*06c3fb27SDimitry Andric static const char *const EOL = "\n"; 1289*06c3fb27SDimitry Andric return EOL; 1290*06c3fb27SDimitry Andric } 1291*06c3fb27SDimitry Andric 1292*06c3fb27SDimitry Andric // Returns the text indicating that the user needs to provide input there: 1293*06c3fb27SDimitry Andric std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") { 1294*06c3fb27SDimitry Andric std::string s = std::string("<# "); 1295*06c3fb27SDimitry Andric s += HintTextToUser; 1296*06c3fb27SDimitry Andric s += " #>"; 1297*06c3fb27SDimitry Andric return s; 1298*06c3fb27SDimitry Andric } 1299*06c3fb27SDimitry Andric 1300*06c3fb27SDimitry Andric // Return the text representation of the given `APInt Val`: 1301*06c3fb27SDimitry Andric static std::string getAPIntText(APInt Val) { 1302*06c3fb27SDimitry Andric SmallVector<char> Txt; 1303*06c3fb27SDimitry Andric Val.toString(Txt, 10, true); 1304*06c3fb27SDimitry Andric // APInt::toString does not add '\0' to the end of the string for us: 1305*06c3fb27SDimitry Andric Txt.push_back('\0'); 1306*06c3fb27SDimitry Andric return Txt.data(); 1307*06c3fb27SDimitry Andric } 1308*06c3fb27SDimitry Andric 1309*06c3fb27SDimitry Andric // Return the source location of the last character of the AST `Node`. 1310*06c3fb27SDimitry Andric template <typename NodeTy> 1311*06c3fb27SDimitry Andric static std::optional<SourceLocation> 1312*06c3fb27SDimitry Andric getEndCharLoc(const NodeTy *Node, const SourceManager &SM, 1313*06c3fb27SDimitry Andric const LangOptions &LangOpts) { 1314*06c3fb27SDimitry Andric unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts); 1315*06c3fb27SDimitry Andric SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1); 1316*06c3fb27SDimitry Andric 1317*06c3fb27SDimitry Andric if (Loc.isValid()) 1318*06c3fb27SDimitry Andric return Loc; 1319*06c3fb27SDimitry Andric 1320*06c3fb27SDimitry Andric return std::nullopt; 1321*06c3fb27SDimitry Andric } 1322*06c3fb27SDimitry Andric 1323*06c3fb27SDimitry Andric // Return the source location just past the last character of the AST `Node`. 1324*06c3fb27SDimitry Andric template <typename NodeTy> 1325*06c3fb27SDimitry Andric static std::optional<SourceLocation> getPastLoc(const NodeTy *Node, 1326*06c3fb27SDimitry Andric const SourceManager &SM, 1327*06c3fb27SDimitry Andric const LangOptions &LangOpts) { 1328*06c3fb27SDimitry Andric SourceLocation Loc = 1329*06c3fb27SDimitry Andric Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts); 1330*06c3fb27SDimitry Andric 1331*06c3fb27SDimitry Andric if (Loc.isValid()) 1332*06c3fb27SDimitry Andric return Loc; 1333*06c3fb27SDimitry Andric 1334*06c3fb27SDimitry Andric return std::nullopt; 1335*06c3fb27SDimitry Andric } 1336*06c3fb27SDimitry Andric 1337*06c3fb27SDimitry Andric // Return text representation of an `Expr`. 1338*06c3fb27SDimitry Andric static std::optional<StringRef> getExprText(const Expr *E, 1339*06c3fb27SDimitry Andric const SourceManager &SM, 1340*06c3fb27SDimitry Andric const LangOptions &LangOpts) { 1341*06c3fb27SDimitry Andric std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts); 1342*06c3fb27SDimitry Andric 1343*06c3fb27SDimitry Andric if (LastCharLoc) 1344*06c3fb27SDimitry Andric return Lexer::getSourceText( 1345*06c3fb27SDimitry Andric CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM, 1346*06c3fb27SDimitry Andric LangOpts); 1347*06c3fb27SDimitry Andric 1348*06c3fb27SDimitry Andric return std::nullopt; 1349*06c3fb27SDimitry Andric } 1350*06c3fb27SDimitry Andric 1351*06c3fb27SDimitry Andric // Returns the literal text in `SourceRange SR`, if `SR` is a valid range. 1352*06c3fb27SDimitry Andric static std::optional<StringRef> getRangeText(SourceRange SR, 1353*06c3fb27SDimitry Andric const SourceManager &SM, 1354*06c3fb27SDimitry Andric const LangOptions &LangOpts) { 1355*06c3fb27SDimitry Andric bool Invalid = false; 1356*06c3fb27SDimitry Andric CharSourceRange CSR = CharSourceRange::getCharRange(SR.getBegin(), SR.getEnd()); 1357*06c3fb27SDimitry Andric StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid); 1358*06c3fb27SDimitry Andric 1359*06c3fb27SDimitry Andric if (!Invalid) 1360*06c3fb27SDimitry Andric return Text; 1361*06c3fb27SDimitry Andric return std::nullopt; 1362*06c3fb27SDimitry Andric } 1363*06c3fb27SDimitry Andric 1364*06c3fb27SDimitry Andric // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer 1365*06c3fb27SDimitry Andric // type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not 1366*06c3fb27SDimitry Andric // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me 1367*06c3fb27SDimitry Andric // :( ), `Qualifiers` of the pointee type is returned separately through the 1368*06c3fb27SDimitry Andric // output parameter `QualifiersToAppend`. 1369*06c3fb27SDimitry Andric static std::optional<std::string> 1370*06c3fb27SDimitry Andric getPointeeTypeText(const ParmVarDecl *VD, const SourceManager &SM, 1371*06c3fb27SDimitry Andric const LangOptions &LangOpts, 1372*06c3fb27SDimitry Andric std::optional<Qualifiers> *QualifiersToAppend) { 1373*06c3fb27SDimitry Andric QualType Ty = VD->getType(); 1374*06c3fb27SDimitry Andric QualType PteTy; 1375*06c3fb27SDimitry Andric 1376*06c3fb27SDimitry Andric assert(Ty->isPointerType() && !Ty->isFunctionPointerType() && 1377*06c3fb27SDimitry Andric "Expecting a VarDecl of type of pointer to object type"); 1378*06c3fb27SDimitry Andric PteTy = Ty->getPointeeType(); 1379*06c3fb27SDimitry Andric if (PteTy->hasUnnamedOrLocalType()) 1380*06c3fb27SDimitry Andric // If the pointee type is unnamed, we can't refer to it 1381*06c3fb27SDimitry Andric return std::nullopt; 1382*06c3fb27SDimitry Andric 1383*06c3fb27SDimitry Andric TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc(); 1384*06c3fb27SDimitry Andric TypeLoc PteTyLoc = TyLoc.getUnqualifiedLoc().getNextTypeLoc(); 1385*06c3fb27SDimitry Andric SourceLocation VDNameStartLoc = VD->getLocation(); 1386*06c3fb27SDimitry Andric 1387*06c3fb27SDimitry Andric if (!(VDNameStartLoc.isValid() && PteTyLoc.getSourceRange().isValid())) { 1388*06c3fb27SDimitry Andric // We are expecting these locations to be valid. But in some cases, they are 1389*06c3fb27SDimitry Andric // not all valid. It is a Clang bug to me and we are not responsible for 1390*06c3fb27SDimitry Andric // fixing it. So we will just give up for now when it happens. 1391*06c3fb27SDimitry Andric return std::nullopt; 1392*06c3fb27SDimitry Andric } 1393*06c3fb27SDimitry Andric 1394*06c3fb27SDimitry Andric // Note that TypeLoc.getEndLoc() returns the begin location of the last token: 1395*06c3fb27SDimitry Andric SourceLocation PteEndOfTokenLoc = 1396*06c3fb27SDimitry Andric Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts); 1397*06c3fb27SDimitry Andric 1398*06c3fb27SDimitry Andric if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, VDNameStartLoc)) { 1399*06c3fb27SDimitry Andric // We only deal with the cases where the source text of the pointee type 1400*06c3fb27SDimitry Andric // appears on the left-hand side of the variable identifier completely, 1401*06c3fb27SDimitry Andric // including the following forms: 1402*06c3fb27SDimitry Andric // `T ident`, 1403*06c3fb27SDimitry Andric // `T ident[]`, where `T` is any type. 1404*06c3fb27SDimitry Andric // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`. 1405*06c3fb27SDimitry Andric return std::nullopt; 1406*06c3fb27SDimitry Andric } 1407*06c3fb27SDimitry Andric if (PteTy.hasQualifiers()) { 1408*06c3fb27SDimitry Andric // TypeLoc does not provide source ranges for qualifiers (it says it's 1409*06c3fb27SDimitry Andric // intentional but seems fishy to me), so we cannot get the full text 1410*06c3fb27SDimitry Andric // `PteTy` via source ranges. 1411*06c3fb27SDimitry Andric *QualifiersToAppend = PteTy.getQualifiers(); 1412*06c3fb27SDimitry Andric } 1413*06c3fb27SDimitry Andric return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts) 1414*06c3fb27SDimitry Andric ->str(); 1415*06c3fb27SDimitry Andric } 1416*06c3fb27SDimitry Andric 1417*06c3fb27SDimitry Andric // Returns the text of the name (with qualifiers) of a `FunctionDecl`. 1418*06c3fb27SDimitry Andric static std::optional<StringRef> getFunNameText(const FunctionDecl *FD, 1419*06c3fb27SDimitry Andric const SourceManager &SM, 1420*06c3fb27SDimitry Andric const LangOptions &LangOpts) { 1421*06c3fb27SDimitry Andric SourceLocation BeginLoc = FD->getQualifier() 1422*06c3fb27SDimitry Andric ? FD->getQualifierLoc().getBeginLoc() 1423*06c3fb27SDimitry Andric : FD->getNameInfo().getBeginLoc(); 1424*06c3fb27SDimitry Andric // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the 1425*06c3fb27SDimitry Andric // last token: 1426*06c3fb27SDimitry Andric SourceLocation EndLoc = Lexer::getLocForEndOfToken( 1427*06c3fb27SDimitry Andric FD->getNameInfo().getEndLoc(), 0, SM, LangOpts); 1428*06c3fb27SDimitry Andric SourceRange NameRange{BeginLoc, EndLoc}; 1429*06c3fb27SDimitry Andric 1430*06c3fb27SDimitry Andric return getRangeText(NameRange, SM, LangOpts); 1431*06c3fb27SDimitry Andric } 1432*06c3fb27SDimitry Andric 1433*06c3fb27SDimitry Andric std::optional<FixItList> 1434*06c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget::getFixits(const Strategy &s) const { 1435*06c3fb27SDimitry Andric const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl()); 1436*06c3fb27SDimitry Andric 1437*06c3fb27SDimitry Andric if (VD && s.lookup(VD) == Strategy::Kind::Span) { 1438*06c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 1439*06c3fb27SDimitry Andric // std::span can't represent elements before its begin() 1440*06c3fb27SDimitry Andric if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx)) 1441*06c3fb27SDimitry Andric if (ConstVal->isNegative()) 1442*06c3fb27SDimitry Andric return std::nullopt; 1443*06c3fb27SDimitry Andric 1444*06c3fb27SDimitry Andric // note that the expr may (oddly) has multiple layers of parens 1445*06c3fb27SDimitry Andric // example: 1446*06c3fb27SDimitry Andric // *((..(pointer + 123)..)) 1447*06c3fb27SDimitry Andric // goal: 1448*06c3fb27SDimitry Andric // pointer[123] 1449*06c3fb27SDimitry Andric // Fix-It: 1450*06c3fb27SDimitry Andric // remove '*(' 1451*06c3fb27SDimitry Andric // replace ' + ' with '[' 1452*06c3fb27SDimitry Andric // replace ')' with ']' 1453*06c3fb27SDimitry Andric 1454*06c3fb27SDimitry Andric // example: 1455*06c3fb27SDimitry Andric // *((..(123 + pointer)..)) 1456*06c3fb27SDimitry Andric // goal: 1457*06c3fb27SDimitry Andric // 123[pointer] 1458*06c3fb27SDimitry Andric // Fix-It: 1459*06c3fb27SDimitry Andric // remove '*(' 1460*06c3fb27SDimitry Andric // replace ' + ' with '[' 1461*06c3fb27SDimitry Andric // replace ')' with ']' 1462*06c3fb27SDimitry Andric 1463*06c3fb27SDimitry Andric const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS(); 1464*06c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 1465*06c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 1466*06c3fb27SDimitry Andric CharSourceRange StarWithTrailWhitespace = 1467*06c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(), 1468*06c3fb27SDimitry Andric LHS->getBeginLoc()); 1469*06c3fb27SDimitry Andric 1470*06c3fb27SDimitry Andric std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts); 1471*06c3fb27SDimitry Andric if (!LHSLocation) 1472*06c3fb27SDimitry Andric return std::nullopt; 1473*06c3fb27SDimitry Andric 1474*06c3fb27SDimitry Andric CharSourceRange PlusWithSurroundingWhitespace = 1475*06c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc()); 1476*06c3fb27SDimitry Andric 1477*06c3fb27SDimitry Andric std::optional<SourceLocation> AddOpLocation = 1478*06c3fb27SDimitry Andric getPastLoc(AddOp, SM, LangOpts); 1479*06c3fb27SDimitry Andric std::optional<SourceLocation> DerefOpLocation = 1480*06c3fb27SDimitry Andric getPastLoc(DerefOp, SM, LangOpts); 1481*06c3fb27SDimitry Andric 1482*06c3fb27SDimitry Andric if (!AddOpLocation || !DerefOpLocation) 1483*06c3fb27SDimitry Andric return std::nullopt; 1484*06c3fb27SDimitry Andric 1485*06c3fb27SDimitry Andric CharSourceRange ClosingParenWithPrecWhitespace = 1486*06c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation); 1487*06c3fb27SDimitry Andric 1488*06c3fb27SDimitry Andric return FixItList{ 1489*06c3fb27SDimitry Andric {FixItHint::CreateRemoval(StarWithTrailWhitespace), 1490*06c3fb27SDimitry Andric FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["), 1491*06c3fb27SDimitry Andric FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}}; 1492*06c3fb27SDimitry Andric } 1493*06c3fb27SDimitry Andric return std::nullopt; // something wrong or unsupported, give up 1494*06c3fb27SDimitry Andric } 1495*06c3fb27SDimitry Andric 1496*06c3fb27SDimitry Andric std::optional<FixItList> 1497*06c3fb27SDimitry Andric PointerDereferenceGadget::getFixits(const Strategy &S) const { 1498*06c3fb27SDimitry Andric const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl()); 1499*06c3fb27SDimitry Andric switch (S.lookup(VD)) { 1500*06c3fb27SDimitry Andric case Strategy::Kind::Span: { 1501*06c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 1502*06c3fb27SDimitry Andric SourceManager &SM = Ctx.getSourceManager(); 1503*06c3fb27SDimitry Andric // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0] 1504*06c3fb27SDimitry Andric // Deletes the *operand 1505*06c3fb27SDimitry Andric CharSourceRange derefRange = clang::CharSourceRange::getCharRange( 1506*06c3fb27SDimitry Andric Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1)); 1507*06c3fb27SDimitry Andric // Inserts the [0] 1508*06c3fb27SDimitry Andric std::optional<SourceLocation> EndOfOperand = 1509*06c3fb27SDimitry Andric getEndCharLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts()); 1510*06c3fb27SDimitry Andric if (EndOfOperand) { 1511*06c3fb27SDimitry Andric return FixItList{{FixItHint::CreateRemoval(derefRange), 1512*06c3fb27SDimitry Andric FixItHint::CreateInsertion( 1513*06c3fb27SDimitry Andric (*EndOfOperand).getLocWithOffset(1), "[0]")}}; 1514*06c3fb27SDimitry Andric } 1515*06c3fb27SDimitry Andric } 1516*06c3fb27SDimitry Andric [[fallthrough]]; 1517*06c3fb27SDimitry Andric case Strategy::Kind::Iterator: 1518*06c3fb27SDimitry Andric case Strategy::Kind::Array: 1519*06c3fb27SDimitry Andric case Strategy::Kind::Vector: 1520*06c3fb27SDimitry Andric llvm_unreachable("Strategy not implemented yet!"); 1521*06c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 1522*06c3fb27SDimitry Andric llvm_unreachable("Invalid strategy!"); 1523*06c3fb27SDimitry Andric } 1524*06c3fb27SDimitry Andric 1525*06c3fb27SDimitry Andric return std::nullopt; 1526*06c3fb27SDimitry Andric } 1527*06c3fb27SDimitry Andric 1528*06c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form UPC(DRE) with 1529*06c3fb27SDimitry Andric // `DRE.data()` 1530*06c3fb27SDimitry Andric std::optional<FixItList> UPCStandalonePointerGadget::getFixits(const Strategy &S) 1531*06c3fb27SDimitry Andric const { 1532*06c3fb27SDimitry Andric const auto VD = cast<VarDecl>(Node->getDecl()); 1533*06c3fb27SDimitry Andric switch (S.lookup(VD)) { 1534*06c3fb27SDimitry Andric case Strategy::Kind::Span: { 1535*06c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 1536*06c3fb27SDimitry Andric SourceManager &SM = Ctx.getSourceManager(); 1537*06c3fb27SDimitry Andric // Inserts the .data() after the DRE 1538*06c3fb27SDimitry Andric std::optional<SourceLocation> EndOfOperand = 1539*06c3fb27SDimitry Andric getPastLoc(Node, SM, Ctx.getLangOpts()); 1540*06c3fb27SDimitry Andric 1541*06c3fb27SDimitry Andric if (EndOfOperand) 1542*06c3fb27SDimitry Andric return FixItList{{FixItHint::CreateInsertion( 1543*06c3fb27SDimitry Andric *EndOfOperand, ".data()")}}; 1544*06c3fb27SDimitry Andric } 1545*06c3fb27SDimitry Andric [[fallthrough]]; 1546*06c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 1547*06c3fb27SDimitry Andric case Strategy::Kind::Iterator: 1548*06c3fb27SDimitry Andric case Strategy::Kind::Array: 1549*06c3fb27SDimitry Andric case Strategy::Kind::Vector: 1550*06c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 1551*06c3fb27SDimitry Andric } 1552*06c3fb27SDimitry Andric 1553*06c3fb27SDimitry Andric return std::nullopt; 1554*06c3fb27SDimitry Andric } 1555*06c3fb27SDimitry Andric 1556*06c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form `&DRE[e]` with 1557*06c3fb27SDimitry Andric // `&DRE.data()[e]`: 1558*06c3fb27SDimitry Andric static std::optional<FixItList> 1559*06c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) { 1560*06c3fb27SDimitry Andric const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr()); 1561*06c3fb27SDimitry Andric const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts()); 1562*06c3fb27SDimitry Andric // FIXME: this `getASTContext` call is costly, we should pass the 1563*06c3fb27SDimitry Andric // ASTContext in: 1564*06c3fb27SDimitry Andric const ASTContext &Ctx = DRE->getDecl()->getASTContext(); 1565*06c3fb27SDimitry Andric const Expr *Idx = ArraySub->getIdx(); 1566*06c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 1567*06c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 1568*06c3fb27SDimitry Andric std::stringstream SS; 1569*06c3fb27SDimitry Andric bool IdxIsLitZero = false; 1570*06c3fb27SDimitry Andric 1571*06c3fb27SDimitry Andric if (auto ICE = Idx->getIntegerConstantExpr(Ctx)) 1572*06c3fb27SDimitry Andric if ((*ICE).isZero()) 1573*06c3fb27SDimitry Andric IdxIsLitZero = true; 1574*06c3fb27SDimitry Andric std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts); 1575*06c3fb27SDimitry Andric if (!DreString) 1576*06c3fb27SDimitry Andric return std::nullopt; 1577*06c3fb27SDimitry Andric 1578*06c3fb27SDimitry Andric if (IdxIsLitZero) { 1579*06c3fb27SDimitry Andric // If the index is literal zero, we produce the most concise fix-it: 1580*06c3fb27SDimitry Andric SS << (*DreString).str() << ".data()"; 1581*06c3fb27SDimitry Andric } else { 1582*06c3fb27SDimitry Andric std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts); 1583*06c3fb27SDimitry Andric if (!IndexString) 1584*06c3fb27SDimitry Andric return std::nullopt; 1585*06c3fb27SDimitry Andric 1586*06c3fb27SDimitry Andric SS << "&" << (*DreString).str() << ".data()" 1587*06c3fb27SDimitry Andric << "[" << (*IndexString).str() << "]"; 1588*06c3fb27SDimitry Andric } 1589*06c3fb27SDimitry Andric return FixItList{ 1590*06c3fb27SDimitry Andric FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())}; 1591*06c3fb27SDimitry Andric } 1592*06c3fb27SDimitry Andric 1593*06c3fb27SDimitry Andric 1594*06c3fb27SDimitry Andric std::optional<FixItList> UPCPreIncrementGadget::getFixits(const Strategy &S) const { 1595*06c3fb27SDimitry Andric DeclUseList DREs = getClaimedVarUseSites(); 1596*06c3fb27SDimitry Andric 1597*06c3fb27SDimitry Andric if (DREs.size() != 1) 1598*06c3fb27SDimitry Andric return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we 1599*06c3fb27SDimitry Andric // give up 1600*06c3fb27SDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) { 1601*06c3fb27SDimitry Andric if (S.lookup(VD) == Strategy::Kind::Span) { 1602*06c3fb27SDimitry Andric FixItList Fixes; 1603*06c3fb27SDimitry Andric std::stringstream SS; 1604*06c3fb27SDimitry Andric const Stmt *PreIncNode = getBaseStmt(); 1605*06c3fb27SDimitry Andric StringRef varName = VD->getName(); 1606*06c3fb27SDimitry Andric const ASTContext &Ctx = VD->getASTContext(); 1607*06c3fb27SDimitry Andric 1608*06c3fb27SDimitry Andric // To transform UPC(++p) to UPC((p = p.subspan(1)).data()): 1609*06c3fb27SDimitry Andric SS << "(" << varName.data() << " = " << varName.data() 1610*06c3fb27SDimitry Andric << ".subspan(1)).data()"; 1611*06c3fb27SDimitry Andric std::optional<SourceLocation> PreIncLocation = 1612*06c3fb27SDimitry Andric getEndCharLoc(PreIncNode, Ctx.getSourceManager(), Ctx.getLangOpts()); 1613*06c3fb27SDimitry Andric if (!PreIncLocation) 1614*06c3fb27SDimitry Andric return std::nullopt; 1615*06c3fb27SDimitry Andric 1616*06c3fb27SDimitry Andric Fixes.push_back(FixItHint::CreateReplacement( 1617*06c3fb27SDimitry Andric SourceRange(PreIncNode->getBeginLoc(), *PreIncLocation), SS.str())); 1618*06c3fb27SDimitry Andric return Fixes; 1619*06c3fb27SDimitry Andric } 1620*06c3fb27SDimitry Andric } 1621*06c3fb27SDimitry Andric return std::nullopt; // Not in the cases that we can handle for now, give up. 1622*06c3fb27SDimitry Andric } 1623*06c3fb27SDimitry Andric 1624*06c3fb27SDimitry Andric 1625*06c3fb27SDimitry Andric // For a non-null initializer `Init` of `T *` type, this function returns 1626*06c3fb27SDimitry Andric // `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it 1627*06c3fb27SDimitry Andric // to output stream. 1628*06c3fb27SDimitry Andric // In many cases, this function cannot figure out the actual extent `S`. It 1629*06c3fb27SDimitry Andric // then will use a place holder to replace `S` to ask users to fill `S` in. The 1630*06c3fb27SDimitry Andric // initializer shall be used to initialize a variable of type `std::span<T>`. 1631*06c3fb27SDimitry Andric // 1632*06c3fb27SDimitry Andric // FIXME: Support multi-level pointers 1633*06c3fb27SDimitry Andric // 1634*06c3fb27SDimitry Andric // Parameters: 1635*06c3fb27SDimitry Andric // `Init` a pointer to the initializer expression 1636*06c3fb27SDimitry Andric // `Ctx` a reference to the ASTContext 1637*06c3fb27SDimitry Andric static FixItList 1638*06c3fb27SDimitry Andric populateInitializerFixItWithSpan(const Expr *Init, ASTContext &Ctx, 1639*06c3fb27SDimitry Andric const StringRef UserFillPlaceHolder) { 1640*06c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 1641*06c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 1642*06c3fb27SDimitry Andric 1643*06c3fb27SDimitry Andric // If `Init` has a constant value that is (or equivalent to) a 1644*06c3fb27SDimitry Andric // NULL pointer, we use the default constructor to initialize the span 1645*06c3fb27SDimitry Andric // object, i.e., a `std:span` variable declaration with no initializer. 1646*06c3fb27SDimitry Andric // So the fix-it is just to remove the initializer. 1647*06c3fb27SDimitry Andric if (Init->isNullPointerConstant(Ctx, 1648*06c3fb27SDimitry Andric // FIXME: Why does this function not ask for `const ASTContext 1649*06c3fb27SDimitry Andric // &`? It should. Maybe worth an NFC patch later. 1650*06c3fb27SDimitry Andric Expr::NullPointerConstantValueDependence:: 1651*06c3fb27SDimitry Andric NPC_ValueDependentIsNotNull)) { 1652*06c3fb27SDimitry Andric std::optional<SourceLocation> InitLocation = 1653*06c3fb27SDimitry Andric getEndCharLoc(Init, SM, LangOpts); 1654*06c3fb27SDimitry Andric if (!InitLocation) 1655*06c3fb27SDimitry Andric return {}; 1656*06c3fb27SDimitry Andric 1657*06c3fb27SDimitry Andric SourceRange SR(Init->getBeginLoc(), *InitLocation); 1658*06c3fb27SDimitry Andric 1659*06c3fb27SDimitry Andric return {FixItHint::CreateRemoval(SR)}; 1660*06c3fb27SDimitry Andric } 1661*06c3fb27SDimitry Andric 1662*06c3fb27SDimitry Andric FixItList FixIts{}; 1663*06c3fb27SDimitry Andric std::string ExtentText = UserFillPlaceHolder.data(); 1664*06c3fb27SDimitry Andric StringRef One = "1"; 1665*06c3fb27SDimitry Andric 1666*06c3fb27SDimitry Andric // Insert `{` before `Init`: 1667*06c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{")); 1668*06c3fb27SDimitry Andric // Try to get the data extent. Break into different cases: 1669*06c3fb27SDimitry Andric if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) { 1670*06c3fb27SDimitry Andric // In cases `Init` is `new T[n]` and there is no explicit cast over 1671*06c3fb27SDimitry Andric // `Init`, we know that `Init` must evaluates to a pointer to `n` objects 1672*06c3fb27SDimitry Andric // of `T`. So the extent is `n` unless `n` has side effects. Similar but 1673*06c3fb27SDimitry Andric // simpler for the case where `Init` is `new T`. 1674*06c3fb27SDimitry Andric if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) { 1675*06c3fb27SDimitry Andric if (!Ext->HasSideEffects(Ctx)) { 1676*06c3fb27SDimitry Andric std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts); 1677*06c3fb27SDimitry Andric if (!ExtentString) 1678*06c3fb27SDimitry Andric return {}; 1679*06c3fb27SDimitry Andric ExtentText = *ExtentString; 1680*06c3fb27SDimitry Andric } 1681*06c3fb27SDimitry Andric } else if (!CxxNew->isArray()) 1682*06c3fb27SDimitry Andric // Although the initializer is not allocating a buffer, the pointer 1683*06c3fb27SDimitry Andric // variable could still be used in buffer access operations. 1684*06c3fb27SDimitry Andric ExtentText = One; 1685*06c3fb27SDimitry Andric } else if (const auto *CArrTy = Ctx.getAsConstantArrayType( 1686*06c3fb27SDimitry Andric Init->IgnoreImpCasts()->getType())) { 1687*06c3fb27SDimitry Andric // In cases `Init` is of an array type after stripping off implicit casts, 1688*06c3fb27SDimitry Andric // the extent is the array size. Note that if the array size is not a 1689*06c3fb27SDimitry Andric // constant, we cannot use it as the extent. 1690*06c3fb27SDimitry Andric ExtentText = getAPIntText(CArrTy->getSize()); 1691*06c3fb27SDimitry Andric } else { 1692*06c3fb27SDimitry Andric // In cases `Init` is of the form `&Var` after stripping of implicit 1693*06c3fb27SDimitry Andric // casts, where `&` is the built-in operator, the extent is 1. 1694*06c3fb27SDimitry Andric if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts())) 1695*06c3fb27SDimitry Andric if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf && 1696*06c3fb27SDimitry Andric isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr())) 1697*06c3fb27SDimitry Andric ExtentText = One; 1698*06c3fb27SDimitry Andric // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`, 1699*06c3fb27SDimitry Andric // and explicit casting, etc. etc. 1700*06c3fb27SDimitry Andric } 1701*06c3fb27SDimitry Andric 1702*06c3fb27SDimitry Andric SmallString<32> StrBuffer{}; 1703*06c3fb27SDimitry Andric std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts); 1704*06c3fb27SDimitry Andric 1705*06c3fb27SDimitry Andric if (!LocPassInit) 1706*06c3fb27SDimitry Andric return {}; 1707*06c3fb27SDimitry Andric 1708*06c3fb27SDimitry Andric StrBuffer.append(", "); 1709*06c3fb27SDimitry Andric StrBuffer.append(ExtentText); 1710*06c3fb27SDimitry Andric StrBuffer.append("}"); 1711*06c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str())); 1712*06c3fb27SDimitry Andric return FixIts; 1713*06c3fb27SDimitry Andric } 1714*06c3fb27SDimitry Andric 1715*06c3fb27SDimitry Andric // For a `VarDecl` of the form `T * var (= Init)?`, this 1716*06c3fb27SDimitry Andric // function generates a fix-it for the declaration, which re-declares `var` to 1717*06c3fb27SDimitry Andric // be of `span<T>` type and transforms the initializer, if present, to a span 1718*06c3fb27SDimitry Andric // constructor---`span<T> var {Init, Extent}`, where `Extent` may need the user 1719*06c3fb27SDimitry Andric // to fill in. 1720*06c3fb27SDimitry Andric // 1721*06c3fb27SDimitry Andric // FIXME: support Multi-level pointers 1722*06c3fb27SDimitry Andric // 1723*06c3fb27SDimitry Andric // Parameters: 1724*06c3fb27SDimitry Andric // `D` a pointer the variable declaration node 1725*06c3fb27SDimitry Andric // `Ctx` a reference to the ASTContext 1726*06c3fb27SDimitry Andric // Returns: 1727*06c3fb27SDimitry Andric // the generated fix-it 1728*06c3fb27SDimitry Andric static FixItList fixVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, 1729*06c3fb27SDimitry Andric const StringRef UserFillPlaceHolder) { 1730*06c3fb27SDimitry Andric const QualType &SpanEltT = D->getType()->getPointeeType(); 1731*06c3fb27SDimitry Andric assert(!SpanEltT.isNull() && "Trying to fix a non-pointer type variable!"); 1732*06c3fb27SDimitry Andric 1733*06c3fb27SDimitry Andric FixItList FixIts{}; 1734*06c3fb27SDimitry Andric std::optional<SourceLocation> 1735*06c3fb27SDimitry Andric ReplacementLastLoc; // the inclusive end location of the replacement 1736*06c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 1737*06c3fb27SDimitry Andric 1738*06c3fb27SDimitry Andric if (const Expr *Init = D->getInit()) { 1739*06c3fb27SDimitry Andric FixItList InitFixIts = 1740*06c3fb27SDimitry Andric populateInitializerFixItWithSpan(Init, Ctx, UserFillPlaceHolder); 1741*06c3fb27SDimitry Andric 1742*06c3fb27SDimitry Andric if (InitFixIts.empty()) 1743*06c3fb27SDimitry Andric return {}; 1744*06c3fb27SDimitry Andric 1745*06c3fb27SDimitry Andric // The loc right before the initializer: 1746*06c3fb27SDimitry Andric ReplacementLastLoc = Init->getBeginLoc().getLocWithOffset(-1); 1747*06c3fb27SDimitry Andric FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts.begin()), 1748*06c3fb27SDimitry Andric std::make_move_iterator(InitFixIts.end())); 1749*06c3fb27SDimitry Andric } else 1750*06c3fb27SDimitry Andric ReplacementLastLoc = getEndCharLoc(D, SM, Ctx.getLangOpts()); 1751*06c3fb27SDimitry Andric 1752*06c3fb27SDimitry Andric // Producing fix-it for variable declaration---make `D` to be of span type: 1753*06c3fb27SDimitry Andric SmallString<32> Replacement; 1754*06c3fb27SDimitry Andric raw_svector_ostream OS(Replacement); 1755*06c3fb27SDimitry Andric 1756*06c3fb27SDimitry Andric OS << "std::span<" << SpanEltT.getAsString() << "> " << D->getName(); 1757*06c3fb27SDimitry Andric 1758*06c3fb27SDimitry Andric if (!ReplacementLastLoc) 1759*06c3fb27SDimitry Andric return {}; 1760*06c3fb27SDimitry Andric 1761*06c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateReplacement( 1762*06c3fb27SDimitry Andric SourceRange{D->getBeginLoc(), *ReplacementLastLoc}, OS.str())); 1763*06c3fb27SDimitry Andric return FixIts; 1764*06c3fb27SDimitry Andric } 1765*06c3fb27SDimitry Andric 1766*06c3fb27SDimitry Andric static bool hasConflictingOverload(const FunctionDecl *FD) { 1767*06c3fb27SDimitry Andric return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult(); 1768*06c3fb27SDimitry Andric } 1769*06c3fb27SDimitry Andric 1770*06c3fb27SDimitry Andric // For a `FunDecl`, one of whose `ParmVarDecl`s is being changed to have a new 1771*06c3fb27SDimitry Andric // type, this function produces fix-its to make the change self-contained. Let 1772*06c3fb27SDimitry Andric // 'F' be the entity defined by the original `FunDecl` and "NewF" be the entity 1773*06c3fb27SDimitry Andric // defined by the `FunDecl` after the change to the parameter. Fix-its produced 1774*06c3fb27SDimitry Andric // by this function are 1775*06c3fb27SDimitry Andric // 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration 1776*06c3fb27SDimitry Andric // of 'F'; 1777*06c3fb27SDimitry Andric // 2. Create a declaration of "NewF" next to each declaration of `F`; 1778*06c3fb27SDimitry Andric // 3. Create a definition of "F" (as its' original definition is now belongs 1779*06c3fb27SDimitry Andric // to "NewF") next to its original definition. The body of the creating 1780*06c3fb27SDimitry Andric // definition calls to "NewF". 1781*06c3fb27SDimitry Andric // 1782*06c3fb27SDimitry Andric // Example: 1783*06c3fb27SDimitry Andric // 1784*06c3fb27SDimitry Andric // void f(int *p); // original declaration 1785*06c3fb27SDimitry Andric // void f(int *p) { // original definition 1786*06c3fb27SDimitry Andric // p[5]; 1787*06c3fb27SDimitry Andric // } 1788*06c3fb27SDimitry Andric // 1789*06c3fb27SDimitry Andric // To change the parameter `p` to be of `std::span<int>` type, we 1790*06c3fb27SDimitry Andric // also add overloads: 1791*06c3fb27SDimitry Andric // 1792*06c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl 1793*06c3fb27SDimitry Andric // void f(std::span<int> p); // added overload decl 1794*06c3fb27SDimitry Andric // void f(std::span<int> p) { // original def where param is changed 1795*06c3fb27SDimitry Andric // p[5]; 1796*06c3fb27SDimitry Andric // } 1797*06c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p) { // added def 1798*06c3fb27SDimitry Andric // return f(std::span(p, <# size #>)); 1799*06c3fb27SDimitry Andric // } 1800*06c3fb27SDimitry Andric // 1801*06c3fb27SDimitry Andric // The actual fix-its may contain more details, e.g., the attribute may be guard 1802*06c3fb27SDimitry Andric // by a macro 1803*06c3fb27SDimitry Andric // #if __has_cpp_attribute(clang::unsafe_buffer_usage) 1804*06c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] 1805*06c3fb27SDimitry Andric // #endif 1806*06c3fb27SDimitry Andric // 1807*06c3fb27SDimitry Andric // `NewTypeText` is the string representation of the new type, to which the 1808*06c3fb27SDimitry Andric // parameter indexed by `ParmIdx` is being changed. 1809*06c3fb27SDimitry Andric static std::optional<FixItList> 1810*06c3fb27SDimitry Andric createOverloadsForFixedParams(unsigned ParmIdx, StringRef NewTyText, 1811*06c3fb27SDimitry Andric const FunctionDecl *FD, const ASTContext &Ctx, 1812*06c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 1813*06c3fb27SDimitry Andric // FIXME: need to make this conflict checking better: 1814*06c3fb27SDimitry Andric if (hasConflictingOverload(FD)) 1815*06c3fb27SDimitry Andric return std::nullopt; 1816*06c3fb27SDimitry Andric 1817*06c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 1818*06c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 1819*06c3fb27SDimitry Andric // FIXME Respect indentation of the original code. 1820*06c3fb27SDimitry Andric 1821*06c3fb27SDimitry Andric // A lambda that creates the text representation of a function declaration 1822*06c3fb27SDimitry Andric // with the new type signature: 1823*06c3fb27SDimitry Andric const auto NewOverloadSignatureCreator = 1824*06c3fb27SDimitry Andric [&SM, &LangOpts](const FunctionDecl *FD, unsigned ParmIdx, 1825*06c3fb27SDimitry Andric StringRef NewTypeText) -> std::optional<std::string> { 1826*06c3fb27SDimitry Andric std::stringstream SS; 1827*06c3fb27SDimitry Andric 1828*06c3fb27SDimitry Andric SS << ";"; 1829*06c3fb27SDimitry Andric SS << getEndOfLine().str(); 1830*06c3fb27SDimitry Andric // Append: ret-type func-name "(" 1831*06c3fb27SDimitry Andric if (auto Prefix = getRangeText( 1832*06c3fb27SDimitry Andric SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()), 1833*06c3fb27SDimitry Andric SM, LangOpts)) 1834*06c3fb27SDimitry Andric SS << Prefix->str(); 1835*06c3fb27SDimitry Andric else 1836*06c3fb27SDimitry Andric return std::nullopt; // give up 1837*06c3fb27SDimitry Andric // Append: parameter-type-list 1838*06c3fb27SDimitry Andric const unsigned NumParms = FD->getNumParams(); 1839*06c3fb27SDimitry Andric 1840*06c3fb27SDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 1841*06c3fb27SDimitry Andric const ParmVarDecl *Parm = FD->getParamDecl(i); 1842*06c3fb27SDimitry Andric 1843*06c3fb27SDimitry Andric if (Parm->isImplicit()) 1844*06c3fb27SDimitry Andric continue; 1845*06c3fb27SDimitry Andric if (i == ParmIdx) { 1846*06c3fb27SDimitry Andric SS << NewTypeText.str(); 1847*06c3fb27SDimitry Andric // print parameter name if provided: 1848*06c3fb27SDimitry Andric if (IdentifierInfo * II = Parm->getIdentifier()) 1849*06c3fb27SDimitry Andric SS << " " << II->getName().str(); 1850*06c3fb27SDimitry Andric } else if (auto ParmTypeText = 1851*06c3fb27SDimitry Andric getRangeText(Parm->getSourceRange(), SM, LangOpts)) { 1852*06c3fb27SDimitry Andric // print the whole `Parm` without modification: 1853*06c3fb27SDimitry Andric SS << ParmTypeText->str(); 1854*06c3fb27SDimitry Andric } else 1855*06c3fb27SDimitry Andric return std::nullopt; // something wrong, give up 1856*06c3fb27SDimitry Andric if (i != NumParms - 1) 1857*06c3fb27SDimitry Andric SS << ", "; 1858*06c3fb27SDimitry Andric } 1859*06c3fb27SDimitry Andric SS << ")"; 1860*06c3fb27SDimitry Andric return SS.str(); 1861*06c3fb27SDimitry Andric }; 1862*06c3fb27SDimitry Andric 1863*06c3fb27SDimitry Andric // A lambda that creates the text representation of a function definition with 1864*06c3fb27SDimitry Andric // the original signature: 1865*06c3fb27SDimitry Andric const auto OldOverloadDefCreator = 1866*06c3fb27SDimitry Andric [&SM, &Handler, 1867*06c3fb27SDimitry Andric &LangOpts](const FunctionDecl *FD, unsigned ParmIdx, 1868*06c3fb27SDimitry Andric StringRef NewTypeText) -> std::optional<std::string> { 1869*06c3fb27SDimitry Andric std::stringstream SS; 1870*06c3fb27SDimitry Andric 1871*06c3fb27SDimitry Andric SS << getEndOfLine().str(); 1872*06c3fb27SDimitry Andric // Append: attr-name ret-type func-name "(" param-list ")" "{" 1873*06c3fb27SDimitry Andric if (auto FDPrefix = getRangeText( 1874*06c3fb27SDimitry Andric SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM, 1875*06c3fb27SDimitry Andric LangOpts)) 1876*06c3fb27SDimitry Andric SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ") 1877*06c3fb27SDimitry Andric << FDPrefix->str() << "{"; 1878*06c3fb27SDimitry Andric else 1879*06c3fb27SDimitry Andric return std::nullopt; 1880*06c3fb27SDimitry Andric // Append: "return" func-name "(" 1881*06c3fb27SDimitry Andric if (auto FunQualName = getFunNameText(FD, SM, LangOpts)) 1882*06c3fb27SDimitry Andric SS << "return " << FunQualName->str() << "("; 1883*06c3fb27SDimitry Andric else 1884*06c3fb27SDimitry Andric return std::nullopt; 1885*06c3fb27SDimitry Andric 1886*06c3fb27SDimitry Andric // Append: arg-list 1887*06c3fb27SDimitry Andric const unsigned NumParms = FD->getNumParams(); 1888*06c3fb27SDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 1889*06c3fb27SDimitry Andric const ParmVarDecl *Parm = FD->getParamDecl(i); 1890*06c3fb27SDimitry Andric 1891*06c3fb27SDimitry Andric if (Parm->isImplicit()) 1892*06c3fb27SDimitry Andric continue; 1893*06c3fb27SDimitry Andric // FIXME: If a parameter has no name, it is unused in the 1894*06c3fb27SDimitry Andric // definition. So we could just leave it as it is. 1895*06c3fb27SDimitry Andric if (!Parm->getIdentifier()) 1896*06c3fb27SDimitry Andric // If a parameter of a function definition has no name: 1897*06c3fb27SDimitry Andric return std::nullopt; 1898*06c3fb27SDimitry Andric if (i == ParmIdx) 1899*06c3fb27SDimitry Andric // This is our spanified paramter! 1900*06c3fb27SDimitry Andric SS << NewTypeText.str() << "(" << Parm->getIdentifier()->getName().str() << ", " 1901*06c3fb27SDimitry Andric << getUserFillPlaceHolder("size") << ")"; 1902*06c3fb27SDimitry Andric else 1903*06c3fb27SDimitry Andric SS << Parm->getIdentifier()->getName().str(); 1904*06c3fb27SDimitry Andric if (i != NumParms - 1) 1905*06c3fb27SDimitry Andric SS << ", "; 1906*06c3fb27SDimitry Andric } 1907*06c3fb27SDimitry Andric // finish call and the body 1908*06c3fb27SDimitry Andric SS << ");}" << getEndOfLine().str(); 1909*06c3fb27SDimitry Andric // FIXME: 80-char line formatting? 1910*06c3fb27SDimitry Andric return SS.str(); 1911*06c3fb27SDimitry Andric }; 1912*06c3fb27SDimitry Andric 1913*06c3fb27SDimitry Andric FixItList FixIts{}; 1914*06c3fb27SDimitry Andric for (FunctionDecl *FReDecl : FD->redecls()) { 1915*06c3fb27SDimitry Andric std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts); 1916*06c3fb27SDimitry Andric 1917*06c3fb27SDimitry Andric if (!Loc) 1918*06c3fb27SDimitry Andric return {}; 1919*06c3fb27SDimitry Andric if (FReDecl->isThisDeclarationADefinition()) { 1920*06c3fb27SDimitry Andric assert(FReDecl == FD && "inconsistent function definition"); 1921*06c3fb27SDimitry Andric // Inserts a definition with the old signature to the end of 1922*06c3fb27SDimitry Andric // `FReDecl`: 1923*06c3fb27SDimitry Andric if (auto OldOverloadDef = 1924*06c3fb27SDimitry Andric OldOverloadDefCreator(FReDecl, ParmIdx, NewTyText)) 1925*06c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef)); 1926*06c3fb27SDimitry Andric else 1927*06c3fb27SDimitry Andric return {}; // give up 1928*06c3fb27SDimitry Andric } else { 1929*06c3fb27SDimitry Andric // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`: 1930*06c3fb27SDimitry Andric if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) { 1931*06c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion( 1932*06c3fb27SDimitry Andric FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt( 1933*06c3fb27SDimitry Andric FReDecl->getBeginLoc(), " "))); 1934*06c3fb27SDimitry Andric } 1935*06c3fb27SDimitry Andric // Inserts a declaration with the new signature to the end of `FReDecl`: 1936*06c3fb27SDimitry Andric if (auto NewOverloadDecl = 1937*06c3fb27SDimitry Andric NewOverloadSignatureCreator(FReDecl, ParmIdx, NewTyText)) 1938*06c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl)); 1939*06c3fb27SDimitry Andric else 1940*06c3fb27SDimitry Andric return {}; 1941*06c3fb27SDimitry Andric } 1942*06c3fb27SDimitry Andric } 1943*06c3fb27SDimitry Andric return FixIts; 1944*06c3fb27SDimitry Andric } 1945*06c3fb27SDimitry Andric 1946*06c3fb27SDimitry Andric // To fix a `ParmVarDecl` to be of `std::span` type. In addition, creating a 1947*06c3fb27SDimitry Andric // new overload of the function so that the change is self-contained (see 1948*06c3fb27SDimitry Andric // `createOverloadsForFixedParams`). 1949*06c3fb27SDimitry Andric static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, 1950*06c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 1951*06c3fb27SDimitry Andric if (PVD->hasDefaultArg()) 1952*06c3fb27SDimitry Andric // FIXME: generate fix-its for default values: 1953*06c3fb27SDimitry Andric return {}; 1954*06c3fb27SDimitry Andric assert(PVD->getType()->isPointerType()); 1955*06c3fb27SDimitry Andric auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext()); 1956*06c3fb27SDimitry Andric 1957*06c3fb27SDimitry Andric if (!FD) 1958*06c3fb27SDimitry Andric return {}; 1959*06c3fb27SDimitry Andric 1960*06c3fb27SDimitry Andric std::optional<Qualifiers> PteTyQualifiers = std::nullopt; 1961*06c3fb27SDimitry Andric std::optional<std::string> PteTyText = getPointeeTypeText( 1962*06c3fb27SDimitry Andric PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers); 1963*06c3fb27SDimitry Andric 1964*06c3fb27SDimitry Andric if (!PteTyText) 1965*06c3fb27SDimitry Andric return {}; 1966*06c3fb27SDimitry Andric 1967*06c3fb27SDimitry Andric std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName(); 1968*06c3fb27SDimitry Andric 1969*06c3fb27SDimitry Andric if (!PVDNameText) 1970*06c3fb27SDimitry Andric return {}; 1971*06c3fb27SDimitry Andric 1972*06c3fb27SDimitry Andric std::string SpanOpen = "std::span<"; 1973*06c3fb27SDimitry Andric std::string SpanClose = ">"; 1974*06c3fb27SDimitry Andric std::string SpanTyText; 1975*06c3fb27SDimitry Andric std::stringstream SS; 1976*06c3fb27SDimitry Andric 1977*06c3fb27SDimitry Andric SS << SpanOpen << *PteTyText; 1978*06c3fb27SDimitry Andric // Append qualifiers to span element type: 1979*06c3fb27SDimitry Andric if (PteTyQualifiers) 1980*06c3fb27SDimitry Andric SS << " " << PteTyQualifiers->getAsString(); 1981*06c3fb27SDimitry Andric SS << SpanClose; 1982*06c3fb27SDimitry Andric // Save the Span type text: 1983*06c3fb27SDimitry Andric SpanTyText = SS.str(); 1984*06c3fb27SDimitry Andric // Append qualifiers to the type of the parameter: 1985*06c3fb27SDimitry Andric if (PVD->getType().hasQualifiers()) 1986*06c3fb27SDimitry Andric SS << " " << PVD->getType().getQualifiers().getAsString(); 1987*06c3fb27SDimitry Andric // Append parameter's name: 1988*06c3fb27SDimitry Andric SS << " " << PVDNameText->str(); 1989*06c3fb27SDimitry Andric 1990*06c3fb27SDimitry Andric FixItList Fixes; 1991*06c3fb27SDimitry Andric unsigned ParmIdx = 0; 1992*06c3fb27SDimitry Andric 1993*06c3fb27SDimitry Andric Fixes.push_back( 1994*06c3fb27SDimitry Andric FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())); 1995*06c3fb27SDimitry Andric for (auto *ParmIter : FD->parameters()) { 1996*06c3fb27SDimitry Andric if (PVD == ParmIter) 1997*06c3fb27SDimitry Andric break; 1998*06c3fb27SDimitry Andric ParmIdx++; 1999*06c3fb27SDimitry Andric } 2000*06c3fb27SDimitry Andric if (ParmIdx < FD->getNumParams()) 2001*06c3fb27SDimitry Andric if (auto OverloadFix = createOverloadsForFixedParams(ParmIdx, SpanTyText, 2002*06c3fb27SDimitry Andric FD, Ctx, Handler)) { 2003*06c3fb27SDimitry Andric Fixes.append(*OverloadFix); 2004*06c3fb27SDimitry Andric return Fixes; 2005*06c3fb27SDimitry Andric } 2006*06c3fb27SDimitry Andric return {}; 2007*06c3fb27SDimitry Andric } 2008*06c3fb27SDimitry Andric 2009*06c3fb27SDimitry Andric static FixItList fixVariableWithSpan(const VarDecl *VD, 2010*06c3fb27SDimitry Andric const DeclUseTracker &Tracker, 2011*06c3fb27SDimitry Andric ASTContext &Ctx, 2012*06c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 2013*06c3fb27SDimitry Andric const DeclStmt *DS = Tracker.lookupDecl(VD); 2014*06c3fb27SDimitry Andric assert(DS && "Fixing non-local variables not implemented yet!"); 2015*06c3fb27SDimitry Andric if (!DS->isSingleDecl()) { 2016*06c3fb27SDimitry Andric // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` 2017*06c3fb27SDimitry Andric return {}; 2018*06c3fb27SDimitry Andric } 2019*06c3fb27SDimitry Andric // Currently DS is an unused variable but we'll need it when 2020*06c3fb27SDimitry Andric // non-single decls are implemented, where the pointee type name 2021*06c3fb27SDimitry Andric // and the '*' are spread around the place. 2022*06c3fb27SDimitry Andric (void)DS; 2023*06c3fb27SDimitry Andric 2024*06c3fb27SDimitry Andric // FIXME: handle cases where DS has multiple declarations 2025*06c3fb27SDimitry Andric return fixVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder()); 2026*06c3fb27SDimitry Andric } 2027*06c3fb27SDimitry Andric 2028*06c3fb27SDimitry Andric // TODO: we should be consistent to use `std::nullopt` to represent no-fix due 2029*06c3fb27SDimitry Andric // to any unexpected problem. 2030*06c3fb27SDimitry Andric static FixItList 2031*06c3fb27SDimitry Andric fixVariable(const VarDecl *VD, Strategy::Kind K, 2032*06c3fb27SDimitry Andric /* The function decl under analysis */ const Decl *D, 2033*06c3fb27SDimitry Andric const DeclUseTracker &Tracker, ASTContext &Ctx, 2034*06c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 2035*06c3fb27SDimitry Andric if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) { 2036*06c3fb27SDimitry Andric auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext()); 2037*06c3fb27SDimitry Andric if (!FD || FD != D) 2038*06c3fb27SDimitry Andric // `FD != D` means that `PVD` belongs to a function that is not being 2039*06c3fb27SDimitry Andric // analyzed currently. Thus `FD` may not be complete. 2040*06c3fb27SDimitry Andric return {}; 2041*06c3fb27SDimitry Andric 2042*06c3fb27SDimitry Andric // TODO If function has a try block we can't change params unless we check 2043*06c3fb27SDimitry Andric // also its catch block for their use. 2044*06c3fb27SDimitry Andric // FIXME We might support static class methods, some select methods, 2045*06c3fb27SDimitry Andric // operators and possibly lamdas. 2046*06c3fb27SDimitry Andric if (FD->isMain() || FD->isConstexpr() || 2047*06c3fb27SDimitry Andric FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate || 2048*06c3fb27SDimitry Andric FD->isVariadic() || 2049*06c3fb27SDimitry Andric // also covers call-operator of lamdas 2050*06c3fb27SDimitry Andric isa<CXXMethodDecl>(FD) || 2051*06c3fb27SDimitry Andric // skip when the function body is a try-block 2052*06c3fb27SDimitry Andric (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) || 2053*06c3fb27SDimitry Andric FD->isOverloadedOperator()) 2054*06c3fb27SDimitry Andric return {}; // TODO test all these cases 2055*06c3fb27SDimitry Andric } 2056*06c3fb27SDimitry Andric 2057*06c3fb27SDimitry Andric switch (K) { 2058*06c3fb27SDimitry Andric case Strategy::Kind::Span: { 2059*06c3fb27SDimitry Andric if (VD->getType()->isPointerType()) { 2060*06c3fb27SDimitry Andric if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) 2061*06c3fb27SDimitry Andric return fixParamWithSpan(PVD, Ctx, Handler); 2062*06c3fb27SDimitry Andric 2063*06c3fb27SDimitry Andric if (VD->isLocalVarDecl()) 2064*06c3fb27SDimitry Andric return fixVariableWithSpan(VD, Tracker, Ctx, Handler); 2065*06c3fb27SDimitry Andric } 2066*06c3fb27SDimitry Andric return {}; 2067*06c3fb27SDimitry Andric } 2068*06c3fb27SDimitry Andric case Strategy::Kind::Iterator: 2069*06c3fb27SDimitry Andric case Strategy::Kind::Array: 2070*06c3fb27SDimitry Andric case Strategy::Kind::Vector: 2071*06c3fb27SDimitry Andric llvm_unreachable("Strategy not implemented yet!"); 2072*06c3fb27SDimitry Andric case Strategy::Kind::Wontfix: 2073*06c3fb27SDimitry Andric llvm_unreachable("Invalid strategy!"); 2074*06c3fb27SDimitry Andric } 2075*06c3fb27SDimitry Andric llvm_unreachable("Unknown strategy!"); 2076*06c3fb27SDimitry Andric } 2077*06c3fb27SDimitry Andric 2078*06c3fb27SDimitry Andric // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the 2079*06c3fb27SDimitry Andric // `RemoveRange` of 'h' overlaps with a macro use. 2080*06c3fb27SDimitry Andric static bool overlapWithMacro(const FixItList &FixIts) { 2081*06c3fb27SDimitry Andric // FIXME: For now we only check if the range (or the first token) is (part of) 2082*06c3fb27SDimitry Andric // a macro expansion. Ideally, we want to check for all tokens in the range. 2083*06c3fb27SDimitry Andric return llvm::any_of(FixIts, [](const FixItHint &Hint) { 2084*06c3fb27SDimitry Andric auto Range = Hint.RemoveRange; 2085*06c3fb27SDimitry Andric if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID()) 2086*06c3fb27SDimitry Andric // If the range (or the first token) is (part of) a macro expansion: 2087*06c3fb27SDimitry Andric return true; 2088*06c3fb27SDimitry Andric return false; 2089*06c3fb27SDimitry Andric }); 2090*06c3fb27SDimitry Andric } 2091*06c3fb27SDimitry Andric 2092*06c3fb27SDimitry Andric static bool impossibleToFixForVar(const FixableGadgetSets &FixablesForAllVars, 2093*06c3fb27SDimitry Andric const Strategy &S, 2094*06c3fb27SDimitry Andric const VarDecl * Var) { 2095*06c3fb27SDimitry Andric for (const auto &F : FixablesForAllVars.byVar.find(Var)->second) { 2096*06c3fb27SDimitry Andric std::optional<FixItList> Fixits = F->getFixits(S); 2097*06c3fb27SDimitry Andric if (!Fixits) { 2098*06c3fb27SDimitry Andric return true; 2099*06c3fb27SDimitry Andric } 2100*06c3fb27SDimitry Andric } 2101*06c3fb27SDimitry Andric return false; 2102*06c3fb27SDimitry Andric } 2103*06c3fb27SDimitry Andric 2104bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList> 2105*06c3fb27SDimitry Andric getFixIts(FixableGadgetSets &FixablesForAllVars, const Strategy &S, 2106*06c3fb27SDimitry Andric ASTContext &Ctx, 2107*06c3fb27SDimitry Andric /* The function decl under analysis */ const Decl *D, 2108*06c3fb27SDimitry Andric const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, 2109*06c3fb27SDimitry Andric const DefMapTy &VarGrpMap) { 2110bdd1243dSDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariable; 2111*06c3fb27SDimitry Andric for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) { 2112*06c3fb27SDimitry Andric FixItsForVariable[VD] = 2113*06c3fb27SDimitry Andric fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler); 2114*06c3fb27SDimitry Andric // If we fail to produce Fix-It for the declaration we have to skip the 2115*06c3fb27SDimitry Andric // variable entirely. 2116*06c3fb27SDimitry Andric if (FixItsForVariable[VD].empty()) { 2117*06c3fb27SDimitry Andric FixItsForVariable.erase(VD); 2118*06c3fb27SDimitry Andric continue; 2119*06c3fb27SDimitry Andric } 2120bdd1243dSDimitry Andric bool ImpossibleToFix = false; 2121bdd1243dSDimitry Andric llvm::SmallVector<FixItHint, 16> FixItsForVD; 2122bdd1243dSDimitry Andric for (const auto &F : Fixables) { 2123*06c3fb27SDimitry Andric std::optional<FixItList> Fixits = F->getFixits(S); 2124bdd1243dSDimitry Andric if (!Fixits) { 2125bdd1243dSDimitry Andric ImpossibleToFix = true; 2126bdd1243dSDimitry Andric break; 2127bdd1243dSDimitry Andric } else { 2128bdd1243dSDimitry Andric const FixItList CorrectFixes = Fixits.value(); 2129bdd1243dSDimitry Andric FixItsForVD.insert(FixItsForVD.end(), CorrectFixes.begin(), 2130bdd1243dSDimitry Andric CorrectFixes.end()); 2131bdd1243dSDimitry Andric } 2132bdd1243dSDimitry Andric } 2133*06c3fb27SDimitry Andric 2134*06c3fb27SDimitry Andric if (ImpossibleToFix) { 2135bdd1243dSDimitry Andric FixItsForVariable.erase(VD); 2136*06c3fb27SDimitry Andric continue; 2137*06c3fb27SDimitry Andric } 2138*06c3fb27SDimitry Andric 2139*06c3fb27SDimitry Andric const auto VarGroupForVD = VarGrpMap.find(VD); 2140*06c3fb27SDimitry Andric if (VarGroupForVD != VarGrpMap.end()) { 2141*06c3fb27SDimitry Andric for (const VarDecl * V : VarGroupForVD->second) { 2142*06c3fb27SDimitry Andric if (V == VD) { 2143*06c3fb27SDimitry Andric continue; 2144*06c3fb27SDimitry Andric } 2145*06c3fb27SDimitry Andric if (impossibleToFixForVar(FixablesForAllVars, S, V)) { 2146*06c3fb27SDimitry Andric ImpossibleToFix = true; 2147*06c3fb27SDimitry Andric break; 2148*06c3fb27SDimitry Andric } 2149*06c3fb27SDimitry Andric } 2150*06c3fb27SDimitry Andric 2151*06c3fb27SDimitry Andric if (ImpossibleToFix) { 2152*06c3fb27SDimitry Andric FixItsForVariable.erase(VD); 2153*06c3fb27SDimitry Andric for (const VarDecl * V : VarGroupForVD->second) { 2154*06c3fb27SDimitry Andric FixItsForVariable.erase(V); 2155*06c3fb27SDimitry Andric } 2156*06c3fb27SDimitry Andric continue; 2157*06c3fb27SDimitry Andric } 2158*06c3fb27SDimitry Andric } 2159bdd1243dSDimitry Andric FixItsForVariable[VD].insert(FixItsForVariable[VD].end(), 2160bdd1243dSDimitry Andric FixItsForVD.begin(), FixItsForVD.end()); 2161*06c3fb27SDimitry Andric 2162*06c3fb27SDimitry Andric // Fix-it shall not overlap with macros or/and templates: 2163*06c3fb27SDimitry Andric if (overlapWithMacro(FixItsForVariable[VD]) || 2164*06c3fb27SDimitry Andric clang::internal::anyConflict(FixItsForVariable[VD], 2165*06c3fb27SDimitry Andric Ctx.getSourceManager())) { 2166*06c3fb27SDimitry Andric FixItsForVariable.erase(VD); 2167*06c3fb27SDimitry Andric continue; 2168*06c3fb27SDimitry Andric } 2169*06c3fb27SDimitry Andric } 2170*06c3fb27SDimitry Andric 2171*06c3fb27SDimitry Andric for (auto VD : FixItsForVariable) { 2172*06c3fb27SDimitry Andric const auto VarGroupForVD = VarGrpMap.find(VD.first); 2173*06c3fb27SDimitry Andric const Strategy::Kind ReplacementTypeForVD = S.lookup(VD.first); 2174*06c3fb27SDimitry Andric if (VarGroupForVD != VarGrpMap.end()) { 2175*06c3fb27SDimitry Andric for (const VarDecl * Var : VarGroupForVD->second) { 2176*06c3fb27SDimitry Andric if (Var == VD.first) { 2177*06c3fb27SDimitry Andric continue; 2178*06c3fb27SDimitry Andric } 2179*06c3fb27SDimitry Andric 2180*06c3fb27SDimitry Andric FixItList GroupFix; 2181*06c3fb27SDimitry Andric if (FixItsForVariable.find(Var) == FixItsForVariable.end()) { 2182*06c3fb27SDimitry Andric GroupFix = fixVariable(Var, ReplacementTypeForVD, D, Tracker, 2183*06c3fb27SDimitry Andric Var->getASTContext(), Handler); 2184*06c3fb27SDimitry Andric } else { 2185*06c3fb27SDimitry Andric GroupFix = FixItsForVariable[Var]; 2186*06c3fb27SDimitry Andric } 2187*06c3fb27SDimitry Andric 2188*06c3fb27SDimitry Andric for (auto Fix : GroupFix) { 2189*06c3fb27SDimitry Andric FixItsForVariable[VD.first].push_back(Fix); 2190*06c3fb27SDimitry Andric } 2191*06c3fb27SDimitry Andric } 2192*06c3fb27SDimitry Andric } 2193bdd1243dSDimitry Andric } 2194bdd1243dSDimitry Andric return FixItsForVariable; 2195bdd1243dSDimitry Andric } 2196bdd1243dSDimitry Andric 2197*06c3fb27SDimitry Andric 2198bdd1243dSDimitry Andric static Strategy 2199bdd1243dSDimitry Andric getNaiveStrategy(const llvm::SmallVectorImpl<const VarDecl *> &UnsafeVars) { 2200bdd1243dSDimitry Andric Strategy S; 2201bdd1243dSDimitry Andric for (const VarDecl *VD : UnsafeVars) { 2202bdd1243dSDimitry Andric S.set(VD, Strategy::Kind::Span); 2203bdd1243dSDimitry Andric } 2204bdd1243dSDimitry Andric return S; 2205bdd1243dSDimitry Andric } 2206bdd1243dSDimitry Andric 2207bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D, 2208*06c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler, 2209*06c3fb27SDimitry Andric bool EmitSuggestions) { 2210bdd1243dSDimitry Andric assert(D && D->getBody()); 2211bdd1243dSDimitry Andric 2212*06c3fb27SDimitry Andric // We do not want to visit a Lambda expression defined inside a method independently. 2213*06c3fb27SDimitry Andric // Instead, it should be visited along with the outer method. 2214*06c3fb27SDimitry Andric if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) { 2215*06c3fb27SDimitry Andric if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass()) 2216*06c3fb27SDimitry Andric return; 2217bdd1243dSDimitry Andric } 2218bdd1243dSDimitry Andric 2219*06c3fb27SDimitry Andric // Do not emit fixit suggestions for functions declared in an 2220*06c3fb27SDimitry Andric // extern "C" block. 2221*06c3fb27SDimitry Andric if (const auto *FD = dyn_cast<FunctionDecl>(D)) { 2222*06c3fb27SDimitry Andric for (FunctionDecl *FReDecl : FD->redecls()) { 2223*06c3fb27SDimitry Andric if (FReDecl->isExternC()) { 2224*06c3fb27SDimitry Andric EmitSuggestions = false; 2225*06c3fb27SDimitry Andric break; 2226*06c3fb27SDimitry Andric } 2227*06c3fb27SDimitry Andric } 2228*06c3fb27SDimitry Andric } 2229*06c3fb27SDimitry Andric 2230*06c3fb27SDimitry Andric WarningGadgetSets UnsafeOps; 2231*06c3fb27SDimitry Andric FixableGadgetSets FixablesForAllVars; 2232*06c3fb27SDimitry Andric 2233*06c3fb27SDimitry Andric auto [FixableGadgets, WarningGadgets, Tracker] = 2234*06c3fb27SDimitry Andric findGadgets(D, Handler, EmitSuggestions); 2235*06c3fb27SDimitry Andric 2236*06c3fb27SDimitry Andric if (!EmitSuggestions) { 2237*06c3fb27SDimitry Andric // Our job is very easy without suggestions. Just warn about 2238*06c3fb27SDimitry Andric // every problematic operation and consider it done. No need to deal 2239*06c3fb27SDimitry Andric // with fixable gadgets, no need to group operations by variable. 2240*06c3fb27SDimitry Andric for (const auto &G : WarningGadgets) { 2241*06c3fb27SDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), 2242*06c3fb27SDimitry Andric /*IsRelatedToDecl=*/false); 2243*06c3fb27SDimitry Andric } 2244*06c3fb27SDimitry Andric 2245*06c3fb27SDimitry Andric // This return guarantees that most of the machine doesn't run when 2246*06c3fb27SDimitry Andric // suggestions aren't requested. 2247*06c3fb27SDimitry Andric assert(FixableGadgets.size() == 0 && 2248*06c3fb27SDimitry Andric "Fixable gadgets found but suggestions not requested!"); 2249*06c3fb27SDimitry Andric return; 2250*06c3fb27SDimitry Andric } 2251*06c3fb27SDimitry Andric 2252*06c3fb27SDimitry Andric UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); 2253*06c3fb27SDimitry Andric FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets)); 2254*06c3fb27SDimitry Andric 2255*06c3fb27SDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariableGroup; 2256*06c3fb27SDimitry Andric DefMapTy VariableGroupsMap{}; 2257*06c3fb27SDimitry Andric 2258bdd1243dSDimitry Andric // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. 2259*06c3fb27SDimitry Andric for (auto it = FixablesForAllVars.byVar.cbegin(); 2260*06c3fb27SDimitry Andric it != FixablesForAllVars.byVar.cend();) { 2261*06c3fb27SDimitry Andric // FIXME: need to deal with global variables later 2262*06c3fb27SDimitry Andric if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first)) || 2263*06c3fb27SDimitry Andric Tracker.hasUnclaimedUses(it->first) || it->first->isInitCapture()) { 2264*06c3fb27SDimitry Andric it = FixablesForAllVars.byVar.erase(it); 2265bdd1243dSDimitry Andric } else { 2266bdd1243dSDimitry Andric ++it; 2267bdd1243dSDimitry Andric } 2268bdd1243dSDimitry Andric } 2269bdd1243dSDimitry Andric 2270bdd1243dSDimitry Andric llvm::SmallVector<const VarDecl *, 16> UnsafeVars; 2271*06c3fb27SDimitry Andric for (const auto &[VD, ignore] : FixablesForAllVars.byVar) 2272bdd1243dSDimitry Andric UnsafeVars.push_back(VD); 2273bdd1243dSDimitry Andric 2274*06c3fb27SDimitry Andric // Fixpoint iteration for pointer assignments 2275*06c3fb27SDimitry Andric using DepMapTy = DenseMap<const VarDecl *, std::set<const VarDecl *>>; 2276*06c3fb27SDimitry Andric DepMapTy DependenciesMap{}; 2277*06c3fb27SDimitry Andric DepMapTy PtrAssignmentGraph{}; 2278*06c3fb27SDimitry Andric 2279*06c3fb27SDimitry Andric for (auto it : FixablesForAllVars.byVar) { 2280*06c3fb27SDimitry Andric for (const FixableGadget *fixable : it.second) { 2281*06c3fb27SDimitry Andric std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair = 2282*06c3fb27SDimitry Andric fixable->getStrategyImplications(); 2283*06c3fb27SDimitry Andric if (ImplPair) { 2284*06c3fb27SDimitry Andric std::pair<const VarDecl *, const VarDecl *> Impl = ImplPair.value(); 2285*06c3fb27SDimitry Andric PtrAssignmentGraph[Impl.first].insert(Impl.second); 2286*06c3fb27SDimitry Andric } 2287*06c3fb27SDimitry Andric } 2288*06c3fb27SDimitry Andric } 2289*06c3fb27SDimitry Andric 2290*06c3fb27SDimitry Andric /* 2291*06c3fb27SDimitry Andric The following code does a BFS traversal of the `PtrAssignmentGraph` 2292*06c3fb27SDimitry Andric considering all unsafe vars as starting nodes and constructs an undirected 2293*06c3fb27SDimitry Andric graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner 2294*06c3fb27SDimitry Andric elimiates all variables that are unreachable from any unsafe var. In other 2295*06c3fb27SDimitry Andric words, this removes all dependencies that don't include any unsafe variable 2296*06c3fb27SDimitry Andric and consequently don't need any fixit generation. 2297*06c3fb27SDimitry Andric Note: A careful reader would observe that the code traverses 2298*06c3fb27SDimitry Andric `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and 2299*06c3fb27SDimitry Andric `Adj` and not between `CurrentVar` and `Adj`. Both approaches would 2300*06c3fb27SDimitry Andric achieve the same result but the one used here dramatically cuts the 2301*06c3fb27SDimitry Andric amount of hoops the second part of the algorithm needs to jump, given that 2302*06c3fb27SDimitry Andric a lot of these connections become "direct". The reader is advised not to 2303*06c3fb27SDimitry Andric imagine how the graph is transformed because of using `Var` instead of 2304*06c3fb27SDimitry Andric `CurrentVar`. The reader can continue reading as if `CurrentVar` was used, 2305*06c3fb27SDimitry Andric and think about why it's equivalent later. 2306*06c3fb27SDimitry Andric */ 2307*06c3fb27SDimitry Andric std::set<const VarDecl *> VisitedVarsDirected{}; 2308*06c3fb27SDimitry Andric for (const auto &[Var, ignore] : UnsafeOps.byVar) { 2309*06c3fb27SDimitry Andric if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) { 2310*06c3fb27SDimitry Andric 2311*06c3fb27SDimitry Andric std::queue<const VarDecl*> QueueDirected{}; 2312*06c3fb27SDimitry Andric QueueDirected.push(Var); 2313*06c3fb27SDimitry Andric while(!QueueDirected.empty()) { 2314*06c3fb27SDimitry Andric const VarDecl* CurrentVar = QueueDirected.front(); 2315*06c3fb27SDimitry Andric QueueDirected.pop(); 2316*06c3fb27SDimitry Andric VisitedVarsDirected.insert(CurrentVar); 2317*06c3fb27SDimitry Andric auto AdjacentNodes = PtrAssignmentGraph[CurrentVar]; 2318*06c3fb27SDimitry Andric for (const VarDecl *Adj : AdjacentNodes) { 2319*06c3fb27SDimitry Andric if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) { 2320*06c3fb27SDimitry Andric QueueDirected.push(Adj); 2321*06c3fb27SDimitry Andric } 2322*06c3fb27SDimitry Andric DependenciesMap[Var].insert(Adj); 2323*06c3fb27SDimitry Andric DependenciesMap[Adj].insert(Var); 2324*06c3fb27SDimitry Andric } 2325*06c3fb27SDimitry Andric } 2326*06c3fb27SDimitry Andric } 2327*06c3fb27SDimitry Andric } 2328*06c3fb27SDimitry Andric 2329*06c3fb27SDimitry Andric // Group Connected Components for Unsafe Vars 2330*06c3fb27SDimitry Andric // (Dependencies based on pointer assignments) 2331*06c3fb27SDimitry Andric std::set<const VarDecl *> VisitedVars{}; 2332*06c3fb27SDimitry Andric for (const auto &[Var, ignore] : UnsafeOps.byVar) { 2333*06c3fb27SDimitry Andric if (VisitedVars.find(Var) == VisitedVars.end()) { 2334*06c3fb27SDimitry Andric std::vector<const VarDecl *> VarGroup{}; 2335*06c3fb27SDimitry Andric 2336*06c3fb27SDimitry Andric std::queue<const VarDecl*> Queue{}; 2337*06c3fb27SDimitry Andric Queue.push(Var); 2338*06c3fb27SDimitry Andric while(!Queue.empty()) { 2339*06c3fb27SDimitry Andric const VarDecl* CurrentVar = Queue.front(); 2340*06c3fb27SDimitry Andric Queue.pop(); 2341*06c3fb27SDimitry Andric VisitedVars.insert(CurrentVar); 2342*06c3fb27SDimitry Andric VarGroup.push_back(CurrentVar); 2343*06c3fb27SDimitry Andric auto AdjacentNodes = DependenciesMap[CurrentVar]; 2344*06c3fb27SDimitry Andric for (const VarDecl *Adj : AdjacentNodes) { 2345*06c3fb27SDimitry Andric if (VisitedVars.find(Adj) == VisitedVars.end()) { 2346*06c3fb27SDimitry Andric Queue.push(Adj); 2347*06c3fb27SDimitry Andric } 2348*06c3fb27SDimitry Andric } 2349*06c3fb27SDimitry Andric } 2350*06c3fb27SDimitry Andric for (const VarDecl * V : VarGroup) { 2351*06c3fb27SDimitry Andric if (UnsafeOps.byVar.find(V) != UnsafeOps.byVar.end()) { 2352*06c3fb27SDimitry Andric VariableGroupsMap[V] = VarGroup; 2353*06c3fb27SDimitry Andric } 2354*06c3fb27SDimitry Andric } 2355*06c3fb27SDimitry Andric } 2356*06c3fb27SDimitry Andric } 2357*06c3fb27SDimitry Andric 2358bdd1243dSDimitry Andric Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars); 2359*06c3fb27SDimitry Andric 2360*06c3fb27SDimitry Andric FixItsForVariableGroup = 2361*06c3fb27SDimitry Andric getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D, 2362*06c3fb27SDimitry Andric Tracker, Handler, VariableGroupsMap); 2363bdd1243dSDimitry Andric 2364bdd1243dSDimitry Andric // FIXME Detect overlapping FixIts. 2365bdd1243dSDimitry Andric 2366bdd1243dSDimitry Andric for (const auto &G : UnsafeOps.noVar) { 2367bdd1243dSDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false); 2368bdd1243dSDimitry Andric } 2369bdd1243dSDimitry Andric 2370bdd1243dSDimitry Andric for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) { 2371*06c3fb27SDimitry Andric auto FixItsIt = FixItsForVariableGroup.find(VD); 2372*06c3fb27SDimitry Andric Handler.handleUnsafeVariableGroup(VD, VariableGroupsMap, 2373*06c3fb27SDimitry Andric FixItsIt != FixItsForVariableGroup.end() 2374bdd1243dSDimitry Andric ? std::move(FixItsIt->second) 2375bdd1243dSDimitry Andric : FixItList{}); 2376bdd1243dSDimitry Andric for (const auto &G : WarningGadgets) { 2377bdd1243dSDimitry Andric Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true); 2378bdd1243dSDimitry Andric } 2379bdd1243dSDimitry Andric } 2380bdd1243dSDimitry Andric } 2381