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