1bdd1243dSDimitry Andric //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric 9bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsage.h" 10*0fca6ea1SDimitry Andric #include "clang/AST/ASTContext.h" 1106c3fb27SDimitry Andric #include "clang/AST/Decl.h" 125f757f3fSDimitry Andric #include "clang/AST/Expr.h" 13bdd1243dSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 14*0fca6ea1SDimitry Andric #include "clang/AST/Stmt.h" 155f757f3fSDimitry Andric #include "clang/AST/StmtVisitor.h" 16bdd1243dSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 17*0fca6ea1SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h" 18*0fca6ea1SDimitry Andric #include "clang/Basic/CharInfo.h" 19*0fca6ea1SDimitry Andric #include "clang/Basic/SourceLocation.h" 2006c3fb27SDimitry Andric #include "clang/Lex/Lexer.h" 2106c3fb27SDimitry Andric #include "clang/Lex/Preprocessor.h" 22*0fca6ea1SDimitry Andric #include "llvm/ADT/APSInt.h" 23bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h" 24*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h" 25*0fca6ea1SDimitry Andric #include "llvm/Support/Casting.h" 26bdd1243dSDimitry Andric #include <memory> 27bdd1243dSDimitry Andric #include <optional> 2806c3fb27SDimitry Andric #include <queue> 29*0fca6ea1SDimitry Andric #include <sstream> 30bdd1243dSDimitry Andric 31bdd1243dSDimitry Andric using namespace llvm; 32bdd1243dSDimitry Andric using namespace clang; 33bdd1243dSDimitry Andric using namespace ast_matchers; 34bdd1243dSDimitry Andric 355f757f3fSDimitry Andric #ifndef NDEBUG 365f757f3fSDimitry Andric namespace { 375f757f3fSDimitry Andric class StmtDebugPrinter 385f757f3fSDimitry Andric : public ConstStmtVisitor<StmtDebugPrinter, std::string> { 395f757f3fSDimitry Andric public: 405f757f3fSDimitry Andric std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); } 415f757f3fSDimitry Andric 425f757f3fSDimitry Andric std::string VisitBinaryOperator(const BinaryOperator *BO) { 435f757f3fSDimitry Andric return "BinaryOperator(" + BO->getOpcodeStr().str() + ")"; 445f757f3fSDimitry Andric } 455f757f3fSDimitry Andric 465f757f3fSDimitry Andric std::string VisitUnaryOperator(const UnaryOperator *UO) { 475f757f3fSDimitry Andric return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")"; 485f757f3fSDimitry Andric } 495f757f3fSDimitry Andric 505f757f3fSDimitry Andric std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) { 515f757f3fSDimitry Andric return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")"; 525f757f3fSDimitry Andric } 535f757f3fSDimitry Andric }; 545f757f3fSDimitry Andric 555f757f3fSDimitry Andric // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form: 565f757f3fSDimitry Andric // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...". 575f757f3fSDimitry Andric static std::string getDREAncestorString(const DeclRefExpr *DRE, 585f757f3fSDimitry Andric ASTContext &Ctx) { 595f757f3fSDimitry Andric std::stringstream SS; 605f757f3fSDimitry Andric const Stmt *St = DRE; 615f757f3fSDimitry Andric StmtDebugPrinter StmtPriner; 625f757f3fSDimitry Andric 635f757f3fSDimitry Andric do { 645f757f3fSDimitry Andric SS << StmtPriner.Visit(St); 655f757f3fSDimitry Andric 665f757f3fSDimitry Andric DynTypedNodeList StParents = Ctx.getParents(*St); 675f757f3fSDimitry Andric 685f757f3fSDimitry Andric if (StParents.size() > 1) 695f757f3fSDimitry Andric return "unavailable due to multiple parents"; 705f757f3fSDimitry Andric if (StParents.size() == 0) 715f757f3fSDimitry Andric break; 725f757f3fSDimitry Andric St = StParents.begin()->get<Stmt>(); 735f757f3fSDimitry Andric if (St) 745f757f3fSDimitry Andric SS << " ==> "; 755f757f3fSDimitry Andric } while (St); 765f757f3fSDimitry Andric return SS.str(); 775f757f3fSDimitry Andric } 785f757f3fSDimitry Andric } // namespace 795f757f3fSDimitry Andric #endif /* NDEBUG */ 805f757f3fSDimitry Andric 81bdd1243dSDimitry Andric namespace clang::ast_matchers { 82bdd1243dSDimitry Andric // A `RecursiveASTVisitor` that traverses all descendants of a given node "n" 83bdd1243dSDimitry Andric // except for those belonging to a different callable of "n". 84bdd1243dSDimitry Andric class MatchDescendantVisitor 85bdd1243dSDimitry Andric : public RecursiveASTVisitor<MatchDescendantVisitor> { 86bdd1243dSDimitry Andric public: 87bdd1243dSDimitry Andric typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase; 88bdd1243dSDimitry Andric 89bdd1243dSDimitry Andric // Creates an AST visitor that matches `Matcher` on all 90bdd1243dSDimitry Andric // descendants of a given node "n" except for the ones 91bdd1243dSDimitry Andric // belonging to a different callable of "n". 92bdd1243dSDimitry Andric MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, 93bdd1243dSDimitry Andric internal::ASTMatchFinder *Finder, 94bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *Builder, 9506c3fb27SDimitry Andric internal::ASTMatchFinder::BindKind Bind, 9606c3fb27SDimitry Andric const bool ignoreUnevaluatedContext) 97bdd1243dSDimitry Andric : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind), 9806c3fb27SDimitry Andric Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {} 99bdd1243dSDimitry Andric 100bdd1243dSDimitry Andric // Returns true if a match is found in a subtree of `DynNode`, which belongs 101bdd1243dSDimitry Andric // to the same callable of `DynNode`. 102bdd1243dSDimitry Andric bool findMatch(const DynTypedNode &DynNode) { 103bdd1243dSDimitry Andric Matches = false; 104bdd1243dSDimitry Andric if (const Stmt *StmtNode = DynNode.get<Stmt>()) { 105bdd1243dSDimitry Andric TraverseStmt(const_cast<Stmt *>(StmtNode)); 106bdd1243dSDimitry Andric *Builder = ResultBindings; 107bdd1243dSDimitry Andric return Matches; 108bdd1243dSDimitry Andric } 109bdd1243dSDimitry Andric return false; 110bdd1243dSDimitry Andric } 111bdd1243dSDimitry Andric 112bdd1243dSDimitry Andric // The following are overriding methods from the base visitor class. 113bdd1243dSDimitry Andric // They are public only to allow CRTP to work. They are *not *part 114bdd1243dSDimitry Andric // of the public API of this class. 115bdd1243dSDimitry Andric 116bdd1243dSDimitry Andric // For the matchers so far used in safe buffers, we only need to match 117bdd1243dSDimitry Andric // `Stmt`s. To override more as needed. 118bdd1243dSDimitry Andric 119bdd1243dSDimitry Andric bool TraverseDecl(Decl *Node) { 120bdd1243dSDimitry Andric if (!Node) 121bdd1243dSDimitry Andric return true; 122bdd1243dSDimitry Andric if (!match(*Node)) 123bdd1243dSDimitry Andric return false; 124bdd1243dSDimitry Andric // To skip callables: 125bdd1243dSDimitry Andric if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node)) 126bdd1243dSDimitry Andric return true; 127bdd1243dSDimitry Andric // Traverse descendants 128bdd1243dSDimitry Andric return VisitorBase::TraverseDecl(Node); 129bdd1243dSDimitry Andric } 130bdd1243dSDimitry Andric 13106c3fb27SDimitry Andric bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) { 13206c3fb27SDimitry Andric // These are unevaluated, except the result expression. 13306c3fb27SDimitry Andric if (ignoreUnevaluatedContext) 13406c3fb27SDimitry Andric return TraverseStmt(Node->getResultExpr()); 13506c3fb27SDimitry Andric return VisitorBase::TraverseGenericSelectionExpr(Node); 13606c3fb27SDimitry Andric } 13706c3fb27SDimitry Andric 13806c3fb27SDimitry Andric bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { 13906c3fb27SDimitry Andric // Unevaluated context. 14006c3fb27SDimitry Andric if (ignoreUnevaluatedContext) 14106c3fb27SDimitry Andric return true; 14206c3fb27SDimitry Andric return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node); 14306c3fb27SDimitry Andric } 14406c3fb27SDimitry Andric 14506c3fb27SDimitry Andric bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) { 14606c3fb27SDimitry Andric // Unevaluated context. 14706c3fb27SDimitry Andric if (ignoreUnevaluatedContext) 14806c3fb27SDimitry Andric return true; 14906c3fb27SDimitry Andric return VisitorBase::TraverseTypeOfExprTypeLoc(Node); 15006c3fb27SDimitry Andric } 15106c3fb27SDimitry Andric 15206c3fb27SDimitry Andric bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) { 15306c3fb27SDimitry Andric // Unevaluated context. 15406c3fb27SDimitry Andric if (ignoreUnevaluatedContext) 15506c3fb27SDimitry Andric return true; 15606c3fb27SDimitry Andric return VisitorBase::TraverseDecltypeTypeLoc(Node); 15706c3fb27SDimitry Andric } 15806c3fb27SDimitry Andric 15906c3fb27SDimitry Andric bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) { 16006c3fb27SDimitry Andric // Unevaluated context. 16106c3fb27SDimitry Andric if (ignoreUnevaluatedContext) 16206c3fb27SDimitry Andric return true; 16306c3fb27SDimitry Andric return VisitorBase::TraverseCXXNoexceptExpr(Node); 16406c3fb27SDimitry Andric } 16506c3fb27SDimitry Andric 16606c3fb27SDimitry Andric bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) { 16706c3fb27SDimitry Andric // Unevaluated context. 16806c3fb27SDimitry Andric if (ignoreUnevaluatedContext) 16906c3fb27SDimitry Andric return true; 17006c3fb27SDimitry Andric return VisitorBase::TraverseCXXTypeidExpr(Node); 17106c3fb27SDimitry Andric } 17206c3fb27SDimitry Andric 173bdd1243dSDimitry Andric bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) { 174bdd1243dSDimitry Andric if (!Node) 175bdd1243dSDimitry Andric return true; 176bdd1243dSDimitry Andric if (!match(*Node)) 177bdd1243dSDimitry Andric return false; 178bdd1243dSDimitry Andric return VisitorBase::TraverseStmt(Node); 179bdd1243dSDimitry Andric } 180bdd1243dSDimitry Andric 181bdd1243dSDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; } 182bdd1243dSDimitry Andric bool shouldVisitImplicitCode() const { 183bdd1243dSDimitry Andric // TODO: let's ignore implicit code for now 184bdd1243dSDimitry Andric return false; 185bdd1243dSDimitry Andric } 186bdd1243dSDimitry Andric 187bdd1243dSDimitry Andric private: 188bdd1243dSDimitry Andric // Sets 'Matched' to true if 'Matcher' matches 'Node' 189bdd1243dSDimitry Andric // 190bdd1243dSDimitry Andric // Returns 'true' if traversal should continue after this function 191bdd1243dSDimitry Andric // returns, i.e. if no match is found or 'Bind' is 'BK_All'. 192bdd1243dSDimitry Andric template <typename T> bool match(const T &Node) { 193bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder); 194bdd1243dSDimitry Andric 195bdd1243dSDimitry Andric if (Matcher->matches(DynTypedNode::create(Node), Finder, 196bdd1243dSDimitry Andric &RecursiveBuilder)) { 197bdd1243dSDimitry Andric ResultBindings.addMatch(RecursiveBuilder); 198bdd1243dSDimitry Andric Matches = true; 199bdd1243dSDimitry Andric if (Bind != internal::ASTMatchFinder::BK_All) 200bdd1243dSDimitry Andric return false; // Abort as soon as a match is found. 201bdd1243dSDimitry Andric } 202bdd1243dSDimitry Andric return true; 203bdd1243dSDimitry Andric } 204bdd1243dSDimitry Andric 205bdd1243dSDimitry Andric const internal::DynTypedMatcher *const Matcher; 206bdd1243dSDimitry Andric internal::ASTMatchFinder *const Finder; 207bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder *const Builder; 208bdd1243dSDimitry Andric internal::BoundNodesTreeBuilder ResultBindings; 209bdd1243dSDimitry Andric const internal::ASTMatchFinder::BindKind Bind; 210bdd1243dSDimitry Andric bool Matches; 21106c3fb27SDimitry Andric bool ignoreUnevaluatedContext; 212bdd1243dSDimitry Andric }; 213bdd1243dSDimitry Andric 21406c3fb27SDimitry Andric // Because we're dealing with raw pointers, let's define what we mean by that. 21506c3fb27SDimitry Andric static auto hasPointerType() { 21606c3fb27SDimitry Andric return hasType(hasCanonicalType(pointerType())); 21706c3fb27SDimitry Andric } 21806c3fb27SDimitry Andric 219*0fca6ea1SDimitry Andric static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); } 22006c3fb27SDimitry Andric 221*0fca6ea1SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>, 222*0fca6ea1SDimitry Andric innerMatcher) { 223bdd1243dSDimitry Andric const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 224bdd1243dSDimitry Andric 225*0fca6ea1SDimitry Andric MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, 226*0fca6ea1SDimitry Andric true); 227bdd1243dSDimitry Andric return Visitor.findMatch(DynTypedNode::create(Node)); 228bdd1243dSDimitry Andric } 22906c3fb27SDimitry Andric 230*0fca6ea1SDimitry Andric AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>, 231*0fca6ea1SDimitry Andric innerMatcher) { 23206c3fb27SDimitry Andric const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 23306c3fb27SDimitry Andric 234*0fca6ea1SDimitry Andric MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, 235*0fca6ea1SDimitry Andric false); 23606c3fb27SDimitry Andric return Visitor.findMatch(DynTypedNode::create(Node)); 23706c3fb27SDimitry Andric } 23806c3fb27SDimitry Andric 23906c3fb27SDimitry Andric // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region 24006c3fb27SDimitry Andric AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *, 24106c3fb27SDimitry Andric Handler) { 24206c3fb27SDimitry Andric return !Handler->isSafeBufferOptOut(Node.getBeginLoc()); 24306c3fb27SDimitry Andric } 24406c3fb27SDimitry Andric 245*0fca6ea1SDimitry Andric AST_MATCHER_P(Stmt, ignoreUnsafeBufferInContainer, 246*0fca6ea1SDimitry Andric const UnsafeBufferUsageHandler *, Handler) { 247*0fca6ea1SDimitry Andric return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc()); 248*0fca6ea1SDimitry Andric } 249*0fca6ea1SDimitry Andric 25006c3fb27SDimitry Andric AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) { 25106c3fb27SDimitry Andric return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder); 25206c3fb27SDimitry Andric } 25306c3fb27SDimitry Andric 25406c3fb27SDimitry Andric // Matches a `UnaryOperator` whose operator is pre-increment: 25506c3fb27SDimitry Andric AST_MATCHER(UnaryOperator, isPreInc) { 25606c3fb27SDimitry Andric return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc; 25706c3fb27SDimitry Andric } 25806c3fb27SDimitry Andric 25906c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher` 26006c3fb27SDimitry Andric // matches 'e' and 'e' is in an Unspecified Lvalue Context. 26106c3fb27SDimitry Andric static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) { 26206c3fb27SDimitry Andric // clang-format off 26306c3fb27SDimitry Andric return 26406c3fb27SDimitry Andric expr(anyOf( 26506c3fb27SDimitry Andric implicitCastExpr( 26606c3fb27SDimitry Andric hasCastKind(CastKind::CK_LValueToRValue), 26706c3fb27SDimitry Andric castSubExpr(innerMatcher)), 26806c3fb27SDimitry Andric binaryOperator( 26906c3fb27SDimitry Andric hasAnyOperatorName("="), 27006c3fb27SDimitry Andric hasLHS(innerMatcher) 27106c3fb27SDimitry Andric ) 27206c3fb27SDimitry Andric )); 27306c3fb27SDimitry Andric // clang-format on 27406c3fb27SDimitry Andric } 27506c3fb27SDimitry Andric 27606c3fb27SDimitry Andric // Returns a matcher that matches any expression `e` such that `InnerMatcher` 27706c3fb27SDimitry Andric // matches `e` and `e` is in an Unspecified Pointer Context (UPC). 27806c3fb27SDimitry Andric static internal::Matcher<Stmt> 27906c3fb27SDimitry Andric isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) { 28006c3fb27SDimitry Andric // A UPC can be 28106c3fb27SDimitry Andric // 1. an argument of a function call (except the callee has [[unsafe_...]] 28206c3fb27SDimitry Andric // attribute), or 28306c3fb27SDimitry Andric // 2. the operand of a pointer-to-(integer or bool) cast operation; or 28406c3fb27SDimitry Andric // 3. the operand of a comparator operation; or 28506c3fb27SDimitry Andric // 4. the operand of a pointer subtraction operation 28606c3fb27SDimitry Andric // (i.e., computing the distance between two pointers); or ... 28706c3fb27SDimitry Andric 288*0fca6ea1SDimitry Andric // clang-format off 289*0fca6ea1SDimitry Andric auto CallArgMatcher = callExpr( 290*0fca6ea1SDimitry Andric forEachArgumentWithParamType( 291*0fca6ea1SDimitry Andric InnerMatcher, 292*0fca6ea1SDimitry Andric isAnyPointer() /* array also decays to pointer type*/), 293*0fca6ea1SDimitry Andric unless(callee( 294*0fca6ea1SDimitry Andric functionDecl(hasAttr(attr::UnsafeBufferUsage))))); 29506c3fb27SDimitry Andric 29606c3fb27SDimitry Andric auto CastOperandMatcher = 29706c3fb27SDimitry Andric castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral), 29806c3fb27SDimitry Andric hasCastKind(CastKind::CK_PointerToBoolean)), 29906c3fb27SDimitry Andric castSubExpr(allOf(hasPointerType(), InnerMatcher))); 30006c3fb27SDimitry Andric 30106c3fb27SDimitry Andric auto CompOperandMatcher = 30206c3fb27SDimitry Andric binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="), 30306c3fb27SDimitry Andric eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)), 30406c3fb27SDimitry Andric hasRHS(allOf(hasPointerType(), InnerMatcher)))); 30506c3fb27SDimitry Andric 30606c3fb27SDimitry Andric // A matcher that matches pointer subtractions: 30706c3fb27SDimitry Andric auto PtrSubtractionMatcher = 30806c3fb27SDimitry Andric binaryOperator(hasOperatorName("-"), 30906c3fb27SDimitry Andric // Note that here we need both LHS and RHS to be 31006c3fb27SDimitry Andric // pointer. Then the inner matcher can match any of 31106c3fb27SDimitry Andric // them: 31206c3fb27SDimitry Andric allOf(hasLHS(hasPointerType()), 31306c3fb27SDimitry Andric hasRHS(hasPointerType())), 31406c3fb27SDimitry Andric eachOf(hasLHS(InnerMatcher), 31506c3fb27SDimitry Andric hasRHS(InnerMatcher))); 316*0fca6ea1SDimitry Andric // clang-format on 31706c3fb27SDimitry Andric 31806c3fb27SDimitry Andric return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher, 31906c3fb27SDimitry Andric PtrSubtractionMatcher)); 32006c3fb27SDimitry Andric // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we 32106c3fb27SDimitry Andric // don't have to check that.) 32206c3fb27SDimitry Andric } 32306c3fb27SDimitry Andric 32406c3fb27SDimitry Andric // Returns a matcher that matches any expression 'e' such that `innerMatcher` 32506c3fb27SDimitry Andric // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression 32606c3fb27SDimitry Andric // 'e' isn't evaluated to an RValue). For example, consider the following code: 32706c3fb27SDimitry Andric // int *p = new int[4]; 32806c3fb27SDimitry Andric // int *q = new int[4]; 32906c3fb27SDimitry Andric // if ((p = q)) {} 33006c3fb27SDimitry Andric // p = q; 33106c3fb27SDimitry Andric // The expression `p = q` in the conditional of the `if` statement 33206c3fb27SDimitry Andric // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;` 33306c3fb27SDimitry Andric // in the assignment statement is in an untyped context. 33406c3fb27SDimitry Andric static internal::Matcher<Stmt> 33506c3fb27SDimitry Andric isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) { 33606c3fb27SDimitry Andric // An unspecified context can be 33706c3fb27SDimitry Andric // 1. A compound statement, 33806c3fb27SDimitry Andric // 2. The body of an if statement 33906c3fb27SDimitry Andric // 3. Body of a loop 34006c3fb27SDimitry Andric auto CompStmt = compoundStmt(forEach(InnerMatcher)); 34106c3fb27SDimitry Andric auto IfStmtThen = ifStmt(hasThen(InnerMatcher)); 34206c3fb27SDimitry Andric auto IfStmtElse = ifStmt(hasElse(InnerMatcher)); 34306c3fb27SDimitry Andric // FIXME: Handle loop bodies. 34406c3fb27SDimitry Andric return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse)); 34506c3fb27SDimitry Andric } 346*0fca6ea1SDimitry Andric 347*0fca6ea1SDimitry Andric // Given a two-param std::span construct call, matches iff the call has the 348*0fca6ea1SDimitry Andric // following forms: 349*0fca6ea1SDimitry Andric // 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE 350*0fca6ea1SDimitry Andric // 2. `std::span<T>{new T, 1}` 351*0fca6ea1SDimitry Andric // 3. `std::span<T>{&var, 1}` 352*0fca6ea1SDimitry Andric // 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size 353*0fca6ea1SDimitry Andric // `n` 354*0fca6ea1SDimitry Andric // 5. `std::span<T>{any, 0}` 355*0fca6ea1SDimitry Andric AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { 356*0fca6ea1SDimitry Andric assert(Node.getNumArgs() == 2 && 357*0fca6ea1SDimitry Andric "expecting a two-parameter std::span constructor"); 358*0fca6ea1SDimitry Andric const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit(); 359*0fca6ea1SDimitry Andric const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit(); 360*0fca6ea1SDimitry Andric auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) { 361*0fca6ea1SDimitry Andric if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext())) 362*0fca6ea1SDimitry Andric if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) { 363*0fca6ea1SDimitry Andric return APSInt::compareValues(*E0CV, *E1CV) == 0; 364*0fca6ea1SDimitry Andric } 365*0fca6ea1SDimitry Andric return false; 366*0fca6ea1SDimitry Andric }; 367*0fca6ea1SDimitry Andric auto AreSameDRE = [](const Expr *E0, const Expr *E1) { 368*0fca6ea1SDimitry Andric if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0)) 369*0fca6ea1SDimitry Andric if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) { 370*0fca6ea1SDimitry Andric return DRE0->getDecl() == DRE1->getDecl(); 371*0fca6ea1SDimitry Andric } 372*0fca6ea1SDimitry Andric return false; 373*0fca6ea1SDimitry Andric }; 374*0fca6ea1SDimitry Andric std::optional<APSInt> Arg1CV = 375*0fca6ea1SDimitry Andric Arg1->getIntegerConstantExpr(Finder->getASTContext()); 376*0fca6ea1SDimitry Andric 377*0fca6ea1SDimitry Andric if (Arg1CV && Arg1CV->isZero()) 378*0fca6ea1SDimitry Andric // Check form 5: 379*0fca6ea1SDimitry Andric return true; 380*0fca6ea1SDimitry Andric switch (Arg0->IgnoreImplicit()->getStmtClass()) { 381*0fca6ea1SDimitry Andric case Stmt::CXXNewExprClass: 382*0fca6ea1SDimitry Andric if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) { 383*0fca6ea1SDimitry Andric // Check form 1: 384*0fca6ea1SDimitry Andric return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) || 385*0fca6ea1SDimitry Andric HaveEqualConstantValues(*Size, Arg1); 386*0fca6ea1SDimitry Andric } 387*0fca6ea1SDimitry Andric // TODO: what's placeholder type? avoid it for now. 388*0fca6ea1SDimitry Andric if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) { 389*0fca6ea1SDimitry Andric // Check form 2: 390*0fca6ea1SDimitry Andric return Arg1CV && Arg1CV->isOne(); 391*0fca6ea1SDimitry Andric } 392*0fca6ea1SDimitry Andric break; 393*0fca6ea1SDimitry Andric case Stmt::UnaryOperatorClass: 394*0fca6ea1SDimitry Andric if (cast<UnaryOperator>(Arg0)->getOpcode() == 395*0fca6ea1SDimitry Andric UnaryOperator::Opcode::UO_AddrOf) 396*0fca6ea1SDimitry Andric // Check form 3: 397*0fca6ea1SDimitry Andric return Arg1CV && Arg1CV->isOne(); 398*0fca6ea1SDimitry Andric break; 399*0fca6ea1SDimitry Andric default: 400*0fca6ea1SDimitry Andric break; 401*0fca6ea1SDimitry Andric } 402*0fca6ea1SDimitry Andric 403*0fca6ea1SDimitry Andric QualType Arg0Ty = Arg0->IgnoreImplicit()->getType(); 404*0fca6ea1SDimitry Andric 405*0fca6ea1SDimitry Andric if (Arg0Ty->isConstantArrayType()) { 406*0fca6ea1SDimitry Andric const APSInt ConstArrSize = 407*0fca6ea1SDimitry Andric APSInt(cast<ConstantArrayType>(Arg0Ty)->getSize()); 408*0fca6ea1SDimitry Andric 409*0fca6ea1SDimitry Andric // Check form 4: 410*0fca6ea1SDimitry Andric return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0; 411*0fca6ea1SDimitry Andric } 412*0fca6ea1SDimitry Andric return false; 413*0fca6ea1SDimitry Andric } 414*0fca6ea1SDimitry Andric 415*0fca6ea1SDimitry Andric AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) { 416*0fca6ea1SDimitry Andric // FIXME: Proper solution: 417*0fca6ea1SDimitry Andric // - refactor Sema::CheckArrayAccess 418*0fca6ea1SDimitry Andric // - split safe/OOB/unknown decision logic from diagnostics emitting code 419*0fca6ea1SDimitry Andric // - e. g. "Try harder to find a NamedDecl to point at in the note." 420*0fca6ea1SDimitry Andric // already duplicated 421*0fca6ea1SDimitry Andric // - call both from Sema and from here 422*0fca6ea1SDimitry Andric 423*0fca6ea1SDimitry Andric const auto *BaseDRE = 424*0fca6ea1SDimitry Andric dyn_cast<DeclRefExpr>(Node.getBase()->IgnoreParenImpCasts()); 425*0fca6ea1SDimitry Andric if (!BaseDRE) 426*0fca6ea1SDimitry Andric return false; 427*0fca6ea1SDimitry Andric if (!BaseDRE->getDecl()) 428*0fca6ea1SDimitry Andric return false; 429*0fca6ea1SDimitry Andric const auto *CATy = Finder->getASTContext().getAsConstantArrayType( 430*0fca6ea1SDimitry Andric BaseDRE->getDecl()->getType()); 431*0fca6ea1SDimitry Andric if (!CATy) 432*0fca6ea1SDimitry Andric return false; 433*0fca6ea1SDimitry Andric 434*0fca6ea1SDimitry Andric if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) { 435*0fca6ea1SDimitry Andric const APInt ArrIdx = IdxLit->getValue(); 436*0fca6ea1SDimitry Andric // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a 437*0fca6ea1SDimitry Andric // bug 438*0fca6ea1SDimitry Andric if (ArrIdx.isNonNegative() && 439*0fca6ea1SDimitry Andric ArrIdx.getLimitedValue() < CATy->getLimitedSize()) 440*0fca6ea1SDimitry Andric return true; 441*0fca6ea1SDimitry Andric } 442*0fca6ea1SDimitry Andric 443*0fca6ea1SDimitry Andric return false; 444*0fca6ea1SDimitry Andric } 445*0fca6ea1SDimitry Andric 446bdd1243dSDimitry Andric } // namespace clang::ast_matchers 447bdd1243dSDimitry Andric 448bdd1243dSDimitry Andric namespace { 449bdd1243dSDimitry Andric // Because the analysis revolves around variables and their types, we'll need to 450bdd1243dSDimitry Andric // track uses of variables (aka DeclRefExprs). 451bdd1243dSDimitry Andric using DeclUseList = SmallVector<const DeclRefExpr *, 1>; 452bdd1243dSDimitry Andric 453bdd1243dSDimitry Andric // Convenience typedef. 454bdd1243dSDimitry Andric using FixItList = SmallVector<FixItHint, 4>; 455bdd1243dSDimitry Andric } // namespace 456bdd1243dSDimitry Andric 457bdd1243dSDimitry Andric namespace { 458bdd1243dSDimitry Andric /// Gadget is an individual operation in the code that may be of interest to 459bdd1243dSDimitry Andric /// this analysis. Each (non-abstract) subclass corresponds to a specific 460bdd1243dSDimitry Andric /// rigid AST structure that constitutes an operation on a pointer-type object. 461bdd1243dSDimitry Andric /// Discovery of a gadget in the code corresponds to claiming that we understand 462bdd1243dSDimitry Andric /// what this part of code is doing well enough to potentially improve it. 463bdd1243dSDimitry Andric /// Gadgets can be warning (immediately deserving a warning) or fixable (not 464bdd1243dSDimitry Andric /// always deserving a warning per se, but requires our attention to identify 465bdd1243dSDimitry Andric /// it warrants a fixit). 466bdd1243dSDimitry Andric class Gadget { 467bdd1243dSDimitry Andric public: 468bdd1243dSDimitry Andric enum class Kind { 469bdd1243dSDimitry Andric #define GADGET(x) x, 470bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 471bdd1243dSDimitry Andric }; 472bdd1243dSDimitry Andric 473bdd1243dSDimitry Andric /// Common type of ASTMatchers used for discovering gadgets. 474bdd1243dSDimitry Andric /// Useful for implementing the static matcher() methods 475bdd1243dSDimitry Andric /// that are expected from all non-abstract subclasses. 476bdd1243dSDimitry Andric using Matcher = decltype(stmt()); 477bdd1243dSDimitry Andric 478bdd1243dSDimitry Andric Gadget(Kind K) : K(K) {} 479bdd1243dSDimitry Andric 480bdd1243dSDimitry Andric Kind getKind() const { return K; } 481bdd1243dSDimitry Andric 4825f757f3fSDimitry Andric #ifndef NDEBUG 4835f757f3fSDimitry Andric StringRef getDebugName() const { 4845f757f3fSDimitry Andric switch (K) { 485*0fca6ea1SDimitry Andric #define GADGET(x) \ 486*0fca6ea1SDimitry Andric case Kind::x: \ 487*0fca6ea1SDimitry Andric return #x; 4885f757f3fSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 4895f757f3fSDimitry Andric } 4905f757f3fSDimitry Andric llvm_unreachable("Unhandled Gadget::Kind enum"); 4915f757f3fSDimitry Andric } 4925f757f3fSDimitry Andric #endif 4935f757f3fSDimitry Andric 494bdd1243dSDimitry Andric virtual bool isWarningGadget() const = 0; 495*0fca6ea1SDimitry Andric // TODO remove this method from WarningGadget interface. It's only used for 496*0fca6ea1SDimitry Andric // debug prints in FixableGadget. 497*0fca6ea1SDimitry Andric virtual SourceLocation getSourceLoc() const = 0; 498bdd1243dSDimitry Andric 499bdd1243dSDimitry Andric /// Returns the list of pointer-type variables on which this gadget performs 500bdd1243dSDimitry Andric /// its operation. Typically, there's only one variable. This isn't a list 501bdd1243dSDimitry Andric /// of all DeclRefExprs in the gadget's AST! 502bdd1243dSDimitry Andric virtual DeclUseList getClaimedVarUseSites() const = 0; 503bdd1243dSDimitry Andric 504bdd1243dSDimitry Andric virtual ~Gadget() = default; 505bdd1243dSDimitry Andric 506bdd1243dSDimitry Andric private: 507bdd1243dSDimitry Andric Kind K; 508bdd1243dSDimitry Andric }; 509bdd1243dSDimitry Andric 510bdd1243dSDimitry Andric /// Warning gadgets correspond to unsafe code patterns that warrants 511bdd1243dSDimitry Andric /// an immediate warning. 512bdd1243dSDimitry Andric class WarningGadget : public Gadget { 513bdd1243dSDimitry Andric public: 514bdd1243dSDimitry Andric WarningGadget(Kind K) : Gadget(K) {} 515bdd1243dSDimitry Andric 516bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return G->isWarningGadget(); } 517bdd1243dSDimitry Andric bool isWarningGadget() const final { return true; } 518*0fca6ea1SDimitry Andric 519*0fca6ea1SDimitry Andric virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 520*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 521*0fca6ea1SDimitry Andric ASTContext &Ctx) const = 0; 522bdd1243dSDimitry Andric }; 523bdd1243dSDimitry Andric 524*0fca6ea1SDimitry Andric /// Fixable gadgets correspond to code patterns that aren't always unsafe but 525*0fca6ea1SDimitry Andric /// need to be properly recognized in order to emit fixes. For example, if a raw 526*0fca6ea1SDimitry Andric /// pointer-type variable is replaced by a safe C++ container, every use of such 527*0fca6ea1SDimitry Andric /// variable must be carefully considered and possibly updated. 528bdd1243dSDimitry Andric class FixableGadget : public Gadget { 529bdd1243dSDimitry Andric public: 530bdd1243dSDimitry Andric FixableGadget(Kind K) : Gadget(K) {} 531bdd1243dSDimitry Andric 532bdd1243dSDimitry Andric static bool classof(const Gadget *G) { return !G->isWarningGadget(); } 533bdd1243dSDimitry Andric bool isWarningGadget() const final { return false; } 534bdd1243dSDimitry Andric 535bdd1243dSDimitry Andric /// Returns a fixit that would fix the current gadget according to 53606c3fb27SDimitry Andric /// the current strategy. Returns std::nullopt if the fix cannot be produced; 537bdd1243dSDimitry Andric /// returns an empty list if no fixes are necessary. 538*0fca6ea1SDimitry Andric virtual std::optional<FixItList> getFixits(const FixitStrategy &) const { 539bdd1243dSDimitry Andric return std::nullopt; 540bdd1243dSDimitry Andric } 54106c3fb27SDimitry Andric 542*0fca6ea1SDimitry Andric /// Returns a list of two elements where the first element is the LHS of a 543*0fca6ea1SDimitry Andric /// pointer assignment statement and the second element is the RHS. This 544*0fca6ea1SDimitry Andric /// two-element list represents the fact that the LHS buffer gets its bounds 545*0fca6ea1SDimitry Andric /// information from the RHS buffer. This information will be used later to 546*0fca6ea1SDimitry Andric /// group all those variables whose types must be modified together to prevent 547*0fca6ea1SDimitry Andric /// type mismatches. 54806c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 54906c3fb27SDimitry Andric getStrategyImplications() const { 55006c3fb27SDimitry Andric return std::nullopt; 55106c3fb27SDimitry Andric } 552bdd1243dSDimitry Andric }; 553bdd1243dSDimitry Andric 554*0fca6ea1SDimitry Andric static auto toSupportedVariable() { return to(varDecl()); } 5555f757f3fSDimitry Andric 556bdd1243dSDimitry Andric using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>; 557bdd1243dSDimitry Andric using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>; 558bdd1243dSDimitry Andric 559bdd1243dSDimitry Andric /// An increment of a pointer-type value is unsafe as it may run the pointer 560bdd1243dSDimitry Andric /// out of bounds. 561bdd1243dSDimitry Andric class IncrementGadget : public WarningGadget { 562bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 563bdd1243dSDimitry Andric const UnaryOperator *Op; 564bdd1243dSDimitry Andric 565bdd1243dSDimitry Andric public: 566bdd1243dSDimitry Andric IncrementGadget(const MatchFinder::MatchResult &Result) 567bdd1243dSDimitry Andric : WarningGadget(Kind::Increment), 568bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 569bdd1243dSDimitry Andric 570bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 571bdd1243dSDimitry Andric return G->getKind() == Kind::Increment; 572bdd1243dSDimitry Andric } 573bdd1243dSDimitry Andric 574bdd1243dSDimitry Andric static Matcher matcher() { 575*0fca6ea1SDimitry Andric return stmt( 576*0fca6ea1SDimitry Andric unaryOperator(hasOperatorName("++"), 577*0fca6ea1SDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))) 578*0fca6ea1SDimitry Andric .bind(OpTag)); 579bdd1243dSDimitry Andric } 580bdd1243dSDimitry Andric 581*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 582*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 583*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 584*0fca6ea1SDimitry Andric Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 585*0fca6ea1SDimitry Andric } 586*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 587bdd1243dSDimitry Andric 588bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 589bdd1243dSDimitry Andric SmallVector<const DeclRefExpr *, 2> Uses; 590bdd1243dSDimitry Andric if (const auto *DRE = 591bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 592bdd1243dSDimitry Andric Uses.push_back(DRE); 593bdd1243dSDimitry Andric } 594bdd1243dSDimitry Andric 595bdd1243dSDimitry Andric return std::move(Uses); 596bdd1243dSDimitry Andric } 597bdd1243dSDimitry Andric }; 598bdd1243dSDimitry Andric 599bdd1243dSDimitry Andric /// A decrement of a pointer-type value is unsafe as it may run the pointer 600bdd1243dSDimitry Andric /// out of bounds. 601bdd1243dSDimitry Andric class DecrementGadget : public WarningGadget { 602bdd1243dSDimitry Andric static constexpr const char *const OpTag = "op"; 603bdd1243dSDimitry Andric const UnaryOperator *Op; 604bdd1243dSDimitry Andric 605bdd1243dSDimitry Andric public: 606bdd1243dSDimitry Andric DecrementGadget(const MatchFinder::MatchResult &Result) 607bdd1243dSDimitry Andric : WarningGadget(Kind::Decrement), 608bdd1243dSDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 609bdd1243dSDimitry Andric 610bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 611bdd1243dSDimitry Andric return G->getKind() == Kind::Decrement; 612bdd1243dSDimitry Andric } 613bdd1243dSDimitry Andric 614bdd1243dSDimitry Andric static Matcher matcher() { 615*0fca6ea1SDimitry Andric return stmt( 616*0fca6ea1SDimitry Andric unaryOperator(hasOperatorName("--"), 617*0fca6ea1SDimitry Andric hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))) 618*0fca6ea1SDimitry Andric .bind(OpTag)); 619bdd1243dSDimitry Andric } 620bdd1243dSDimitry Andric 621*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 622*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 623*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 624*0fca6ea1SDimitry Andric Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 625*0fca6ea1SDimitry Andric } 626*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 627bdd1243dSDimitry Andric 628bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 629bdd1243dSDimitry Andric if (const auto *DRE = 630bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 631bdd1243dSDimitry Andric return {DRE}; 632bdd1243dSDimitry Andric } 633bdd1243dSDimitry Andric 634bdd1243dSDimitry Andric return {}; 635bdd1243dSDimitry Andric } 636bdd1243dSDimitry Andric }; 637bdd1243dSDimitry Andric 638bdd1243dSDimitry Andric /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as 639bdd1243dSDimitry Andric /// it doesn't have any bounds checks for the array. 640bdd1243dSDimitry Andric class ArraySubscriptGadget : public WarningGadget { 64106c3fb27SDimitry Andric static constexpr const char *const ArraySubscrTag = "ArraySubscript"; 642bdd1243dSDimitry Andric const ArraySubscriptExpr *ASE; 643bdd1243dSDimitry Andric 644bdd1243dSDimitry Andric public: 645bdd1243dSDimitry Andric ArraySubscriptGadget(const MatchFinder::MatchResult &Result) 646bdd1243dSDimitry Andric : WarningGadget(Kind::ArraySubscript), 647bdd1243dSDimitry Andric ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {} 648bdd1243dSDimitry Andric 649bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 650bdd1243dSDimitry Andric return G->getKind() == Kind::ArraySubscript; 651bdd1243dSDimitry Andric } 652bdd1243dSDimitry Andric 653bdd1243dSDimitry Andric static Matcher matcher() { 654bdd1243dSDimitry Andric // clang-format off 655bdd1243dSDimitry Andric return stmt(arraySubscriptExpr( 656bdd1243dSDimitry Andric hasBase(ignoringParenImpCasts( 657bdd1243dSDimitry Andric anyOf(hasPointerType(), hasArrayType()))), 658*0fca6ea1SDimitry Andric unless(anyOf( 659*0fca6ea1SDimitry Andric isSafeArraySubscript(), 660*0fca6ea1SDimitry Andric hasIndex( 66106c3fb27SDimitry Andric anyOf(integerLiteral(equals(0)), arrayInitIndexExpr()) 662*0fca6ea1SDimitry Andric ) 663*0fca6ea1SDimitry Andric ))).bind(ArraySubscrTag)); 664bdd1243dSDimitry Andric // clang-format on 665bdd1243dSDimitry Andric } 666bdd1243dSDimitry Andric 667*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 668*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 669*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 670*0fca6ea1SDimitry Andric Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx); 671*0fca6ea1SDimitry Andric } 672*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); } 673bdd1243dSDimitry Andric 674bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 675bdd1243dSDimitry Andric if (const auto *DRE = 676bdd1243dSDimitry Andric dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) { 677bdd1243dSDimitry Andric return {DRE}; 678bdd1243dSDimitry Andric } 679bdd1243dSDimitry Andric 680bdd1243dSDimitry Andric return {}; 681bdd1243dSDimitry Andric } 682bdd1243dSDimitry Andric }; 683bdd1243dSDimitry Andric 684bdd1243dSDimitry Andric /// A pointer arithmetic expression of one of the forms: 685bdd1243dSDimitry Andric /// \code 686bdd1243dSDimitry Andric /// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n 687bdd1243dSDimitry Andric /// \endcode 688bdd1243dSDimitry Andric class PointerArithmeticGadget : public WarningGadget { 689bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticTag = "ptrAdd"; 690bdd1243dSDimitry Andric static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr"; 691bdd1243dSDimitry Andric const BinaryOperator *PA; // pointer arithmetic expression 692bdd1243dSDimitry Andric const Expr *Ptr; // the pointer expression in `PA` 693bdd1243dSDimitry Andric 694bdd1243dSDimitry Andric public: 695bdd1243dSDimitry Andric PointerArithmeticGadget(const MatchFinder::MatchResult &Result) 696bdd1243dSDimitry Andric : WarningGadget(Kind::PointerArithmetic), 697bdd1243dSDimitry Andric PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)), 698bdd1243dSDimitry Andric Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {} 699bdd1243dSDimitry Andric 700bdd1243dSDimitry Andric static bool classof(const Gadget *G) { 701bdd1243dSDimitry Andric return G->getKind() == Kind::PointerArithmetic; 702bdd1243dSDimitry Andric } 703bdd1243dSDimitry Andric 704bdd1243dSDimitry Andric static Matcher matcher() { 70506c3fb27SDimitry Andric auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType())); 70606c3fb27SDimitry Andric auto PtrAtRight = 70706c3fb27SDimitry Andric allOf(hasOperatorName("+"), 708bdd1243dSDimitry Andric hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 709bdd1243dSDimitry Andric hasLHS(HasIntegerType)); 71006c3fb27SDimitry Andric auto PtrAtLeft = 71106c3fb27SDimitry Andric allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"), 712bdd1243dSDimitry Andric hasOperatorName("+="), hasOperatorName("-=")), 713bdd1243dSDimitry Andric hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 714bdd1243dSDimitry Andric hasRHS(HasIntegerType)); 715bdd1243dSDimitry Andric 71606c3fb27SDimitry Andric return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight)) 71706c3fb27SDimitry Andric .bind(PointerArithmeticTag)); 718bdd1243dSDimitry Andric } 719bdd1243dSDimitry Andric 720*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 721*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 722*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 723*0fca6ea1SDimitry Andric Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx); 724*0fca6ea1SDimitry Andric } 725*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); } 726bdd1243dSDimitry Andric 727bdd1243dSDimitry Andric DeclUseList getClaimedVarUseSites() const override { 72806c3fb27SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) { 729bdd1243dSDimitry Andric return {DRE}; 730bdd1243dSDimitry Andric } 731bdd1243dSDimitry Andric 732bdd1243dSDimitry Andric return {}; 733bdd1243dSDimitry Andric } 734bdd1243dSDimitry Andric // FIXME: pointer adding zero should be fine 735bdd1243dSDimitry Andric // FIXME: this gadge will need a fix-it 736bdd1243dSDimitry Andric }; 73706c3fb27SDimitry Andric 738*0fca6ea1SDimitry Andric class SpanTwoParamConstructorGadget : public WarningGadget { 739*0fca6ea1SDimitry Andric static constexpr const char *const SpanTwoParamConstructorTag = 740*0fca6ea1SDimitry Andric "spanTwoParamConstructor"; 741*0fca6ea1SDimitry Andric const CXXConstructExpr *Ctor; // the span constructor expression 742*0fca6ea1SDimitry Andric 743*0fca6ea1SDimitry Andric public: 744*0fca6ea1SDimitry Andric SpanTwoParamConstructorGadget(const MatchFinder::MatchResult &Result) 745*0fca6ea1SDimitry Andric : WarningGadget(Kind::SpanTwoParamConstructor), 746*0fca6ea1SDimitry Andric Ctor(Result.Nodes.getNodeAs<CXXConstructExpr>( 747*0fca6ea1SDimitry Andric SpanTwoParamConstructorTag)) {} 748*0fca6ea1SDimitry Andric 749*0fca6ea1SDimitry Andric static bool classof(const Gadget *G) { 750*0fca6ea1SDimitry Andric return G->getKind() == Kind::SpanTwoParamConstructor; 751*0fca6ea1SDimitry Andric } 752*0fca6ea1SDimitry Andric 753*0fca6ea1SDimitry Andric static Matcher matcher() { 754*0fca6ea1SDimitry Andric auto HasTwoParamSpanCtorDecl = hasDeclaration( 755*0fca6ea1SDimitry Andric cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"), 756*0fca6ea1SDimitry Andric parameterCountIs(2))); 757*0fca6ea1SDimitry Andric 758*0fca6ea1SDimitry Andric return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl, 759*0fca6ea1SDimitry Andric unless(isSafeSpanTwoParamConstruct())) 760*0fca6ea1SDimitry Andric .bind(SpanTwoParamConstructorTag)); 761*0fca6ea1SDimitry Andric } 762*0fca6ea1SDimitry Andric 763*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 764*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 765*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 766*0fca6ea1SDimitry Andric Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx); 767*0fca6ea1SDimitry Andric } 768*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); } 769*0fca6ea1SDimitry Andric 770*0fca6ea1SDimitry Andric DeclUseList getClaimedVarUseSites() const override { 771*0fca6ea1SDimitry Andric // If the constructor call is of the form `std::span{var, n}`, `var` is 772*0fca6ea1SDimitry Andric // considered an unsafe variable. 773*0fca6ea1SDimitry Andric if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) { 774*0fca6ea1SDimitry Andric if (isa<VarDecl>(DRE->getDecl())) 775*0fca6ea1SDimitry Andric return {DRE}; 776*0fca6ea1SDimitry Andric } 777*0fca6ea1SDimitry Andric return {}; 778*0fca6ea1SDimitry Andric } 779*0fca6ea1SDimitry Andric }; 780*0fca6ea1SDimitry Andric 78106c3fb27SDimitry Andric /// A pointer initialization expression of the form: 78206c3fb27SDimitry Andric /// \code 78306c3fb27SDimitry Andric /// int *p = q; 78406c3fb27SDimitry Andric /// \endcode 78506c3fb27SDimitry Andric class PointerInitGadget : public FixableGadget { 78606c3fb27SDimitry Andric private: 78706c3fb27SDimitry Andric static constexpr const char *const PointerInitLHSTag = "ptrInitLHS"; 78806c3fb27SDimitry Andric static constexpr const char *const PointerInitRHSTag = "ptrInitRHS"; 78906c3fb27SDimitry Andric const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI` 79006c3fb27SDimitry Andric const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI` 79106c3fb27SDimitry Andric 79206c3fb27SDimitry Andric public: 79306c3fb27SDimitry Andric PointerInitGadget(const MatchFinder::MatchResult &Result) 79406c3fb27SDimitry Andric : FixableGadget(Kind::PointerInit), 79506c3fb27SDimitry Andric PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)), 79606c3fb27SDimitry Andric PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {} 79706c3fb27SDimitry Andric 79806c3fb27SDimitry Andric static bool classof(const Gadget *G) { 79906c3fb27SDimitry Andric return G->getKind() == Kind::PointerInit; 80006c3fb27SDimitry Andric } 80106c3fb27SDimitry Andric 80206c3fb27SDimitry Andric static Matcher matcher() { 803*0fca6ea1SDimitry Andric auto PtrInitStmt = declStmt(hasSingleDecl( 804*0fca6ea1SDimitry Andric varDecl(hasInitializer(ignoringImpCasts( 805*0fca6ea1SDimitry Andric declRefExpr(hasPointerType(), toSupportedVariable()) 806*0fca6ea1SDimitry Andric .bind(PointerInitRHSTag)))) 807*0fca6ea1SDimitry Andric .bind(PointerInitLHSTag))); 80806c3fb27SDimitry Andric 80906c3fb27SDimitry Andric return stmt(PtrInitStmt); 81006c3fb27SDimitry Andric } 81106c3fb27SDimitry Andric 812*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 813*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 814*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { 815*0fca6ea1SDimitry Andric return PtrInitRHS->getBeginLoc(); 8165f757f3fSDimitry Andric } 81706c3fb27SDimitry Andric 81806c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 81906c3fb27SDimitry Andric return DeclUseList{PtrInitRHS}; 82006c3fb27SDimitry Andric } 82106c3fb27SDimitry Andric 82206c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 82306c3fb27SDimitry Andric getStrategyImplications() const override { 824*0fca6ea1SDimitry Andric return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl())); 82506c3fb27SDimitry Andric } 82606c3fb27SDimitry Andric }; 82706c3fb27SDimitry Andric 82806c3fb27SDimitry Andric /// A pointer assignment expression of the form: 82906c3fb27SDimitry Andric /// \code 83006c3fb27SDimitry Andric /// p = q; 83106c3fb27SDimitry Andric /// \endcode 832*0fca6ea1SDimitry Andric /// where both `p` and `q` are pointers. 833*0fca6ea1SDimitry Andric class PtrToPtrAssignmentGadget : public FixableGadget { 83406c3fb27SDimitry Andric private: 83506c3fb27SDimitry Andric static constexpr const char *const PointerAssignLHSTag = "ptrLHS"; 83606c3fb27SDimitry Andric static constexpr const char *const PointerAssignRHSTag = "ptrRHS"; 83706c3fb27SDimitry Andric const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA` 83806c3fb27SDimitry Andric const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA` 83906c3fb27SDimitry Andric 84006c3fb27SDimitry Andric public: 841*0fca6ea1SDimitry Andric PtrToPtrAssignmentGadget(const MatchFinder::MatchResult &Result) 842*0fca6ea1SDimitry Andric : FixableGadget(Kind::PtrToPtrAssignment), 84306c3fb27SDimitry Andric PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)), 84406c3fb27SDimitry Andric PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {} 84506c3fb27SDimitry Andric 84606c3fb27SDimitry Andric static bool classof(const Gadget *G) { 847*0fca6ea1SDimitry Andric return G->getKind() == Kind::PtrToPtrAssignment; 84806c3fb27SDimitry Andric } 84906c3fb27SDimitry Andric 85006c3fb27SDimitry Andric static Matcher matcher() { 851*0fca6ea1SDimitry Andric auto PtrAssignExpr = binaryOperator( 852*0fca6ea1SDimitry Andric allOf(hasOperatorName("="), 853*0fca6ea1SDimitry Andric hasRHS(ignoringParenImpCasts( 854*0fca6ea1SDimitry Andric declRefExpr(hasPointerType(), toSupportedVariable()) 855*0fca6ea1SDimitry Andric .bind(PointerAssignRHSTag))), 856*0fca6ea1SDimitry Andric hasLHS(declRefExpr(hasPointerType(), toSupportedVariable()) 857*0fca6ea1SDimitry Andric .bind(PointerAssignLHSTag)))); 85806c3fb27SDimitry Andric 85906c3fb27SDimitry Andric return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr)); 86006c3fb27SDimitry Andric } 86106c3fb27SDimitry Andric 862*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 863*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 864*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); } 86506c3fb27SDimitry Andric 86606c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 86706c3fb27SDimitry Andric return DeclUseList{PtrLHS, PtrRHS}; 86806c3fb27SDimitry Andric } 86906c3fb27SDimitry Andric 87006c3fb27SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 87106c3fb27SDimitry Andric getStrategyImplications() const override { 87206c3fb27SDimitry Andric return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()), 87306c3fb27SDimitry Andric cast<VarDecl>(PtrRHS->getDecl())); 87406c3fb27SDimitry Andric } 87506c3fb27SDimitry Andric }; 87606c3fb27SDimitry Andric 877*0fca6ea1SDimitry Andric /// An assignment expression of the form: 878*0fca6ea1SDimitry Andric /// \code 879*0fca6ea1SDimitry Andric /// ptr = array; 880*0fca6ea1SDimitry Andric /// \endcode 881*0fca6ea1SDimitry Andric /// where `p` is a pointer and `array` is a constant size array. 882*0fca6ea1SDimitry Andric class CArrayToPtrAssignmentGadget : public FixableGadget { 883*0fca6ea1SDimitry Andric private: 884*0fca6ea1SDimitry Andric static constexpr const char *const PointerAssignLHSTag = "ptrLHS"; 885*0fca6ea1SDimitry Andric static constexpr const char *const PointerAssignRHSTag = "ptrRHS"; 886*0fca6ea1SDimitry Andric const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA` 887*0fca6ea1SDimitry Andric const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA` 888*0fca6ea1SDimitry Andric 889*0fca6ea1SDimitry Andric public: 890*0fca6ea1SDimitry Andric CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult &Result) 891*0fca6ea1SDimitry Andric : FixableGadget(Kind::CArrayToPtrAssignment), 892*0fca6ea1SDimitry Andric PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)), 893*0fca6ea1SDimitry Andric PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {} 894*0fca6ea1SDimitry Andric 895*0fca6ea1SDimitry Andric static bool classof(const Gadget *G) { 896*0fca6ea1SDimitry Andric return G->getKind() == Kind::CArrayToPtrAssignment; 897*0fca6ea1SDimitry Andric } 898*0fca6ea1SDimitry Andric 899*0fca6ea1SDimitry Andric static Matcher matcher() { 900*0fca6ea1SDimitry Andric auto PtrAssignExpr = binaryOperator( 901*0fca6ea1SDimitry Andric allOf(hasOperatorName("="), 902*0fca6ea1SDimitry Andric hasRHS(ignoringParenImpCasts( 903*0fca6ea1SDimitry Andric declRefExpr(hasType(hasCanonicalType(constantArrayType())), 904*0fca6ea1SDimitry Andric toSupportedVariable()) 905*0fca6ea1SDimitry Andric .bind(PointerAssignRHSTag))), 906*0fca6ea1SDimitry Andric hasLHS(declRefExpr(hasPointerType(), toSupportedVariable()) 907*0fca6ea1SDimitry Andric .bind(PointerAssignLHSTag)))); 908*0fca6ea1SDimitry Andric 909*0fca6ea1SDimitry Andric return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr)); 910*0fca6ea1SDimitry Andric } 911*0fca6ea1SDimitry Andric 912*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 913*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 914*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); } 915*0fca6ea1SDimitry Andric 916*0fca6ea1SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 917*0fca6ea1SDimitry Andric return DeclUseList{PtrLHS, PtrRHS}; 918*0fca6ea1SDimitry Andric } 919*0fca6ea1SDimitry Andric 920*0fca6ea1SDimitry Andric virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 921*0fca6ea1SDimitry Andric getStrategyImplications() const override { 922*0fca6ea1SDimitry Andric return {}; 923*0fca6ea1SDimitry Andric } 924*0fca6ea1SDimitry Andric }; 925*0fca6ea1SDimitry Andric 92606c3fb27SDimitry Andric /// A call of a function or method that performs unchecked buffer operations 92706c3fb27SDimitry Andric /// over one of its pointer parameters. 92806c3fb27SDimitry Andric class UnsafeBufferUsageAttrGadget : public WarningGadget { 92906c3fb27SDimitry Andric constexpr static const char *const OpTag = "call_expr"; 93006c3fb27SDimitry Andric const CallExpr *Op; 93106c3fb27SDimitry Andric 93206c3fb27SDimitry Andric public: 93306c3fb27SDimitry Andric UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result) 93406c3fb27SDimitry Andric : WarningGadget(Kind::UnsafeBufferUsageAttr), 93506c3fb27SDimitry Andric Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {} 93606c3fb27SDimitry Andric 93706c3fb27SDimitry Andric static bool classof(const Gadget *G) { 93806c3fb27SDimitry Andric return G->getKind() == Kind::UnsafeBufferUsageAttr; 93906c3fb27SDimitry Andric } 94006c3fb27SDimitry Andric 94106c3fb27SDimitry Andric static Matcher matcher() { 942*0fca6ea1SDimitry Andric auto HasUnsafeFnDecl = 943*0fca6ea1SDimitry Andric callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))); 944*0fca6ea1SDimitry Andric return stmt(callExpr(HasUnsafeFnDecl).bind(OpTag)); 945*0fca6ea1SDimitry Andric } 946*0fca6ea1SDimitry Andric 947*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 948*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 949*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 950*0fca6ea1SDimitry Andric Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 951*0fca6ea1SDimitry Andric } 952*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 953*0fca6ea1SDimitry Andric 954*0fca6ea1SDimitry Andric DeclUseList getClaimedVarUseSites() const override { return {}; } 955*0fca6ea1SDimitry Andric }; 956*0fca6ea1SDimitry Andric 957*0fca6ea1SDimitry Andric /// A call of a constructor that performs unchecked buffer operations 958*0fca6ea1SDimitry Andric /// over one of its pointer parameters, or constructs a class object that will 959*0fca6ea1SDimitry Andric /// perform buffer operations that depend on the correctness of the parameters. 960*0fca6ea1SDimitry Andric class UnsafeBufferUsageCtorAttrGadget : public WarningGadget { 961*0fca6ea1SDimitry Andric constexpr static const char *const OpTag = "cxx_construct_expr"; 962*0fca6ea1SDimitry Andric const CXXConstructExpr *Op; 963*0fca6ea1SDimitry Andric 964*0fca6ea1SDimitry Andric public: 965*0fca6ea1SDimitry Andric UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult &Result) 966*0fca6ea1SDimitry Andric : WarningGadget(Kind::UnsafeBufferUsageCtorAttr), 967*0fca6ea1SDimitry Andric Op(Result.Nodes.getNodeAs<CXXConstructExpr>(OpTag)) {} 968*0fca6ea1SDimitry Andric 969*0fca6ea1SDimitry Andric static bool classof(const Gadget *G) { 970*0fca6ea1SDimitry Andric return G->getKind() == Kind::UnsafeBufferUsageCtorAttr; 971*0fca6ea1SDimitry Andric } 972*0fca6ea1SDimitry Andric 973*0fca6ea1SDimitry Andric static Matcher matcher() { 974*0fca6ea1SDimitry Andric auto HasUnsafeCtorDecl = 975*0fca6ea1SDimitry Andric hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage))); 976*0fca6ea1SDimitry Andric // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget. 977*0fca6ea1SDimitry Andric auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher(); 978*0fca6ea1SDimitry Andric return stmt( 979*0fca6ea1SDimitry Andric cxxConstructExpr(HasUnsafeCtorDecl, unless(HasTwoParamSpanCtorDecl)) 98006c3fb27SDimitry Andric .bind(OpTag)); 98106c3fb27SDimitry Andric } 982*0fca6ea1SDimitry Andric 983*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 984*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 985*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 986*0fca6ea1SDimitry Andric Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 987*0fca6ea1SDimitry Andric } 988*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 98906c3fb27SDimitry Andric 99006c3fb27SDimitry Andric DeclUseList getClaimedVarUseSites() const override { return {}; } 99106c3fb27SDimitry Andric }; 99206c3fb27SDimitry Andric 9931db9f3b2SDimitry Andric // Warning gadget for unsafe invocation of span::data method. 9941db9f3b2SDimitry Andric // Triggers when the pointer returned by the invocation is immediately 9951db9f3b2SDimitry Andric // cast to a larger type. 9961db9f3b2SDimitry Andric 9971db9f3b2SDimitry Andric class DataInvocationGadget : public WarningGadget { 9981db9f3b2SDimitry Andric constexpr static const char *const OpTag = "data_invocation_expr"; 9991db9f3b2SDimitry Andric const ExplicitCastExpr *Op; 10001db9f3b2SDimitry Andric 10011db9f3b2SDimitry Andric public: 10021db9f3b2SDimitry Andric DataInvocationGadget(const MatchFinder::MatchResult &Result) 10031db9f3b2SDimitry Andric : WarningGadget(Kind::DataInvocation), 10041db9f3b2SDimitry Andric Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {} 10051db9f3b2SDimitry Andric 10061db9f3b2SDimitry Andric static bool classof(const Gadget *G) { 10071db9f3b2SDimitry Andric return G->getKind() == Kind::DataInvocation; 10081db9f3b2SDimitry Andric } 10091db9f3b2SDimitry Andric 10101db9f3b2SDimitry Andric static Matcher matcher() { 10117a6dacacSDimitry Andric Matcher callExpr = cxxMemberCallExpr( 10127a6dacacSDimitry Andric callee(cxxMethodDecl(hasName("data"), ofClass(hasName("std::span"))))); 10131db9f3b2SDimitry Andric return stmt( 10147a6dacacSDimitry Andric explicitCastExpr(anyOf(has(callExpr), has(parenExpr(has(callExpr))))) 10151db9f3b2SDimitry Andric .bind(OpTag)); 10161db9f3b2SDimitry Andric } 1017*0fca6ea1SDimitry Andric 1018*0fca6ea1SDimitry Andric void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 1019*0fca6ea1SDimitry Andric bool IsRelatedToDecl, 1020*0fca6ea1SDimitry Andric ASTContext &Ctx) const override { 1021*0fca6ea1SDimitry Andric Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 1022*0fca6ea1SDimitry Andric } 1023*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 10241db9f3b2SDimitry Andric 10251db9f3b2SDimitry Andric DeclUseList getClaimedVarUseSites() const override { return {}; } 10261db9f3b2SDimitry Andric }; 10271db9f3b2SDimitry Andric 102806c3fb27SDimitry Andric // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue 102906c3fb27SDimitry Andric // Context (see `isInUnspecifiedLvalueContext`). 103006c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator. 103106c3fb27SDimitry Andric class ULCArraySubscriptGadget : public FixableGadget { 103206c3fb27SDimitry Andric private: 103306c3fb27SDimitry Andric static constexpr const char *const ULCArraySubscriptTag = 103406c3fb27SDimitry Andric "ArraySubscriptUnderULC"; 103506c3fb27SDimitry Andric const ArraySubscriptExpr *Node; 103606c3fb27SDimitry Andric 103706c3fb27SDimitry Andric public: 103806c3fb27SDimitry Andric ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result) 103906c3fb27SDimitry Andric : FixableGadget(Kind::ULCArraySubscript), 104006c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) { 104106c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 104206c3fb27SDimitry Andric } 104306c3fb27SDimitry Andric 104406c3fb27SDimitry Andric static bool classof(const Gadget *G) { 104506c3fb27SDimitry Andric return G->getKind() == Kind::ULCArraySubscript; 104606c3fb27SDimitry Andric } 104706c3fb27SDimitry Andric 104806c3fb27SDimitry Andric static Matcher matcher() { 104906c3fb27SDimitry Andric auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 1050*0fca6ea1SDimitry Andric auto BaseIsArrayOrPtrDRE = hasBase( 1051*0fca6ea1SDimitry Andric ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable()))); 105206c3fb27SDimitry Andric auto Target = 105306c3fb27SDimitry Andric arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag); 105406c3fb27SDimitry Andric 105506c3fb27SDimitry Andric return expr(isInUnspecifiedLvalueContext(Target)); 105606c3fb27SDimitry Andric } 105706c3fb27SDimitry Andric 1058*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 1059*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 1060*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 106106c3fb27SDimitry Andric 106206c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 106306c3fb27SDimitry Andric if (const auto *DRE = 106406c3fb27SDimitry Andric dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) { 106506c3fb27SDimitry Andric return {DRE}; 106606c3fb27SDimitry Andric } 106706c3fb27SDimitry Andric return {}; 106806c3fb27SDimitry Andric } 106906c3fb27SDimitry Andric }; 107006c3fb27SDimitry Andric 107106c3fb27SDimitry Andric // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the 107206c3fb27SDimitry Andric // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits 107306c3fb27SDimitry Andric // fixit of the form `UPC(DRE.data())`. 107406c3fb27SDimitry Andric class UPCStandalonePointerGadget : public FixableGadget { 107506c3fb27SDimitry Andric private: 107606c3fb27SDimitry Andric static constexpr const char *const DeclRefExprTag = "StandalonePointer"; 107706c3fb27SDimitry Andric const DeclRefExpr *Node; 107806c3fb27SDimitry Andric 107906c3fb27SDimitry Andric public: 108006c3fb27SDimitry Andric UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result) 108106c3fb27SDimitry Andric : FixableGadget(Kind::UPCStandalonePointer), 108206c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) { 108306c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 108406c3fb27SDimitry Andric } 108506c3fb27SDimitry Andric 108606c3fb27SDimitry Andric static bool classof(const Gadget *G) { 108706c3fb27SDimitry Andric return G->getKind() == Kind::UPCStandalonePointer; 108806c3fb27SDimitry Andric } 108906c3fb27SDimitry Andric 109006c3fb27SDimitry Andric static Matcher matcher() { 109106c3fb27SDimitry Andric auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 1092*0fca6ea1SDimitry Andric auto target = expr(ignoringParenImpCasts( 1093*0fca6ea1SDimitry Andric declRefExpr(allOf(ArrayOrPtr, toSupportedVariable())) 1094*0fca6ea1SDimitry Andric .bind(DeclRefExprTag))); 109506c3fb27SDimitry Andric return stmt(isInUnspecifiedPointerContext(target)); 109606c3fb27SDimitry Andric } 109706c3fb27SDimitry Andric 1098*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 1099*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 1100*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 110106c3fb27SDimitry Andric 1102*0fca6ea1SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; } 110306c3fb27SDimitry Andric }; 110406c3fb27SDimitry Andric 110506c3fb27SDimitry Andric class PointerDereferenceGadget : public FixableGadget { 110606c3fb27SDimitry Andric static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 110706c3fb27SDimitry Andric static constexpr const char *const OperatorTag = "op"; 110806c3fb27SDimitry Andric 110906c3fb27SDimitry Andric const DeclRefExpr *BaseDeclRefExpr = nullptr; 111006c3fb27SDimitry Andric const UnaryOperator *Op = nullptr; 111106c3fb27SDimitry Andric 111206c3fb27SDimitry Andric public: 111306c3fb27SDimitry Andric PointerDereferenceGadget(const MatchFinder::MatchResult &Result) 111406c3fb27SDimitry Andric : FixableGadget(Kind::PointerDereference), 111506c3fb27SDimitry Andric BaseDeclRefExpr( 111606c3fb27SDimitry Andric Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 111706c3fb27SDimitry Andric Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {} 111806c3fb27SDimitry Andric 111906c3fb27SDimitry Andric static bool classof(const Gadget *G) { 112006c3fb27SDimitry Andric return G->getKind() == Kind::PointerDereference; 112106c3fb27SDimitry Andric } 112206c3fb27SDimitry Andric 112306c3fb27SDimitry Andric static Matcher matcher() { 112406c3fb27SDimitry Andric auto Target = 112506c3fb27SDimitry Andric unaryOperator( 112606c3fb27SDimitry Andric hasOperatorName("*"), 112706c3fb27SDimitry Andric has(expr(ignoringParenImpCasts( 11285f757f3fSDimitry Andric declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag))))) 112906c3fb27SDimitry Andric .bind(OperatorTag); 113006c3fb27SDimitry Andric 113106c3fb27SDimitry Andric return expr(isInUnspecifiedLvalueContext(Target)); 113206c3fb27SDimitry Andric } 113306c3fb27SDimitry Andric 113406c3fb27SDimitry Andric DeclUseList getClaimedVarUseSites() const override { 113506c3fb27SDimitry Andric return {BaseDeclRefExpr}; 113606c3fb27SDimitry Andric } 113706c3fb27SDimitry Andric 1138*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 1139*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 1140*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 114106c3fb27SDimitry Andric }; 114206c3fb27SDimitry Andric 114306c3fb27SDimitry Andric // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer 114406c3fb27SDimitry Andric // Context (see `isInUnspecifiedPointerContext`). 114506c3fb27SDimitry Andric // Note here `[]` is the built-in subscript operator. 114606c3fb27SDimitry Andric class UPCAddressofArraySubscriptGadget : public FixableGadget { 114706c3fb27SDimitry Andric private: 114806c3fb27SDimitry Andric static constexpr const char *const UPCAddressofArraySubscriptTag = 114906c3fb27SDimitry Andric "AddressofArraySubscriptUnderUPC"; 115006c3fb27SDimitry Andric const UnaryOperator *Node; // the `&DRE[any]` node 115106c3fb27SDimitry Andric 115206c3fb27SDimitry Andric public: 115306c3fb27SDimitry Andric UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result) 115406c3fb27SDimitry Andric : FixableGadget(Kind::ULCArraySubscript), 115506c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<UnaryOperator>( 115606c3fb27SDimitry Andric UPCAddressofArraySubscriptTag)) { 115706c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 115806c3fb27SDimitry Andric } 115906c3fb27SDimitry Andric 116006c3fb27SDimitry Andric static bool classof(const Gadget *G) { 116106c3fb27SDimitry Andric return G->getKind() == Kind::UPCAddressofArraySubscript; 116206c3fb27SDimitry Andric } 116306c3fb27SDimitry Andric 116406c3fb27SDimitry Andric static Matcher matcher() { 116506c3fb27SDimitry Andric return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 1166*0fca6ea1SDimitry Andric unaryOperator( 1167*0fca6ea1SDimitry Andric hasOperatorName("&"), 1168*0fca6ea1SDimitry Andric hasUnaryOperand(arraySubscriptExpr(hasBase( 1169*0fca6ea1SDimitry Andric ignoringParenImpCasts(declRefExpr(toSupportedVariable())))))) 117006c3fb27SDimitry Andric .bind(UPCAddressofArraySubscriptTag))))); 117106c3fb27SDimitry Andric } 117206c3fb27SDimitry Andric 1173*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 1174*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &) const override; 1175*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 117606c3fb27SDimitry Andric 117706c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 117806c3fb27SDimitry Andric const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr()); 117906c3fb27SDimitry Andric const auto *DRE = 1180*0fca6ea1SDimitry Andric cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts()); 118106c3fb27SDimitry Andric return {DRE}; 118206c3fb27SDimitry Andric } 118306c3fb27SDimitry Andric }; 1184bdd1243dSDimitry Andric } // namespace 1185bdd1243dSDimitry Andric 1186bdd1243dSDimitry Andric namespace { 1187bdd1243dSDimitry Andric // An auxiliary tracking facility for the fixit analysis. It helps connect 118806c3fb27SDimitry Andric // declarations to its uses and make sure we've covered all uses with our 118906c3fb27SDimitry Andric // analysis before we try to fix the declaration. 1190bdd1243dSDimitry Andric class DeclUseTracker { 1191bdd1243dSDimitry Andric using UseSetTy = SmallSet<const DeclRefExpr *, 16>; 1192bdd1243dSDimitry Andric using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>; 1193bdd1243dSDimitry Andric 1194bdd1243dSDimitry Andric // Allocate on the heap for easier move. 1195bdd1243dSDimitry Andric std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()}; 1196bdd1243dSDimitry Andric DefMapTy Defs{}; 1197bdd1243dSDimitry Andric 1198bdd1243dSDimitry Andric public: 1199bdd1243dSDimitry Andric DeclUseTracker() = default; 1200bdd1243dSDimitry Andric DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies. 120106c3fb27SDimitry Andric DeclUseTracker &operator=(const DeclUseTracker &) = delete; 1202bdd1243dSDimitry Andric DeclUseTracker(DeclUseTracker &&) = default; 1203bdd1243dSDimitry Andric DeclUseTracker &operator=(DeclUseTracker &&) = default; 1204bdd1243dSDimitry Andric 1205bdd1243dSDimitry Andric // Start tracking a freshly discovered DRE. 1206bdd1243dSDimitry Andric void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); } 1207bdd1243dSDimitry Andric 1208bdd1243dSDimitry Andric // Stop tracking the DRE as it's been fully figured out. 1209bdd1243dSDimitry Andric void claimUse(const DeclRefExpr *DRE) { 1210bdd1243dSDimitry Andric assert(Uses->count(DRE) && 1211bdd1243dSDimitry Andric "DRE not found or claimed by multiple matchers!"); 1212bdd1243dSDimitry Andric Uses->erase(DRE); 1213bdd1243dSDimitry Andric } 1214bdd1243dSDimitry Andric 1215bdd1243dSDimitry Andric // A variable is unclaimed if at least one use is unclaimed. 1216bdd1243dSDimitry Andric bool hasUnclaimedUses(const VarDecl *VD) const { 1217bdd1243dSDimitry Andric // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs? 1218bdd1243dSDimitry Andric return any_of(*Uses, [VD](const DeclRefExpr *DRE) { 1219bdd1243dSDimitry Andric return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl(); 1220bdd1243dSDimitry Andric }); 1221bdd1243dSDimitry Andric } 1222bdd1243dSDimitry Andric 12235f757f3fSDimitry Andric UseSetTy getUnclaimedUses(const VarDecl *VD) const { 12245f757f3fSDimitry Andric UseSetTy ReturnSet; 12255f757f3fSDimitry Andric for (auto use : *Uses) { 12265f757f3fSDimitry Andric if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) { 12275f757f3fSDimitry Andric ReturnSet.insert(use); 12285f757f3fSDimitry Andric } 12295f757f3fSDimitry Andric } 12305f757f3fSDimitry Andric return ReturnSet; 12315f757f3fSDimitry Andric } 12325f757f3fSDimitry Andric 1233bdd1243dSDimitry Andric void discoverDecl(const DeclStmt *DS) { 1234bdd1243dSDimitry Andric for (const Decl *D : DS->decls()) { 1235bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(D)) { 1236bdd1243dSDimitry Andric // FIXME: Assertion temporarily disabled due to a bug in 1237bdd1243dSDimitry Andric // ASTMatcher internal behavior in presence of GNU 1238bdd1243dSDimitry Andric // statement-expressions. We need to properly investigate this 1239bdd1243dSDimitry Andric // because it can screw up our algorithm in other ways. 1240bdd1243dSDimitry Andric // assert(Defs.count(VD) == 0 && "Definition already discovered!"); 1241bdd1243dSDimitry Andric Defs[VD] = DS; 1242bdd1243dSDimitry Andric } 1243bdd1243dSDimitry Andric } 1244bdd1243dSDimitry Andric } 1245bdd1243dSDimitry Andric 1246bdd1243dSDimitry Andric const DeclStmt *lookupDecl(const VarDecl *VD) const { 12475f757f3fSDimitry Andric return Defs.lookup(VD); 1248bdd1243dSDimitry Andric } 1249bdd1243dSDimitry Andric }; 1250bdd1243dSDimitry Andric } // namespace 1251bdd1243dSDimitry Andric 125206c3fb27SDimitry Andric // Representing a pointer type expression of the form `++Ptr` in an Unspecified 125306c3fb27SDimitry Andric // Pointer Context (UPC): 125406c3fb27SDimitry Andric class UPCPreIncrementGadget : public FixableGadget { 125506c3fb27SDimitry Andric private: 125606c3fb27SDimitry Andric static constexpr const char *const UPCPreIncrementTag = 125706c3fb27SDimitry Andric "PointerPreIncrementUnderUPC"; 125806c3fb27SDimitry Andric const UnaryOperator *Node; // the `++Ptr` node 125906c3fb27SDimitry Andric 126006c3fb27SDimitry Andric public: 126106c3fb27SDimitry Andric UPCPreIncrementGadget(const MatchFinder::MatchResult &Result) 126206c3fb27SDimitry Andric : FixableGadget(Kind::UPCPreIncrement), 126306c3fb27SDimitry Andric Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) { 126406c3fb27SDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 126506c3fb27SDimitry Andric } 126606c3fb27SDimitry Andric 126706c3fb27SDimitry Andric static bool classof(const Gadget *G) { 126806c3fb27SDimitry Andric return G->getKind() == Kind::UPCPreIncrement; 126906c3fb27SDimitry Andric } 127006c3fb27SDimitry Andric 127106c3fb27SDimitry Andric static Matcher matcher() { 127206c3fb27SDimitry Andric // Note here we match `++Ptr` for any expression `Ptr` of pointer type. 127306c3fb27SDimitry Andric // Although currently we can only provide fix-its when `Ptr` is a DRE, we 127406c3fb27SDimitry Andric // can have the matcher be general, so long as `getClaimedVarUseSites` does 127506c3fb27SDimitry Andric // things right. 127606c3fb27SDimitry Andric return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 127706c3fb27SDimitry Andric unaryOperator(isPreInc(), 1278*0fca6ea1SDimitry Andric hasUnaryOperand(declRefExpr(toSupportedVariable()))) 1279*0fca6ea1SDimitry Andric .bind(UPCPreIncrementTag))))); 128006c3fb27SDimitry Andric } 128106c3fb27SDimitry Andric 1282*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 1283*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 1284*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 128506c3fb27SDimitry Andric 128606c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 128706c3fb27SDimitry Andric return {dyn_cast<DeclRefExpr>(Node->getSubExpr())}; 128806c3fb27SDimitry Andric } 128906c3fb27SDimitry Andric }; 129006c3fb27SDimitry Andric 12915f757f3fSDimitry Andric // Representing a pointer type expression of the form `Ptr += n` in an 12925f757f3fSDimitry Andric // Unspecified Untyped Context (UUC): 12935f757f3fSDimitry Andric class UUCAddAssignGadget : public FixableGadget { 12945f757f3fSDimitry Andric private: 12955f757f3fSDimitry Andric static constexpr const char *const UUCAddAssignTag = 12965f757f3fSDimitry Andric "PointerAddAssignUnderUUC"; 12975f757f3fSDimitry Andric static constexpr const char *const OffsetTag = "Offset"; 12985f757f3fSDimitry Andric 12995f757f3fSDimitry Andric const BinaryOperator *Node; // the `Ptr += n` node 13005f757f3fSDimitry Andric const Expr *Offset = nullptr; 13015f757f3fSDimitry Andric 13025f757f3fSDimitry Andric public: 13035f757f3fSDimitry Andric UUCAddAssignGadget(const MatchFinder::MatchResult &Result) 13045f757f3fSDimitry Andric : FixableGadget(Kind::UUCAddAssign), 13055f757f3fSDimitry Andric Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)), 13065f757f3fSDimitry Andric Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) { 13075f757f3fSDimitry Andric assert(Node != nullptr && "Expecting a non-null matching result"); 13085f757f3fSDimitry Andric } 13095f757f3fSDimitry Andric 13105f757f3fSDimitry Andric static bool classof(const Gadget *G) { 13115f757f3fSDimitry Andric return G->getKind() == Kind::UUCAddAssign; 13125f757f3fSDimitry Andric } 13135f757f3fSDimitry Andric 13145f757f3fSDimitry Andric static Matcher matcher() { 1315*0fca6ea1SDimitry Andric // clang-format off 13165f757f3fSDimitry Andric return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts( 13175f757f3fSDimitry Andric binaryOperator(hasOperatorName("+="), 1318*0fca6ea1SDimitry Andric hasLHS( 1319*0fca6ea1SDimitry Andric declRefExpr( 1320*0fca6ea1SDimitry Andric hasPointerType(), 1321*0fca6ea1SDimitry Andric toSupportedVariable())), 13225f757f3fSDimitry Andric hasRHS(expr().bind(OffsetTag))) 13235f757f3fSDimitry Andric .bind(UUCAddAssignTag))))); 1324*0fca6ea1SDimitry Andric // clang-format on 13255f757f3fSDimitry Andric } 13265f757f3fSDimitry Andric 1327*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 1328*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &S) const override; 1329*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 13305f757f3fSDimitry Andric 13315f757f3fSDimitry Andric virtual DeclUseList getClaimedVarUseSites() const override { 13325f757f3fSDimitry Andric return {dyn_cast<DeclRefExpr>(Node->getLHS())}; 13335f757f3fSDimitry Andric } 13345f757f3fSDimitry Andric }; 13355f757f3fSDimitry Andric 133606c3fb27SDimitry Andric // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 + 133706c3fb27SDimitry Andric // ptr)`: 133806c3fb27SDimitry Andric class DerefSimplePtrArithFixableGadget : public FixableGadget { 133906c3fb27SDimitry Andric static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 134006c3fb27SDimitry Andric static constexpr const char *const DerefOpTag = "DerefOp"; 134106c3fb27SDimitry Andric static constexpr const char *const AddOpTag = "AddOp"; 134206c3fb27SDimitry Andric static constexpr const char *const OffsetTag = "Offset"; 134306c3fb27SDimitry Andric 134406c3fb27SDimitry Andric const DeclRefExpr *BaseDeclRefExpr = nullptr; 134506c3fb27SDimitry Andric const UnaryOperator *DerefOp = nullptr; 134606c3fb27SDimitry Andric const BinaryOperator *AddOp = nullptr; 134706c3fb27SDimitry Andric const IntegerLiteral *Offset = nullptr; 134806c3fb27SDimitry Andric 134906c3fb27SDimitry Andric public: 135006c3fb27SDimitry Andric DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result) 135106c3fb27SDimitry Andric : FixableGadget(Kind::DerefSimplePtrArithFixable), 135206c3fb27SDimitry Andric BaseDeclRefExpr( 135306c3fb27SDimitry Andric Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 135406c3fb27SDimitry Andric DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)), 135506c3fb27SDimitry Andric AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)), 135606c3fb27SDimitry Andric Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {} 135706c3fb27SDimitry Andric 135806c3fb27SDimitry Andric static Matcher matcher() { 135906c3fb27SDimitry Andric // clang-format off 136006c3fb27SDimitry Andric auto ThePtr = expr(hasPointerType(), 13615f757f3fSDimitry Andric ignoringImpCasts(declRefExpr(toSupportedVariable()). 13625f757f3fSDimitry Andric bind(BaseDeclRefExprTag))); 136306c3fb27SDimitry Andric auto PlusOverPtrAndInteger = expr(anyOf( 136406c3fb27SDimitry Andric binaryOperator(hasOperatorName("+"), hasLHS(ThePtr), 136506c3fb27SDimitry Andric hasRHS(integerLiteral().bind(OffsetTag))) 136606c3fb27SDimitry Andric .bind(AddOpTag), 136706c3fb27SDimitry Andric binaryOperator(hasOperatorName("+"), hasRHS(ThePtr), 136806c3fb27SDimitry Andric hasLHS(integerLiteral().bind(OffsetTag))) 136906c3fb27SDimitry Andric .bind(AddOpTag))); 137006c3fb27SDimitry Andric return isInUnspecifiedLvalueContext(unaryOperator( 137106c3fb27SDimitry Andric hasOperatorName("*"), 137206c3fb27SDimitry Andric hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger))) 137306c3fb27SDimitry Andric .bind(DerefOpTag)); 137406c3fb27SDimitry Andric // clang-format on 137506c3fb27SDimitry Andric } 137606c3fb27SDimitry Andric 1377*0fca6ea1SDimitry Andric virtual std::optional<FixItList> 1378*0fca6ea1SDimitry Andric getFixits(const FixitStrategy &s) const final; 1379*0fca6ea1SDimitry Andric SourceLocation getSourceLoc() const override { 1380*0fca6ea1SDimitry Andric return DerefOp->getBeginLoc(); 1381*0fca6ea1SDimitry Andric } 138206c3fb27SDimitry Andric 138306c3fb27SDimitry Andric virtual DeclUseList getClaimedVarUseSites() const final { 138406c3fb27SDimitry Andric return {BaseDeclRefExpr}; 138506c3fb27SDimitry Andric } 138606c3fb27SDimitry Andric }; 138706c3fb27SDimitry Andric 1388bdd1243dSDimitry Andric /// Scan the function and return a list of gadgets found with provided kits. 138906c3fb27SDimitry Andric static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker> 139006c3fb27SDimitry Andric findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler, 139106c3fb27SDimitry Andric bool EmitSuggestions) { 1392bdd1243dSDimitry Andric 1393bdd1243dSDimitry Andric struct GadgetFinderCallback : MatchFinder::MatchCallback { 1394bdd1243dSDimitry Andric FixableGadgetList FixableGadgets; 1395bdd1243dSDimitry Andric WarningGadgetList WarningGadgets; 1396bdd1243dSDimitry Andric DeclUseTracker Tracker; 1397bdd1243dSDimitry Andric 1398bdd1243dSDimitry Andric void run(const MatchFinder::MatchResult &Result) override { 1399bdd1243dSDimitry Andric // In debug mode, assert that we've found exactly one gadget. 1400bdd1243dSDimitry Andric // This helps us avoid conflicts in .bind() tags. 1401bdd1243dSDimitry Andric #if NDEBUG 1402bdd1243dSDimitry Andric #define NEXT return 1403bdd1243dSDimitry Andric #else 1404bdd1243dSDimitry Andric [[maybe_unused]] int numFound = 0; 1405bdd1243dSDimitry Andric #define NEXT ++numFound 1406bdd1243dSDimitry Andric #endif 1407bdd1243dSDimitry Andric 1408bdd1243dSDimitry Andric if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) { 1409bdd1243dSDimitry Andric Tracker.discoverUse(DRE); 1410bdd1243dSDimitry Andric NEXT; 1411bdd1243dSDimitry Andric } 1412bdd1243dSDimitry Andric 1413bdd1243dSDimitry Andric if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) { 1414bdd1243dSDimitry Andric Tracker.discoverDecl(DS); 1415bdd1243dSDimitry Andric NEXT; 1416bdd1243dSDimitry Andric } 1417bdd1243dSDimitry Andric 1418bdd1243dSDimitry Andric // Figure out which matcher we've found, and call the appropriate 1419bdd1243dSDimitry Andric // subclass constructor. 1420bdd1243dSDimitry Andric // FIXME: Can we do this more logarithmically? 1421bdd1243dSDimitry Andric #define FIXABLE_GADGET(name) \ 1422bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1423bdd1243dSDimitry Andric FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1424bdd1243dSDimitry Andric NEXT; \ 1425bdd1243dSDimitry Andric } 1426bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1427bdd1243dSDimitry Andric #define WARNING_GADGET(name) \ 1428bdd1243dSDimitry Andric if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1429bdd1243dSDimitry Andric WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1430bdd1243dSDimitry Andric NEXT; \ 1431bdd1243dSDimitry Andric } 1432bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1433bdd1243dSDimitry Andric 1434bdd1243dSDimitry Andric assert(numFound >= 1 && "Gadgets not found in match result!"); 1435bdd1243dSDimitry Andric assert(numFound <= 1 && "Conflicting bind tags in gadgets!"); 1436bdd1243dSDimitry Andric } 1437bdd1243dSDimitry Andric }; 1438bdd1243dSDimitry Andric 1439bdd1243dSDimitry Andric MatchFinder M; 1440bdd1243dSDimitry Andric GadgetFinderCallback CB; 1441bdd1243dSDimitry Andric 1442bdd1243dSDimitry Andric // clang-format off 1443bdd1243dSDimitry Andric M.addMatcher( 144406c3fb27SDimitry Andric stmt( 144506c3fb27SDimitry Andric forEachDescendantEvaluatedStmt(stmt(anyOf( 1446bdd1243dSDimitry Andric // Add Gadget::matcher() for every gadget in the registry. 144706c3fb27SDimitry Andric #define WARNING_GADGET(x) \ 144806c3fb27SDimitry Andric allOf(x ## Gadget::matcher().bind(#x), \ 144906c3fb27SDimitry Andric notInSafeBufferOptOut(&Handler)), 1450*0fca6ea1SDimitry Andric #define WARNING_CONTAINER_GADGET(x) \ 1451*0fca6ea1SDimitry Andric allOf(x ## Gadget::matcher().bind(#x), \ 1452*0fca6ea1SDimitry Andric notInSafeBufferOptOut(&Handler), \ 1453*0fca6ea1SDimitry Andric unless(ignoreUnsafeBufferInContainer(&Handler))), 145406c3fb27SDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 145506c3fb27SDimitry Andric // Avoid a hanging comma. 145606c3fb27SDimitry Andric unless(stmt()) 145706c3fb27SDimitry Andric ))) 145806c3fb27SDimitry Andric ), 145906c3fb27SDimitry Andric &CB 146006c3fb27SDimitry Andric ); 146106c3fb27SDimitry Andric // clang-format on 146206c3fb27SDimitry Andric 146306c3fb27SDimitry Andric if (EmitSuggestions) { 146406c3fb27SDimitry Andric // clang-format off 146506c3fb27SDimitry Andric M.addMatcher( 146606c3fb27SDimitry Andric stmt( 146706c3fb27SDimitry Andric forEachDescendantStmt(stmt(eachOf( 146806c3fb27SDimitry Andric #define FIXABLE_GADGET(x) \ 1469bdd1243dSDimitry Andric x ## Gadget::matcher().bind(#x), 1470bdd1243dSDimitry Andric #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1471bdd1243dSDimitry Andric // In parallel, match all DeclRefExprs so that to find out 1472bdd1243dSDimitry Andric // whether there are any uncovered by gadgets. 1473bdd1243dSDimitry Andric declRefExpr(anyOf(hasPointerType(), hasArrayType()), 14745f757f3fSDimitry Andric to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"), 1475bdd1243dSDimitry Andric // Also match DeclStmts because we'll need them when fixing 1476bdd1243dSDimitry Andric // their underlying VarDecls that otherwise don't have 1477bdd1243dSDimitry Andric // any backreferences to DeclStmts. 1478bdd1243dSDimitry Andric declStmt().bind("any_ds") 147906c3fb27SDimitry Andric ))) 148006c3fb27SDimitry Andric ), 1481bdd1243dSDimitry Andric &CB 1482bdd1243dSDimitry Andric ); 1483bdd1243dSDimitry Andric // clang-format on 148406c3fb27SDimitry Andric } 1485bdd1243dSDimitry Andric 1486bdd1243dSDimitry Andric M.match(*D->getBody(), D->getASTContext()); 148706c3fb27SDimitry Andric return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets), 148806c3fb27SDimitry Andric std::move(CB.Tracker)}; 1489bdd1243dSDimitry Andric } 1490bdd1243dSDimitry Andric 149106c3fb27SDimitry Andric // Compares AST nodes by source locations. 149206c3fb27SDimitry Andric template <typename NodeTy> struct CompareNode { 149306c3fb27SDimitry Andric bool operator()(const NodeTy *N1, const NodeTy *N2) const { 149406c3fb27SDimitry Andric return N1->getBeginLoc().getRawEncoding() < 149506c3fb27SDimitry Andric N2->getBeginLoc().getRawEncoding(); 149606c3fb27SDimitry Andric } 149706c3fb27SDimitry Andric }; 149806c3fb27SDimitry Andric 1499bdd1243dSDimitry Andric struct WarningGadgetSets { 150006c3fb27SDimitry Andric std::map<const VarDecl *, std::set<const WarningGadget *>, 150106c3fb27SDimitry Andric // To keep keys sorted by their locations in the map so that the 150206c3fb27SDimitry Andric // order is deterministic: 150306c3fb27SDimitry Andric CompareNode<VarDecl>> 150406c3fb27SDimitry Andric byVar; 1505bdd1243dSDimitry Andric // These Gadgets are not related to pointer variables (e. g. temporaries). 150606c3fb27SDimitry Andric llvm::SmallVector<const WarningGadget *, 16> noVar; 1507bdd1243dSDimitry Andric }; 1508bdd1243dSDimitry Andric 1509bdd1243dSDimitry Andric static WarningGadgetSets 151006c3fb27SDimitry Andric groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) { 1511bdd1243dSDimitry Andric WarningGadgetSets result; 1512bdd1243dSDimitry Andric // If some gadgets cover more than one 1513bdd1243dSDimitry Andric // variable, they'll appear more than once in the map. 1514bdd1243dSDimitry Andric for (auto &G : AllUnsafeOperations) { 1515bdd1243dSDimitry Andric DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites(); 1516bdd1243dSDimitry Andric 1517bdd1243dSDimitry Andric bool AssociatedWithVarDecl = false; 1518bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : ClaimedVarUseSites) { 1519bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 152006c3fb27SDimitry Andric result.byVar[VD].insert(G.get()); 1521bdd1243dSDimitry Andric AssociatedWithVarDecl = true; 1522bdd1243dSDimitry Andric } 1523bdd1243dSDimitry Andric } 1524bdd1243dSDimitry Andric 1525bdd1243dSDimitry Andric if (!AssociatedWithVarDecl) { 152606c3fb27SDimitry Andric result.noVar.push_back(G.get()); 1527bdd1243dSDimitry Andric continue; 1528bdd1243dSDimitry Andric } 1529bdd1243dSDimitry Andric } 1530bdd1243dSDimitry Andric return result; 1531bdd1243dSDimitry Andric } 1532bdd1243dSDimitry Andric 1533bdd1243dSDimitry Andric struct FixableGadgetSets { 15345f757f3fSDimitry Andric std::map<const VarDecl *, std::set<const FixableGadget *>, 15355f757f3fSDimitry Andric // To keep keys sorted by their locations in the map so that the 15365f757f3fSDimitry Andric // order is deterministic: 15375f757f3fSDimitry Andric CompareNode<VarDecl>> 15385f757f3fSDimitry Andric byVar; 1539bdd1243dSDimitry Andric }; 1540bdd1243dSDimitry Andric 1541bdd1243dSDimitry Andric static FixableGadgetSets 1542bdd1243dSDimitry Andric groupFixablesByVar(FixableGadgetList &&AllFixableOperations) { 1543bdd1243dSDimitry Andric FixableGadgetSets FixablesForUnsafeVars; 1544bdd1243dSDimitry Andric for (auto &F : AllFixableOperations) { 1545bdd1243dSDimitry Andric DeclUseList DREs = F->getClaimedVarUseSites(); 1546bdd1243dSDimitry Andric 1547bdd1243dSDimitry Andric for (const DeclRefExpr *DRE : DREs) { 1548bdd1243dSDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 154906c3fb27SDimitry Andric FixablesForUnsafeVars.byVar[VD].insert(F.get()); 1550bdd1243dSDimitry Andric } 1551bdd1243dSDimitry Andric } 1552bdd1243dSDimitry Andric } 1553bdd1243dSDimitry Andric return FixablesForUnsafeVars; 1554bdd1243dSDimitry Andric } 1555bdd1243dSDimitry Andric 155606c3fb27SDimitry Andric bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts, 155706c3fb27SDimitry Andric const SourceManager &SM) { 155806c3fb27SDimitry Andric // A simple interval overlap detection algorithm. Sorts all ranges by their 155906c3fb27SDimitry Andric // begin location then finds the first overlap in one pass. 156006c3fb27SDimitry Andric std::vector<const FixItHint *> All; // a copy of `FixIts` 156106c3fb27SDimitry Andric 156206c3fb27SDimitry Andric for (const FixItHint &H : FixIts) 156306c3fb27SDimitry Andric All.push_back(&H); 156406c3fb27SDimitry Andric std::sort(All.begin(), All.end(), 156506c3fb27SDimitry Andric [&SM](const FixItHint *H1, const FixItHint *H2) { 156606c3fb27SDimitry Andric return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(), 156706c3fb27SDimitry Andric H2->RemoveRange.getBegin()); 156806c3fb27SDimitry Andric }); 156906c3fb27SDimitry Andric 157006c3fb27SDimitry Andric const FixItHint *CurrHint = nullptr; 157106c3fb27SDimitry Andric 157206c3fb27SDimitry Andric for (const FixItHint *Hint : All) { 157306c3fb27SDimitry Andric if (!CurrHint || 157406c3fb27SDimitry Andric SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(), 157506c3fb27SDimitry Andric Hint->RemoveRange.getBegin())) { 157606c3fb27SDimitry Andric // Either to initialize `CurrHint` or `CurrHint` does not 157706c3fb27SDimitry Andric // overlap with `Hint`: 157806c3fb27SDimitry Andric CurrHint = Hint; 157906c3fb27SDimitry Andric } else 158006c3fb27SDimitry Andric // In case `Hint` overlaps the `CurrHint`, we found at least one 158106c3fb27SDimitry Andric // conflict: 158206c3fb27SDimitry Andric return true; 158306c3fb27SDimitry Andric } 158406c3fb27SDimitry Andric return false; 158506c3fb27SDimitry Andric } 158606c3fb27SDimitry Andric 158706c3fb27SDimitry Andric std::optional<FixItList> 1588*0fca6ea1SDimitry Andric PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const { 158906c3fb27SDimitry Andric const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl()); 159006c3fb27SDimitry Andric const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl()); 159106c3fb27SDimitry Andric switch (S.lookup(LeftVD)) { 1592*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Span: 1593*0fca6ea1SDimitry Andric if (S.lookup(RightVD) == FixitStrategy::Kind::Span) 159406c3fb27SDimitry Andric return FixItList{}; 159506c3fb27SDimitry Andric return std::nullopt; 1596*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Wontfix: 159706c3fb27SDimitry Andric return std::nullopt; 1598*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Iterator: 1599*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Array: 1600*0fca6ea1SDimitry Andric return std::nullopt; 1601*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Vector: 160206c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 160306c3fb27SDimitry Andric } 160406c3fb27SDimitry Andric return std::nullopt; 160506c3fb27SDimitry Andric } 160606c3fb27SDimitry Andric 1607*0fca6ea1SDimitry Andric /// \returns fixit that adds .data() call after \DRE. 1608*0fca6ea1SDimitry Andric static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx, 1609*0fca6ea1SDimitry Andric const DeclRefExpr *DRE); 1610*0fca6ea1SDimitry Andric 161106c3fb27SDimitry Andric std::optional<FixItList> 1612*0fca6ea1SDimitry Andric CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const { 1613*0fca6ea1SDimitry Andric const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl()); 1614*0fca6ea1SDimitry Andric const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl()); 1615*0fca6ea1SDimitry Andric // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is 1616*0fca6ea1SDimitry Andric // non-trivial. 1617*0fca6ea1SDimitry Andric // 1618*0fca6ea1SDimitry Andric // CArrayToPtrAssignmentGadget doesn't have strategy implications because 1619*0fca6ea1SDimitry Andric // constant size array propagates its bounds. Because of that LHS and RHS are 1620*0fca6ea1SDimitry Andric // addressed by two different fixits. 1621*0fca6ea1SDimitry Andric // 1622*0fca6ea1SDimitry Andric // At the same time FixitStrategy S doesn't reflect what group a fixit belongs 1623*0fca6ea1SDimitry Andric // to and can't be generally relied on in multi-variable Fixables! 1624*0fca6ea1SDimitry Andric // 1625*0fca6ea1SDimitry Andric // E. g. If an instance of this gadget is fixing variable on LHS then the 1626*0fca6ea1SDimitry Andric // variable on RHS is fixed by a different fixit and its strategy for LHS 1627*0fca6ea1SDimitry Andric // fixit is as if Wontfix. 1628*0fca6ea1SDimitry Andric // 1629*0fca6ea1SDimitry Andric // The only exception is Wontfix strategy for a given variable as that is 1630*0fca6ea1SDimitry Andric // valid for any fixit produced for the given input source code. 1631*0fca6ea1SDimitry Andric if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) { 1632*0fca6ea1SDimitry Andric if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) { 1633*0fca6ea1SDimitry Andric return FixItList{}; 1634*0fca6ea1SDimitry Andric } 1635*0fca6ea1SDimitry Andric } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) { 1636*0fca6ea1SDimitry Andric if (S.lookup(RightVD) == FixitStrategy::Kind::Array) { 1637*0fca6ea1SDimitry Andric return createDataFixit(RightVD->getASTContext(), PtrRHS); 1638*0fca6ea1SDimitry Andric } 1639*0fca6ea1SDimitry Andric } 1640*0fca6ea1SDimitry Andric return std::nullopt; 1641*0fca6ea1SDimitry Andric } 1642*0fca6ea1SDimitry Andric 1643*0fca6ea1SDimitry Andric std::optional<FixItList> 1644*0fca6ea1SDimitry Andric PointerInitGadget::getFixits(const FixitStrategy &S) const { 164506c3fb27SDimitry Andric const auto *LeftVD = PtrInitLHS; 164606c3fb27SDimitry Andric const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl()); 164706c3fb27SDimitry Andric switch (S.lookup(LeftVD)) { 1648*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Span: 1649*0fca6ea1SDimitry Andric if (S.lookup(RightVD) == FixitStrategy::Kind::Span) 165006c3fb27SDimitry Andric return FixItList{}; 165106c3fb27SDimitry Andric return std::nullopt; 1652*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Wontfix: 165306c3fb27SDimitry Andric return std::nullopt; 1654*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Iterator: 1655*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Array: 1656*0fca6ea1SDimitry Andric return std::nullopt; 1657*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Vector: 165806c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 165906c3fb27SDimitry Andric } 166006c3fb27SDimitry Andric return std::nullopt; 166106c3fb27SDimitry Andric } 166206c3fb27SDimitry Andric 16635f757f3fSDimitry Andric static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, 16645f757f3fSDimitry Andric const ASTContext &Ctx) { 16655f757f3fSDimitry Andric if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) { 16665f757f3fSDimitry Andric if (ConstVal->isNegative()) 16675f757f3fSDimitry Andric return false; 16685f757f3fSDimitry Andric } else if (!Expr->getType()->isUnsignedIntegerType()) 16695f757f3fSDimitry Andric return false; 16705f757f3fSDimitry Andric return true; 16715f757f3fSDimitry Andric } 16725f757f3fSDimitry Andric 167306c3fb27SDimitry Andric std::optional<FixItList> 1674*0fca6ea1SDimitry Andric ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const { 167506c3fb27SDimitry Andric if (const auto *DRE = 167606c3fb27SDimitry Andric dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) 167706c3fb27SDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 167806c3fb27SDimitry Andric switch (S.lookup(VD)) { 1679*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Span: { 16805f757f3fSDimitry Andric 168106c3fb27SDimitry Andric // If the index has a negative constant value, we give up as no valid 168206c3fb27SDimitry Andric // fix-it can be generated: 168306c3fb27SDimitry Andric const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in! 168406c3fb27SDimitry Andric VD->getASTContext(); 16855f757f3fSDimitry Andric if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx)) 168606c3fb27SDimitry Andric return std::nullopt; 168706c3fb27SDimitry Andric // no-op is a good fix-it, otherwise 168806c3fb27SDimitry Andric return FixItList{}; 168906c3fb27SDimitry Andric } 1690*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Array: 1691*0fca6ea1SDimitry Andric return FixItList{}; 1692*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Wontfix: 1693*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Iterator: 1694*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Vector: 169506c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 169606c3fb27SDimitry Andric } 169706c3fb27SDimitry Andric } 169806c3fb27SDimitry Andric return std::nullopt; 169906c3fb27SDimitry Andric } 170006c3fb27SDimitry Andric 170106c3fb27SDimitry Andric static std::optional<FixItList> // forward declaration 170206c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node); 170306c3fb27SDimitry Andric 170406c3fb27SDimitry Andric std::optional<FixItList> 1705*0fca6ea1SDimitry Andric UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const { 170606c3fb27SDimitry Andric auto DREs = getClaimedVarUseSites(); 170706c3fb27SDimitry Andric const auto *VD = cast<VarDecl>(DREs.front()->getDecl()); 170806c3fb27SDimitry Andric 170906c3fb27SDimitry Andric switch (S.lookup(VD)) { 1710*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Span: 171106c3fb27SDimitry Andric return fixUPCAddressofArraySubscriptWithSpan(Node); 1712*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Wontfix: 1713*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Iterator: 1714*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Array: 1715*0fca6ea1SDimitry Andric return std::nullopt; 1716*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Vector: 171706c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 171806c3fb27SDimitry Andric } 171906c3fb27SDimitry Andric return std::nullopt; // something went wrong, no fix-it 172006c3fb27SDimitry Andric } 172106c3fb27SDimitry Andric 172206c3fb27SDimitry Andric // FIXME: this function should be customizable through format 172306c3fb27SDimitry Andric static StringRef getEndOfLine() { 172406c3fb27SDimitry Andric static const char *const EOL = "\n"; 172506c3fb27SDimitry Andric return EOL; 172606c3fb27SDimitry Andric } 172706c3fb27SDimitry Andric 172806c3fb27SDimitry Andric // Returns the text indicating that the user needs to provide input there: 172906c3fb27SDimitry Andric std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") { 173006c3fb27SDimitry Andric std::string s = std::string("<# "); 173106c3fb27SDimitry Andric s += HintTextToUser; 173206c3fb27SDimitry Andric s += " #>"; 173306c3fb27SDimitry Andric return s; 173406c3fb27SDimitry Andric } 173506c3fb27SDimitry Andric 173606c3fb27SDimitry Andric // Return the source location of the last character of the AST `Node`. 173706c3fb27SDimitry Andric template <typename NodeTy> 173806c3fb27SDimitry Andric static std::optional<SourceLocation> 173906c3fb27SDimitry Andric getEndCharLoc(const NodeTy *Node, const SourceManager &SM, 174006c3fb27SDimitry Andric const LangOptions &LangOpts) { 174106c3fb27SDimitry Andric unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts); 174206c3fb27SDimitry Andric SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1); 174306c3fb27SDimitry Andric 174406c3fb27SDimitry Andric if (Loc.isValid()) 174506c3fb27SDimitry Andric return Loc; 174606c3fb27SDimitry Andric 174706c3fb27SDimitry Andric return std::nullopt; 174806c3fb27SDimitry Andric } 174906c3fb27SDimitry Andric 175006c3fb27SDimitry Andric // Return the source location just past the last character of the AST `Node`. 175106c3fb27SDimitry Andric template <typename NodeTy> 175206c3fb27SDimitry Andric static std::optional<SourceLocation> getPastLoc(const NodeTy *Node, 175306c3fb27SDimitry Andric const SourceManager &SM, 175406c3fb27SDimitry Andric const LangOptions &LangOpts) { 175506c3fb27SDimitry Andric SourceLocation Loc = 175606c3fb27SDimitry Andric Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts); 175706c3fb27SDimitry Andric if (Loc.isValid()) 175806c3fb27SDimitry Andric return Loc; 175906c3fb27SDimitry Andric return std::nullopt; 176006c3fb27SDimitry Andric } 176106c3fb27SDimitry Andric 176206c3fb27SDimitry Andric // Return text representation of an `Expr`. 176306c3fb27SDimitry Andric static std::optional<StringRef> getExprText(const Expr *E, 176406c3fb27SDimitry Andric const SourceManager &SM, 176506c3fb27SDimitry Andric const LangOptions &LangOpts) { 176606c3fb27SDimitry Andric std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts); 176706c3fb27SDimitry Andric 176806c3fb27SDimitry Andric if (LastCharLoc) 176906c3fb27SDimitry Andric return Lexer::getSourceText( 177006c3fb27SDimitry Andric CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM, 177106c3fb27SDimitry Andric LangOpts); 177206c3fb27SDimitry Andric 177306c3fb27SDimitry Andric return std::nullopt; 177406c3fb27SDimitry Andric } 177506c3fb27SDimitry Andric 177606c3fb27SDimitry Andric // Returns the literal text in `SourceRange SR`, if `SR` is a valid range. 177706c3fb27SDimitry Andric static std::optional<StringRef> getRangeText(SourceRange SR, 177806c3fb27SDimitry Andric const SourceManager &SM, 177906c3fb27SDimitry Andric const LangOptions &LangOpts) { 178006c3fb27SDimitry Andric bool Invalid = false; 17815f757f3fSDimitry Andric CharSourceRange CSR = CharSourceRange::getCharRange(SR); 178206c3fb27SDimitry Andric StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid); 178306c3fb27SDimitry Andric 178406c3fb27SDimitry Andric if (!Invalid) 178506c3fb27SDimitry Andric return Text; 178606c3fb27SDimitry Andric return std::nullopt; 178706c3fb27SDimitry Andric } 178806c3fb27SDimitry Andric 17895f757f3fSDimitry Andric // Returns the begin location of the identifier of the given variable 17905f757f3fSDimitry Andric // declaration. 17915f757f3fSDimitry Andric static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) { 17925f757f3fSDimitry Andric // According to the implementation of `VarDecl`, `VD->getLocation()` actually 17935f757f3fSDimitry Andric // returns the begin location of the identifier of the declaration: 17945f757f3fSDimitry Andric return VD->getLocation(); 17955f757f3fSDimitry Andric } 17965f757f3fSDimitry Andric 17975f757f3fSDimitry Andric // Returns the literal text of the identifier of the given variable declaration. 17985f757f3fSDimitry Andric static std::optional<StringRef> 17995f757f3fSDimitry Andric getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM, 18005f757f3fSDimitry Andric const LangOptions &LangOpts) { 18015f757f3fSDimitry Andric SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD); 18025f757f3fSDimitry Andric SourceLocation ParmIdentEndLoc = 18035f757f3fSDimitry Andric Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts); 18045f757f3fSDimitry Andric 18055f757f3fSDimitry Andric if (ParmIdentEndLoc.isMacroID() && 18065f757f3fSDimitry Andric !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts)) 18075f757f3fSDimitry Andric return std::nullopt; 18085f757f3fSDimitry Andric return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts); 18095f757f3fSDimitry Andric } 18105f757f3fSDimitry Andric 18115f757f3fSDimitry Andric // We cannot fix a variable declaration if it has some other specifiers than the 18125f757f3fSDimitry Andric // type specifier. Because the source ranges of those specifiers could overlap 18135f757f3fSDimitry Andric // with the source range that is being replaced using fix-its. Especially when 18145f757f3fSDimitry Andric // we often cannot obtain accurate source ranges of cv-qualified type 18155f757f3fSDimitry Andric // specifiers. 18165f757f3fSDimitry Andric // FIXME: also deal with type attributes 18175f757f3fSDimitry Andric static bool hasUnsupportedSpecifiers(const VarDecl *VD, 18185f757f3fSDimitry Andric const SourceManager &SM) { 18195f757f3fSDimitry Andric // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the 18205f757f3fSDimitry Andric // source range of `VD`: 18215f757f3fSDimitry Andric bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool { 18225f757f3fSDimitry Andric return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(), 18235f757f3fSDimitry Andric VD->getBeginLoc())) && 18245f757f3fSDimitry Andric !(SM.isBeforeInTranslationUnit(VD->getEndLoc(), 18255f757f3fSDimitry Andric At->getRange().getBegin())); 18265f757f3fSDimitry Andric }); 18275f757f3fSDimitry Andric return VD->isInlineSpecified() || VD->isConstexpr() || 18285f757f3fSDimitry Andric VD->hasConstantInitialization() || !VD->hasLocalStorage() || 18295f757f3fSDimitry Andric AttrRangeOverlapping; 18305f757f3fSDimitry Andric } 18315f757f3fSDimitry Andric 18325f757f3fSDimitry Andric // Returns the `SourceRange` of `D`. The reason why this function exists is 18335f757f3fSDimitry Andric // that `D->getSourceRange()` may return a range where the end location is the 18345f757f3fSDimitry Andric // starting location of the last token. The end location of the source range 18355f757f3fSDimitry Andric // returned by this function is the last location of the last token. 18365f757f3fSDimitry Andric static SourceRange getSourceRangeToTokenEnd(const Decl *D, 18375f757f3fSDimitry Andric const SourceManager &SM, 18385f757f3fSDimitry Andric const LangOptions &LangOpts) { 18395f757f3fSDimitry Andric SourceLocation Begin = D->getBeginLoc(); 18405f757f3fSDimitry Andric SourceLocation 18415f757f3fSDimitry Andric End = // `D->getEndLoc` should always return the starting location of the 18425f757f3fSDimitry Andric // last token, so we should get the end of the token 18435f757f3fSDimitry Andric Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts); 18445f757f3fSDimitry Andric 18455f757f3fSDimitry Andric return SourceRange(Begin, End); 18465f757f3fSDimitry Andric } 18475f757f3fSDimitry Andric 184806c3fb27SDimitry Andric // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer 184906c3fb27SDimitry Andric // type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not 185006c3fb27SDimitry Andric // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me 185106c3fb27SDimitry Andric // :( ), `Qualifiers` of the pointee type is returned separately through the 185206c3fb27SDimitry Andric // output parameter `QualifiersToAppend`. 185306c3fb27SDimitry Andric static std::optional<std::string> 18545f757f3fSDimitry Andric getPointeeTypeText(const VarDecl *VD, const SourceManager &SM, 185506c3fb27SDimitry Andric const LangOptions &LangOpts, 185606c3fb27SDimitry Andric std::optional<Qualifiers> *QualifiersToAppend) { 185706c3fb27SDimitry Andric QualType Ty = VD->getType(); 185806c3fb27SDimitry Andric QualType PteTy; 185906c3fb27SDimitry Andric 186006c3fb27SDimitry Andric assert(Ty->isPointerType() && !Ty->isFunctionPointerType() && 186106c3fb27SDimitry Andric "Expecting a VarDecl of type of pointer to object type"); 186206c3fb27SDimitry Andric PteTy = Ty->getPointeeType(); 18635f757f3fSDimitry Andric 18645f757f3fSDimitry Andric TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc(); 18655f757f3fSDimitry Andric TypeLoc PteTyLoc; 18665f757f3fSDimitry Andric 18675f757f3fSDimitry Andric // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns 18685f757f3fSDimitry Andric // the `TypeLoc` of the pointee type: 18695f757f3fSDimitry Andric switch (TyLoc.getTypeLocClass()) { 18705f757f3fSDimitry Andric case TypeLoc::ConstantArray: 18715f757f3fSDimitry Andric case TypeLoc::IncompleteArray: 18725f757f3fSDimitry Andric case TypeLoc::VariableArray: 18735f757f3fSDimitry Andric case TypeLoc::DependentSizedArray: 18745f757f3fSDimitry Andric case TypeLoc::Decayed: 18755f757f3fSDimitry Andric assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a " 18765f757f3fSDimitry Andric "pointer type unless it decays."); 18775f757f3fSDimitry Andric PteTyLoc = TyLoc.getNextTypeLoc(); 18785f757f3fSDimitry Andric break; 18795f757f3fSDimitry Andric case TypeLoc::Pointer: 18805f757f3fSDimitry Andric PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc(); 18815f757f3fSDimitry Andric break; 18825f757f3fSDimitry Andric default: 18835f757f3fSDimitry Andric return std::nullopt; 18845f757f3fSDimitry Andric } 18855f757f3fSDimitry Andric if (PteTyLoc.isNull()) 18865f757f3fSDimitry Andric // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g., 18875f757f3fSDimitry Andric // when the pointer type is `auto`. 188806c3fb27SDimitry Andric return std::nullopt; 188906c3fb27SDimitry Andric 18905f757f3fSDimitry Andric SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD); 189106c3fb27SDimitry Andric 18925f757f3fSDimitry Andric if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) { 189306c3fb27SDimitry Andric // We are expecting these locations to be valid. But in some cases, they are 189406c3fb27SDimitry Andric // not all valid. It is a Clang bug to me and we are not responsible for 189506c3fb27SDimitry Andric // fixing it. So we will just give up for now when it happens. 189606c3fb27SDimitry Andric return std::nullopt; 189706c3fb27SDimitry Andric } 189806c3fb27SDimitry Andric 189906c3fb27SDimitry Andric // Note that TypeLoc.getEndLoc() returns the begin location of the last token: 190006c3fb27SDimitry Andric SourceLocation PteEndOfTokenLoc = 190106c3fb27SDimitry Andric Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts); 190206c3fb27SDimitry Andric 19035f757f3fSDimitry Andric if (!PteEndOfTokenLoc.isValid()) 19045f757f3fSDimitry Andric // Sometimes we cannot get the end location of the pointee type, e.g., when 19055f757f3fSDimitry Andric // there are macros involved. 19065f757f3fSDimitry Andric return std::nullopt; 19075f757f3fSDimitry Andric if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) { 190806c3fb27SDimitry Andric // We only deal with the cases where the source text of the pointee type 190906c3fb27SDimitry Andric // appears on the left-hand side of the variable identifier completely, 191006c3fb27SDimitry Andric // including the following forms: 191106c3fb27SDimitry Andric // `T ident`, 191206c3fb27SDimitry Andric // `T ident[]`, where `T` is any type. 191306c3fb27SDimitry Andric // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`. 191406c3fb27SDimitry Andric return std::nullopt; 191506c3fb27SDimitry Andric } 191606c3fb27SDimitry Andric if (PteTy.hasQualifiers()) { 191706c3fb27SDimitry Andric // TypeLoc does not provide source ranges for qualifiers (it says it's 191806c3fb27SDimitry Andric // intentional but seems fishy to me), so we cannot get the full text 191906c3fb27SDimitry Andric // `PteTy` via source ranges. 192006c3fb27SDimitry Andric *QualifiersToAppend = PteTy.getQualifiers(); 192106c3fb27SDimitry Andric } 192206c3fb27SDimitry Andric return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts) 192306c3fb27SDimitry Andric ->str(); 192406c3fb27SDimitry Andric } 192506c3fb27SDimitry Andric 192606c3fb27SDimitry Andric // Returns the text of the name (with qualifiers) of a `FunctionDecl`. 192706c3fb27SDimitry Andric static std::optional<StringRef> getFunNameText(const FunctionDecl *FD, 192806c3fb27SDimitry Andric const SourceManager &SM, 192906c3fb27SDimitry Andric const LangOptions &LangOpts) { 193006c3fb27SDimitry Andric SourceLocation BeginLoc = FD->getQualifier() 193106c3fb27SDimitry Andric ? FD->getQualifierLoc().getBeginLoc() 193206c3fb27SDimitry Andric : FD->getNameInfo().getBeginLoc(); 193306c3fb27SDimitry Andric // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the 193406c3fb27SDimitry Andric // last token: 193506c3fb27SDimitry Andric SourceLocation EndLoc = Lexer::getLocForEndOfToken( 193606c3fb27SDimitry Andric FD->getNameInfo().getEndLoc(), 0, SM, LangOpts); 193706c3fb27SDimitry Andric SourceRange NameRange{BeginLoc, EndLoc}; 193806c3fb27SDimitry Andric 193906c3fb27SDimitry Andric return getRangeText(NameRange, SM, LangOpts); 194006c3fb27SDimitry Andric } 194106c3fb27SDimitry Andric 19425f757f3fSDimitry Andric // Returns the text representing a `std::span` type where the element type is 19435f757f3fSDimitry Andric // represented by `EltTyText`. 19445f757f3fSDimitry Andric // 19455f757f3fSDimitry Andric // Note the optional parameter `Qualifiers`: one needs to pass qualifiers 19465f757f3fSDimitry Andric // explicitly if the element type needs to be qualified. 19475f757f3fSDimitry Andric static std::string 19485f757f3fSDimitry Andric getSpanTypeText(StringRef EltTyText, 19495f757f3fSDimitry Andric std::optional<Qualifiers> Quals = std::nullopt) { 19505f757f3fSDimitry Andric const char *const SpanOpen = "std::span<"; 19515f757f3fSDimitry Andric 19525f757f3fSDimitry Andric if (Quals) 19535f757f3fSDimitry Andric return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>'; 19545f757f3fSDimitry Andric return SpanOpen + EltTyText.str() + '>'; 19555f757f3fSDimitry Andric } 19565f757f3fSDimitry Andric 195706c3fb27SDimitry Andric std::optional<FixItList> 1958*0fca6ea1SDimitry Andric DerefSimplePtrArithFixableGadget::getFixits(const FixitStrategy &s) const { 195906c3fb27SDimitry Andric const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl()); 196006c3fb27SDimitry Andric 1961*0fca6ea1SDimitry Andric if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) { 196206c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 196306c3fb27SDimitry Andric // std::span can't represent elements before its begin() 196406c3fb27SDimitry Andric if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx)) 196506c3fb27SDimitry Andric if (ConstVal->isNegative()) 196606c3fb27SDimitry Andric return std::nullopt; 196706c3fb27SDimitry Andric 196806c3fb27SDimitry Andric // note that the expr may (oddly) has multiple layers of parens 196906c3fb27SDimitry Andric // example: 197006c3fb27SDimitry Andric // *((..(pointer + 123)..)) 197106c3fb27SDimitry Andric // goal: 197206c3fb27SDimitry Andric // pointer[123] 197306c3fb27SDimitry Andric // Fix-It: 197406c3fb27SDimitry Andric // remove '*(' 197506c3fb27SDimitry Andric // replace ' + ' with '[' 197606c3fb27SDimitry Andric // replace ')' with ']' 197706c3fb27SDimitry Andric 197806c3fb27SDimitry Andric // example: 197906c3fb27SDimitry Andric // *((..(123 + pointer)..)) 198006c3fb27SDimitry Andric // goal: 198106c3fb27SDimitry Andric // 123[pointer] 198206c3fb27SDimitry Andric // Fix-It: 198306c3fb27SDimitry Andric // remove '*(' 198406c3fb27SDimitry Andric // replace ' + ' with '[' 198506c3fb27SDimitry Andric // replace ')' with ']' 198606c3fb27SDimitry Andric 198706c3fb27SDimitry Andric const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS(); 198806c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 198906c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 199006c3fb27SDimitry Andric CharSourceRange StarWithTrailWhitespace = 199106c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(), 199206c3fb27SDimitry Andric LHS->getBeginLoc()); 199306c3fb27SDimitry Andric 199406c3fb27SDimitry Andric std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts); 199506c3fb27SDimitry Andric if (!LHSLocation) 199606c3fb27SDimitry Andric return std::nullopt; 199706c3fb27SDimitry Andric 199806c3fb27SDimitry Andric CharSourceRange PlusWithSurroundingWhitespace = 199906c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc()); 200006c3fb27SDimitry Andric 200106c3fb27SDimitry Andric std::optional<SourceLocation> AddOpLocation = 200206c3fb27SDimitry Andric getPastLoc(AddOp, SM, LangOpts); 200306c3fb27SDimitry Andric std::optional<SourceLocation> DerefOpLocation = 200406c3fb27SDimitry Andric getPastLoc(DerefOp, SM, LangOpts); 200506c3fb27SDimitry Andric 200606c3fb27SDimitry Andric if (!AddOpLocation || !DerefOpLocation) 200706c3fb27SDimitry Andric return std::nullopt; 200806c3fb27SDimitry Andric 200906c3fb27SDimitry Andric CharSourceRange ClosingParenWithPrecWhitespace = 201006c3fb27SDimitry Andric clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation); 201106c3fb27SDimitry Andric 201206c3fb27SDimitry Andric return FixItList{ 201306c3fb27SDimitry Andric {FixItHint::CreateRemoval(StarWithTrailWhitespace), 201406c3fb27SDimitry Andric FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["), 201506c3fb27SDimitry Andric FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}}; 201606c3fb27SDimitry Andric } 201706c3fb27SDimitry Andric return std::nullopt; // something wrong or unsupported, give up 201806c3fb27SDimitry Andric } 201906c3fb27SDimitry Andric 202006c3fb27SDimitry Andric std::optional<FixItList> 2021*0fca6ea1SDimitry Andric PointerDereferenceGadget::getFixits(const FixitStrategy &S) const { 202206c3fb27SDimitry Andric const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl()); 202306c3fb27SDimitry Andric switch (S.lookup(VD)) { 2024*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Span: { 202506c3fb27SDimitry Andric ASTContext &Ctx = VD->getASTContext(); 202606c3fb27SDimitry Andric SourceManager &SM = Ctx.getSourceManager(); 202706c3fb27SDimitry Andric // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0] 202806c3fb27SDimitry Andric // Deletes the *operand 202906c3fb27SDimitry Andric CharSourceRange derefRange = clang::CharSourceRange::getCharRange( 203006c3fb27SDimitry Andric Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1)); 203106c3fb27SDimitry Andric // Inserts the [0] 20325f757f3fSDimitry Andric if (auto LocPastOperand = 20335f757f3fSDimitry Andric getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) { 203406c3fb27SDimitry Andric return FixItList{{FixItHint::CreateRemoval(derefRange), 20355f757f3fSDimitry Andric FixItHint::CreateInsertion(*LocPastOperand, "[0]")}}; 203606c3fb27SDimitry Andric } 20375f757f3fSDimitry Andric break; 203806c3fb27SDimitry Andric } 2039*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Iterator: 2040*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Array: 2041*0fca6ea1SDimitry Andric return std::nullopt; 2042*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Vector: 2043*0fca6ea1SDimitry Andric llvm_unreachable("FixitStrategy not implemented yet!"); 2044*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Wontfix: 204506c3fb27SDimitry Andric llvm_unreachable("Invalid strategy!"); 204606c3fb27SDimitry Andric } 204706c3fb27SDimitry Andric 204806c3fb27SDimitry Andric return std::nullopt; 204906c3fb27SDimitry Andric } 205006c3fb27SDimitry Andric 2051*0fca6ea1SDimitry Andric static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx, 2052*0fca6ea1SDimitry Andric const DeclRefExpr *DRE) { 2053*0fca6ea1SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 205406c3fb27SDimitry Andric // Inserts the .data() after the DRE 205506c3fb27SDimitry Andric std::optional<SourceLocation> EndOfOperand = 2056*0fca6ea1SDimitry Andric getPastLoc(DRE, SM, Ctx.getLangOpts()); 205706c3fb27SDimitry Andric 205806c3fb27SDimitry Andric if (EndOfOperand) 2059*0fca6ea1SDimitry Andric return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}}; 2060*0fca6ea1SDimitry Andric 2061*0fca6ea1SDimitry Andric return std::nullopt; 2062*0fca6ea1SDimitry Andric } 2063*0fca6ea1SDimitry Andric 2064*0fca6ea1SDimitry Andric // Generates fix-its replacing an expression of the form UPC(DRE) with 2065*0fca6ea1SDimitry Andric // `DRE.data()` 2066*0fca6ea1SDimitry Andric std::optional<FixItList> 2067*0fca6ea1SDimitry Andric UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const { 2068*0fca6ea1SDimitry Andric const auto VD = cast<VarDecl>(Node->getDecl()); 2069*0fca6ea1SDimitry Andric switch (S.lookup(VD)) { 2070*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Array: 2071*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Span: { 2072*0fca6ea1SDimitry Andric return createDataFixit(VD->getASTContext(), Node); 20735f757f3fSDimitry Andric // FIXME: Points inside a macro expansion. 20745f757f3fSDimitry Andric break; 207506c3fb27SDimitry Andric } 2076*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Wontfix: 2077*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Iterator: 2078*0fca6ea1SDimitry Andric return std::nullopt; 2079*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Vector: 208006c3fb27SDimitry Andric llvm_unreachable("unsupported strategies for FixableGadgets"); 208106c3fb27SDimitry Andric } 208206c3fb27SDimitry Andric 208306c3fb27SDimitry Andric return std::nullopt; 208406c3fb27SDimitry Andric } 208506c3fb27SDimitry Andric 208606c3fb27SDimitry Andric // Generates fix-its replacing an expression of the form `&DRE[e]` with 208706c3fb27SDimitry Andric // `&DRE.data()[e]`: 208806c3fb27SDimitry Andric static std::optional<FixItList> 208906c3fb27SDimitry Andric fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) { 209006c3fb27SDimitry Andric const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr()); 209106c3fb27SDimitry Andric const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts()); 209206c3fb27SDimitry Andric // FIXME: this `getASTContext` call is costly, we should pass the 209306c3fb27SDimitry Andric // ASTContext in: 209406c3fb27SDimitry Andric const ASTContext &Ctx = DRE->getDecl()->getASTContext(); 209506c3fb27SDimitry Andric const Expr *Idx = ArraySub->getIdx(); 209606c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 209706c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 209806c3fb27SDimitry Andric std::stringstream SS; 209906c3fb27SDimitry Andric bool IdxIsLitZero = false; 210006c3fb27SDimitry Andric 210106c3fb27SDimitry Andric if (auto ICE = Idx->getIntegerConstantExpr(Ctx)) 210206c3fb27SDimitry Andric if ((*ICE).isZero()) 210306c3fb27SDimitry Andric IdxIsLitZero = true; 210406c3fb27SDimitry Andric std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts); 210506c3fb27SDimitry Andric if (!DreString) 210606c3fb27SDimitry Andric return std::nullopt; 210706c3fb27SDimitry Andric 210806c3fb27SDimitry Andric if (IdxIsLitZero) { 210906c3fb27SDimitry Andric // If the index is literal zero, we produce the most concise fix-it: 211006c3fb27SDimitry Andric SS << (*DreString).str() << ".data()"; 211106c3fb27SDimitry Andric } else { 211206c3fb27SDimitry Andric std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts); 211306c3fb27SDimitry Andric if (!IndexString) 211406c3fb27SDimitry Andric return std::nullopt; 211506c3fb27SDimitry Andric 211606c3fb27SDimitry Andric SS << "&" << (*DreString).str() << ".data()" 211706c3fb27SDimitry Andric << "[" << (*IndexString).str() << "]"; 211806c3fb27SDimitry Andric } 211906c3fb27SDimitry Andric return FixItList{ 212006c3fb27SDimitry Andric FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())}; 212106c3fb27SDimitry Andric } 212206c3fb27SDimitry Andric 21235f757f3fSDimitry Andric std::optional<FixItList> 2124*0fca6ea1SDimitry Andric UUCAddAssignGadget::getFixits(const FixitStrategy &S) const { 21255f757f3fSDimitry Andric DeclUseList DREs = getClaimedVarUseSites(); 21265f757f3fSDimitry Andric 21275f757f3fSDimitry Andric if (DREs.size() != 1) 21285f757f3fSDimitry Andric return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we 21295f757f3fSDimitry Andric // give up 21305f757f3fSDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) { 2131*0fca6ea1SDimitry Andric if (S.lookup(VD) == FixitStrategy::Kind::Span) { 21325f757f3fSDimitry Andric FixItList Fixes; 21335f757f3fSDimitry Andric 2134*0fca6ea1SDimitry Andric const Stmt *AddAssignNode = Node; 21355f757f3fSDimitry Andric StringRef varName = VD->getName(); 21365f757f3fSDimitry Andric const ASTContext &Ctx = VD->getASTContext(); 21375f757f3fSDimitry Andric 21385f757f3fSDimitry Andric if (!isNonNegativeIntegerExpr(Offset, VD, Ctx)) 21395f757f3fSDimitry Andric return std::nullopt; 21405f757f3fSDimitry Andric 21415f757f3fSDimitry Andric // To transform UUC(p += n) to UUC(p = p.subspan(..)): 21425f757f3fSDimitry Andric bool NotParenExpr = 21435f757f3fSDimitry Andric (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc()); 21445f757f3fSDimitry Andric std::string SS = varName.str() + " = " + varName.str() + ".subspan"; 21455f757f3fSDimitry Andric if (NotParenExpr) 21465f757f3fSDimitry Andric SS += "("; 21475f757f3fSDimitry Andric 21485f757f3fSDimitry Andric std::optional<SourceLocation> AddAssignLocation = getEndCharLoc( 21495f757f3fSDimitry Andric AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts()); 21505f757f3fSDimitry Andric if (!AddAssignLocation) 21515f757f3fSDimitry Andric return std::nullopt; 21525f757f3fSDimitry Andric 21535f757f3fSDimitry Andric Fixes.push_back(FixItHint::CreateReplacement( 21545f757f3fSDimitry Andric SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()), 21555f757f3fSDimitry Andric SS)); 21565f757f3fSDimitry Andric if (NotParenExpr) 21575f757f3fSDimitry Andric Fixes.push_back(FixItHint::CreateInsertion( 21585f757f3fSDimitry Andric Offset->getEndLoc().getLocWithOffset(1), ")")); 21595f757f3fSDimitry Andric return Fixes; 21605f757f3fSDimitry Andric } 21615f757f3fSDimitry Andric } 21625f757f3fSDimitry Andric return std::nullopt; // Not in the cases that we can handle for now, give up. 21635f757f3fSDimitry Andric } 216406c3fb27SDimitry Andric 2165*0fca6ea1SDimitry Andric std::optional<FixItList> 2166*0fca6ea1SDimitry Andric UPCPreIncrementGadget::getFixits(const FixitStrategy &S) const { 216706c3fb27SDimitry Andric DeclUseList DREs = getClaimedVarUseSites(); 216806c3fb27SDimitry Andric 216906c3fb27SDimitry Andric if (DREs.size() != 1) 217006c3fb27SDimitry Andric return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we 217106c3fb27SDimitry Andric // give up 217206c3fb27SDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) { 2173*0fca6ea1SDimitry Andric if (S.lookup(VD) == FixitStrategy::Kind::Span) { 217406c3fb27SDimitry Andric FixItList Fixes; 217506c3fb27SDimitry Andric std::stringstream SS; 217606c3fb27SDimitry Andric StringRef varName = VD->getName(); 217706c3fb27SDimitry Andric const ASTContext &Ctx = VD->getASTContext(); 217806c3fb27SDimitry Andric 217906c3fb27SDimitry Andric // To transform UPC(++p) to UPC((p = p.subspan(1)).data()): 218006c3fb27SDimitry Andric SS << "(" << varName.data() << " = " << varName.data() 218106c3fb27SDimitry Andric << ".subspan(1)).data()"; 218206c3fb27SDimitry Andric std::optional<SourceLocation> PreIncLocation = 2183*0fca6ea1SDimitry Andric getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts()); 218406c3fb27SDimitry Andric if (!PreIncLocation) 218506c3fb27SDimitry Andric return std::nullopt; 218606c3fb27SDimitry Andric 218706c3fb27SDimitry Andric Fixes.push_back(FixItHint::CreateReplacement( 2188*0fca6ea1SDimitry Andric SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str())); 218906c3fb27SDimitry Andric return Fixes; 219006c3fb27SDimitry Andric } 219106c3fb27SDimitry Andric } 219206c3fb27SDimitry Andric return std::nullopt; // Not in the cases that we can handle for now, give up. 219306c3fb27SDimitry Andric } 219406c3fb27SDimitry Andric 219506c3fb27SDimitry Andric // For a non-null initializer `Init` of `T *` type, this function returns 219606c3fb27SDimitry Andric // `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it 219706c3fb27SDimitry Andric // to output stream. 219806c3fb27SDimitry Andric // In many cases, this function cannot figure out the actual extent `S`. It 219906c3fb27SDimitry Andric // then will use a place holder to replace `S` to ask users to fill `S` in. The 220006c3fb27SDimitry Andric // initializer shall be used to initialize a variable of type `std::span<T>`. 2201*0fca6ea1SDimitry Andric // In some cases (e. g. constant size array) the initializer should remain 2202*0fca6ea1SDimitry Andric // unchanged and the function returns empty list. In case the function can't 2203*0fca6ea1SDimitry Andric // provide the right fixit it will return nullopt. 220406c3fb27SDimitry Andric // 220506c3fb27SDimitry Andric // FIXME: Support multi-level pointers 220606c3fb27SDimitry Andric // 220706c3fb27SDimitry Andric // Parameters: 220806c3fb27SDimitry Andric // `Init` a pointer to the initializer expression 220906c3fb27SDimitry Andric // `Ctx` a reference to the ASTContext 2210*0fca6ea1SDimitry Andric static std::optional<FixItList> 22115f757f3fSDimitry Andric FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, 221206c3fb27SDimitry Andric const StringRef UserFillPlaceHolder) { 221306c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 221406c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 221506c3fb27SDimitry Andric 221606c3fb27SDimitry Andric // If `Init` has a constant value that is (or equivalent to) a 221706c3fb27SDimitry Andric // NULL pointer, we use the default constructor to initialize the span 221806c3fb27SDimitry Andric // object, i.e., a `std:span` variable declaration with no initializer. 221906c3fb27SDimitry Andric // So the fix-it is just to remove the initializer. 2220*0fca6ea1SDimitry Andric if (Init->isNullPointerConstant( 2221*0fca6ea1SDimitry Andric Ctx, 222206c3fb27SDimitry Andric // FIXME: Why does this function not ask for `const ASTContext 222306c3fb27SDimitry Andric // &`? It should. Maybe worth an NFC patch later. 222406c3fb27SDimitry Andric Expr::NullPointerConstantValueDependence:: 222506c3fb27SDimitry Andric NPC_ValueDependentIsNotNull)) { 222606c3fb27SDimitry Andric std::optional<SourceLocation> InitLocation = 222706c3fb27SDimitry Andric getEndCharLoc(Init, SM, LangOpts); 222806c3fb27SDimitry Andric if (!InitLocation) 2229*0fca6ea1SDimitry Andric return std::nullopt; 223006c3fb27SDimitry Andric 223106c3fb27SDimitry Andric SourceRange SR(Init->getBeginLoc(), *InitLocation); 223206c3fb27SDimitry Andric 2233*0fca6ea1SDimitry Andric return FixItList{FixItHint::CreateRemoval(SR)}; 223406c3fb27SDimitry Andric } 223506c3fb27SDimitry Andric 223606c3fb27SDimitry Andric FixItList FixIts{}; 223706c3fb27SDimitry Andric std::string ExtentText = UserFillPlaceHolder.data(); 223806c3fb27SDimitry Andric StringRef One = "1"; 223906c3fb27SDimitry Andric 224006c3fb27SDimitry Andric // Insert `{` before `Init`: 224106c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{")); 224206c3fb27SDimitry Andric // Try to get the data extent. Break into different cases: 224306c3fb27SDimitry Andric if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) { 224406c3fb27SDimitry Andric // In cases `Init` is `new T[n]` and there is no explicit cast over 224506c3fb27SDimitry Andric // `Init`, we know that `Init` must evaluates to a pointer to `n` objects 224606c3fb27SDimitry Andric // of `T`. So the extent is `n` unless `n` has side effects. Similar but 224706c3fb27SDimitry Andric // simpler for the case where `Init` is `new T`. 224806c3fb27SDimitry Andric if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) { 224906c3fb27SDimitry Andric if (!Ext->HasSideEffects(Ctx)) { 225006c3fb27SDimitry Andric std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts); 225106c3fb27SDimitry Andric if (!ExtentString) 2252*0fca6ea1SDimitry Andric return std::nullopt; 225306c3fb27SDimitry Andric ExtentText = *ExtentString; 225406c3fb27SDimitry Andric } 225506c3fb27SDimitry Andric } else if (!CxxNew->isArray()) 225606c3fb27SDimitry Andric // Although the initializer is not allocating a buffer, the pointer 225706c3fb27SDimitry Andric // variable could still be used in buffer access operations. 225806c3fb27SDimitry Andric ExtentText = One; 2259*0fca6ea1SDimitry Andric } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) { 2260*0fca6ea1SDimitry Andric // std::span has a single parameter constructor for initialization with 2261*0fca6ea1SDimitry Andric // constant size array. The size is auto-deduced as the constructor is a 2262*0fca6ea1SDimitry Andric // function template. The correct fixit is empty - no changes should happen. 2263*0fca6ea1SDimitry Andric return FixItList{}; 226406c3fb27SDimitry Andric } else { 226506c3fb27SDimitry Andric // In cases `Init` is of the form `&Var` after stripping of implicit 226606c3fb27SDimitry Andric // casts, where `&` is the built-in operator, the extent is 1. 226706c3fb27SDimitry Andric if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts())) 226806c3fb27SDimitry Andric if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf && 226906c3fb27SDimitry Andric isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr())) 227006c3fb27SDimitry Andric ExtentText = One; 227106c3fb27SDimitry Andric // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`, 227206c3fb27SDimitry Andric // and explicit casting, etc. etc. 227306c3fb27SDimitry Andric } 227406c3fb27SDimitry Andric 227506c3fb27SDimitry Andric SmallString<32> StrBuffer{}; 227606c3fb27SDimitry Andric std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts); 227706c3fb27SDimitry Andric 227806c3fb27SDimitry Andric if (!LocPassInit) 2279*0fca6ea1SDimitry Andric return std::nullopt; 228006c3fb27SDimitry Andric 228106c3fb27SDimitry Andric StrBuffer.append(", "); 228206c3fb27SDimitry Andric StrBuffer.append(ExtentText); 228306c3fb27SDimitry Andric StrBuffer.append("}"); 228406c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str())); 228506c3fb27SDimitry Andric return FixIts; 228606c3fb27SDimitry Andric } 228706c3fb27SDimitry Andric 22885f757f3fSDimitry Andric #ifndef NDEBUG 22895f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg) \ 2290*0fca6ea1SDimitry Andric Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \ 2291*0fca6ea1SDimitry Andric "failed to produce fixit for declaration '" + \ 2292*0fca6ea1SDimitry Andric (D)->getNameAsString() + "'" + (Msg)) 22935f757f3fSDimitry Andric #else 22945f757f3fSDimitry Andric #define DEBUG_NOTE_DECL_FAIL(D, Msg) 22955f757f3fSDimitry Andric #endif 22965f757f3fSDimitry Andric 22975f757f3fSDimitry Andric // For the given variable declaration with a pointer-to-T type, returns the text 22985f757f3fSDimitry Andric // `std::span<T>`. If it is unable to generate the text, returns 22995f757f3fSDimitry Andric // `std::nullopt`. 2300*0fca6ea1SDimitry Andric static std::optional<std::string> 2301*0fca6ea1SDimitry Andric createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx) { 23025f757f3fSDimitry Andric assert(VD->getType()->isPointerType()); 23035f757f3fSDimitry Andric 23045f757f3fSDimitry Andric std::optional<Qualifiers> PteTyQualifiers = std::nullopt; 23055f757f3fSDimitry Andric std::optional<std::string> PteTyText = getPointeeTypeText( 23065f757f3fSDimitry Andric VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers); 23075f757f3fSDimitry Andric 23085f757f3fSDimitry Andric if (!PteTyText) 23095f757f3fSDimitry Andric return std::nullopt; 23105f757f3fSDimitry Andric 23115f757f3fSDimitry Andric std::string SpanTyText = "std::span<"; 23125f757f3fSDimitry Andric 23135f757f3fSDimitry Andric SpanTyText.append(*PteTyText); 23145f757f3fSDimitry Andric // Append qualifiers to span element type if any: 23155f757f3fSDimitry Andric if (PteTyQualifiers) { 23165f757f3fSDimitry Andric SpanTyText.append(" "); 23175f757f3fSDimitry Andric SpanTyText.append(PteTyQualifiers->getAsString()); 23185f757f3fSDimitry Andric } 23195f757f3fSDimitry Andric SpanTyText.append(">"); 23205f757f3fSDimitry Andric return SpanTyText; 23215f757f3fSDimitry Andric } 23225f757f3fSDimitry Andric 232306c3fb27SDimitry Andric // For a `VarDecl` of the form `T * var (= Init)?`, this 23245f757f3fSDimitry Andric // function generates fix-its that 23255f757f3fSDimitry Andric // 1) replace `T * var` with `std::span<T> var`; and 23265f757f3fSDimitry Andric // 2) change `Init` accordingly to a span constructor, if it exists. 232706c3fb27SDimitry Andric // 232806c3fb27SDimitry Andric // FIXME: support Multi-level pointers 232906c3fb27SDimitry Andric // 233006c3fb27SDimitry Andric // Parameters: 233106c3fb27SDimitry Andric // `D` a pointer the variable declaration node 233206c3fb27SDimitry Andric // `Ctx` a reference to the ASTContext 23335f757f3fSDimitry Andric // `UserFillPlaceHolder` the user-input placeholder text 233406c3fb27SDimitry Andric // Returns: 23355f757f3fSDimitry Andric // the non-empty fix-it list, if fix-its are successfuly generated; empty 23365f757f3fSDimitry Andric // list otherwise. 23375f757f3fSDimitry Andric static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, 23385f757f3fSDimitry Andric const StringRef UserFillPlaceHolder, 23395f757f3fSDimitry Andric UnsafeBufferUsageHandler &Handler) { 23405f757f3fSDimitry Andric if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager())) 23415f757f3fSDimitry Andric return {}; 234206c3fb27SDimitry Andric 234306c3fb27SDimitry Andric FixItList FixIts{}; 23445f757f3fSDimitry Andric std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx); 234506c3fb27SDimitry Andric 23465f757f3fSDimitry Andric if (!SpanTyText) { 23475f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type"); 23485f757f3fSDimitry Andric return {}; 23495f757f3fSDimitry Andric } 23505f757f3fSDimitry Andric 23515f757f3fSDimitry Andric // Will hold the text for `std::span<T> Ident`: 23525f757f3fSDimitry Andric std::stringstream SS; 23535f757f3fSDimitry Andric 23545f757f3fSDimitry Andric SS << *SpanTyText; 23555f757f3fSDimitry Andric // Fix the initializer if it exists: 235606c3fb27SDimitry Andric if (const Expr *Init = D->getInit()) { 2357*0fca6ea1SDimitry Andric std::optional<FixItList> InitFixIts = 23585f757f3fSDimitry Andric FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder); 2359*0fca6ea1SDimitry Andric if (!InitFixIts) 236006c3fb27SDimitry Andric return {}; 2361*0fca6ea1SDimitry Andric FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()), 2362*0fca6ea1SDimitry Andric std::make_move_iterator(InitFixIts->end())); 23635f757f3fSDimitry Andric } 2364*0fca6ea1SDimitry Andric // For declaration of the form `T * ident = init;`, we want to replace 2365*0fca6ea1SDimitry Andric // `T * ` with `std::span<T>`. 2366*0fca6ea1SDimitry Andric // We ignore CV-qualifiers so for `T * const ident;` we also want to replace 2367*0fca6ea1SDimitry Andric // just `T *` with `std::span<T>`. 2368*0fca6ea1SDimitry Andric const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc(); 23695f757f3fSDimitry Andric if (!EndLocForReplacement.isValid()) { 23705f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration"); 237106c3fb27SDimitry Andric return {}; 23725f757f3fSDimitry Andric } 2373*0fca6ea1SDimitry Andric // The only exception is that for `T *ident` we'll add a single space between 2374*0fca6ea1SDimitry Andric // "std::span<T>" and "ident". 2375*0fca6ea1SDimitry Andric // FIXME: The condition is false for identifiers expended from macros. 2376*0fca6ea1SDimitry Andric if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D)) 2377*0fca6ea1SDimitry Andric SS << " "; 2378*0fca6ea1SDimitry Andric 237906c3fb27SDimitry Andric FixIts.push_back(FixItHint::CreateReplacement( 23805f757f3fSDimitry Andric SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str())); 238106c3fb27SDimitry Andric return FixIts; 238206c3fb27SDimitry Andric } 238306c3fb27SDimitry Andric 238406c3fb27SDimitry Andric static bool hasConflictingOverload(const FunctionDecl *FD) { 238506c3fb27SDimitry Andric return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult(); 238606c3fb27SDimitry Andric } 238706c3fb27SDimitry Andric 23885f757f3fSDimitry Andric // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new 23895f757f3fSDimitry Andric // types, this function produces fix-its to make the change self-contained. Let 23905f757f3fSDimitry Andric // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the 23915f757f3fSDimitry Andric // entity defined by the `FunctionDecl` after the change to the parameters. 23925f757f3fSDimitry Andric // Fix-its produced by this function are 239306c3fb27SDimitry Andric // 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration 239406c3fb27SDimitry Andric // of 'F'; 239506c3fb27SDimitry Andric // 2. Create a declaration of "NewF" next to each declaration of `F`; 239606c3fb27SDimitry Andric // 3. Create a definition of "F" (as its' original definition is now belongs 239706c3fb27SDimitry Andric // to "NewF") next to its original definition. The body of the creating 239806c3fb27SDimitry Andric // definition calls to "NewF". 239906c3fb27SDimitry Andric // 240006c3fb27SDimitry Andric // Example: 240106c3fb27SDimitry Andric // 240206c3fb27SDimitry Andric // void f(int *p); // original declaration 240306c3fb27SDimitry Andric // void f(int *p) { // original definition 240406c3fb27SDimitry Andric // p[5]; 240506c3fb27SDimitry Andric // } 240606c3fb27SDimitry Andric // 240706c3fb27SDimitry Andric // To change the parameter `p` to be of `std::span<int>` type, we 240806c3fb27SDimitry Andric // also add overloads: 240906c3fb27SDimitry Andric // 241006c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl 241106c3fb27SDimitry Andric // void f(std::span<int> p); // added overload decl 241206c3fb27SDimitry Andric // void f(std::span<int> p) { // original def where param is changed 241306c3fb27SDimitry Andric // p[5]; 241406c3fb27SDimitry Andric // } 241506c3fb27SDimitry Andric // [[clang::unsafe_buffer_usage]] void f(int *p) { // added def 241606c3fb27SDimitry Andric // return f(std::span(p, <# size #>)); 241706c3fb27SDimitry Andric // } 241806c3fb27SDimitry Andric // 241906c3fb27SDimitry Andric static std::optional<FixItList> 2420*0fca6ea1SDimitry Andric createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD, 24215f757f3fSDimitry Andric const ASTContext &Ctx, 242206c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 242306c3fb27SDimitry Andric // FIXME: need to make this conflict checking better: 242406c3fb27SDimitry Andric if (hasConflictingOverload(FD)) 242506c3fb27SDimitry Andric return std::nullopt; 242606c3fb27SDimitry Andric 242706c3fb27SDimitry Andric const SourceManager &SM = Ctx.getSourceManager(); 242806c3fb27SDimitry Andric const LangOptions &LangOpts = Ctx.getLangOpts(); 24295f757f3fSDimitry Andric const unsigned NumParms = FD->getNumParams(); 24305f757f3fSDimitry Andric std::vector<std::string> NewTysTexts(NumParms); 24315f757f3fSDimitry Andric std::vector<bool> ParmsMask(NumParms, false); 24325f757f3fSDimitry Andric bool AtLeastOneParmToFix = false; 24335f757f3fSDimitry Andric 24345f757f3fSDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 24355f757f3fSDimitry Andric const ParmVarDecl *PVD = FD->getParamDecl(i); 24365f757f3fSDimitry Andric 2437*0fca6ea1SDimitry Andric if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix) 24385f757f3fSDimitry Andric continue; 2439*0fca6ea1SDimitry Andric if (S.lookup(PVD) != FixitStrategy::Kind::Span) 24405f757f3fSDimitry Andric // Not supported, not suppose to happen: 24415f757f3fSDimitry Andric return std::nullopt; 24425f757f3fSDimitry Andric 24435f757f3fSDimitry Andric std::optional<Qualifiers> PteTyQuals = std::nullopt; 24445f757f3fSDimitry Andric std::optional<std::string> PteTyText = 24455f757f3fSDimitry Andric getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals); 24465f757f3fSDimitry Andric 24475f757f3fSDimitry Andric if (!PteTyText) 24485f757f3fSDimitry Andric // something wrong in obtaining the text of the pointee type, give up 24495f757f3fSDimitry Andric return std::nullopt; 2450*0fca6ea1SDimitry Andric // FIXME: whether we should create std::span type depends on the 2451*0fca6ea1SDimitry Andric // FixitStrategy. 24525f757f3fSDimitry Andric NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals); 24535f757f3fSDimitry Andric ParmsMask[i] = true; 24545f757f3fSDimitry Andric AtLeastOneParmToFix = true; 24555f757f3fSDimitry Andric } 24565f757f3fSDimitry Andric if (!AtLeastOneParmToFix) 24575f757f3fSDimitry Andric // No need to create function overloads: 24585f757f3fSDimitry Andric return {}; 245906c3fb27SDimitry Andric // FIXME Respect indentation of the original code. 246006c3fb27SDimitry Andric 246106c3fb27SDimitry Andric // A lambda that creates the text representation of a function declaration 24625f757f3fSDimitry Andric // with the new type signatures: 246306c3fb27SDimitry Andric const auto NewOverloadSignatureCreator = 24645f757f3fSDimitry Andric [&SM, &LangOpts, &NewTysTexts, 24655f757f3fSDimitry Andric &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> { 246606c3fb27SDimitry Andric std::stringstream SS; 246706c3fb27SDimitry Andric 246806c3fb27SDimitry Andric SS << ";"; 246906c3fb27SDimitry Andric SS << getEndOfLine().str(); 247006c3fb27SDimitry Andric // Append: ret-type func-name "(" 247106c3fb27SDimitry Andric if (auto Prefix = getRangeText( 247206c3fb27SDimitry Andric SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()), 247306c3fb27SDimitry Andric SM, LangOpts)) 247406c3fb27SDimitry Andric SS << Prefix->str(); 247506c3fb27SDimitry Andric else 247606c3fb27SDimitry Andric return std::nullopt; // give up 247706c3fb27SDimitry Andric // Append: parameter-type-list 247806c3fb27SDimitry Andric const unsigned NumParms = FD->getNumParams(); 247906c3fb27SDimitry Andric 248006c3fb27SDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 248106c3fb27SDimitry Andric const ParmVarDecl *Parm = FD->getParamDecl(i); 248206c3fb27SDimitry Andric 248306c3fb27SDimitry Andric if (Parm->isImplicit()) 248406c3fb27SDimitry Andric continue; 24855f757f3fSDimitry Andric if (ParmsMask[i]) { 24865f757f3fSDimitry Andric // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its 24875f757f3fSDimitry Andric // new type: 24885f757f3fSDimitry Andric SS << NewTysTexts[i]; 248906c3fb27SDimitry Andric // print parameter name if provided: 249006c3fb27SDimitry Andric if (IdentifierInfo *II = Parm->getIdentifier()) 24915f757f3fSDimitry Andric SS << ' ' << II->getName().str(); 2492*0fca6ea1SDimitry Andric } else if (auto ParmTypeText = 2493*0fca6ea1SDimitry Andric getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts), 24945f757f3fSDimitry Andric SM, LangOpts)) { 249506c3fb27SDimitry Andric // print the whole `Parm` without modification: 249606c3fb27SDimitry Andric SS << ParmTypeText->str(); 249706c3fb27SDimitry Andric } else 249806c3fb27SDimitry Andric return std::nullopt; // something wrong, give up 249906c3fb27SDimitry Andric if (i != NumParms - 1) 250006c3fb27SDimitry Andric SS << ", "; 250106c3fb27SDimitry Andric } 250206c3fb27SDimitry Andric SS << ")"; 250306c3fb27SDimitry Andric return SS.str(); 250406c3fb27SDimitry Andric }; 250506c3fb27SDimitry Andric 250606c3fb27SDimitry Andric // A lambda that creates the text representation of a function definition with 250706c3fb27SDimitry Andric // the original signature: 250806c3fb27SDimitry Andric const auto OldOverloadDefCreator = 25095f757f3fSDimitry Andric [&Handler, &SM, &LangOpts, &NewTysTexts, 25105f757f3fSDimitry Andric &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> { 251106c3fb27SDimitry Andric std::stringstream SS; 251206c3fb27SDimitry Andric 251306c3fb27SDimitry Andric SS << getEndOfLine().str(); 251406c3fb27SDimitry Andric // Append: attr-name ret-type func-name "(" param-list ")" "{" 251506c3fb27SDimitry Andric if (auto FDPrefix = getRangeText( 251606c3fb27SDimitry Andric SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM, 251706c3fb27SDimitry Andric LangOpts)) 251806c3fb27SDimitry Andric SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ") 251906c3fb27SDimitry Andric << FDPrefix->str() << "{"; 252006c3fb27SDimitry Andric else 252106c3fb27SDimitry Andric return std::nullopt; 252206c3fb27SDimitry Andric // Append: "return" func-name "(" 252306c3fb27SDimitry Andric if (auto FunQualName = getFunNameText(FD, SM, LangOpts)) 252406c3fb27SDimitry Andric SS << "return " << FunQualName->str() << "("; 252506c3fb27SDimitry Andric else 252606c3fb27SDimitry Andric return std::nullopt; 252706c3fb27SDimitry Andric 252806c3fb27SDimitry Andric // Append: arg-list 252906c3fb27SDimitry Andric const unsigned NumParms = FD->getNumParams(); 253006c3fb27SDimitry Andric for (unsigned i = 0; i < NumParms; i++) { 253106c3fb27SDimitry Andric const ParmVarDecl *Parm = FD->getParamDecl(i); 253206c3fb27SDimitry Andric 253306c3fb27SDimitry Andric if (Parm->isImplicit()) 253406c3fb27SDimitry Andric continue; 253506c3fb27SDimitry Andric // FIXME: If a parameter has no name, it is unused in the 253606c3fb27SDimitry Andric // definition. So we could just leave it as it is. 253706c3fb27SDimitry Andric if (!Parm->getIdentifier()) 253806c3fb27SDimitry Andric // If a parameter of a function definition has no name: 253906c3fb27SDimitry Andric return std::nullopt; 25405f757f3fSDimitry Andric if (ParmsMask[i]) 254106c3fb27SDimitry Andric // This is our spanified paramter! 25425f757f3fSDimitry Andric SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str() 25435f757f3fSDimitry Andric << ", " << getUserFillPlaceHolder("size") << ")"; 254406c3fb27SDimitry Andric else 254506c3fb27SDimitry Andric SS << Parm->getIdentifier()->getName().str(); 254606c3fb27SDimitry Andric if (i != NumParms - 1) 254706c3fb27SDimitry Andric SS << ", "; 254806c3fb27SDimitry Andric } 254906c3fb27SDimitry Andric // finish call and the body 255006c3fb27SDimitry Andric SS << ");}" << getEndOfLine().str(); 255106c3fb27SDimitry Andric // FIXME: 80-char line formatting? 255206c3fb27SDimitry Andric return SS.str(); 255306c3fb27SDimitry Andric }; 255406c3fb27SDimitry Andric 255506c3fb27SDimitry Andric FixItList FixIts{}; 255606c3fb27SDimitry Andric for (FunctionDecl *FReDecl : FD->redecls()) { 255706c3fb27SDimitry Andric std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts); 255806c3fb27SDimitry Andric 255906c3fb27SDimitry Andric if (!Loc) 256006c3fb27SDimitry Andric return {}; 256106c3fb27SDimitry Andric if (FReDecl->isThisDeclarationADefinition()) { 256206c3fb27SDimitry Andric assert(FReDecl == FD && "inconsistent function definition"); 256306c3fb27SDimitry Andric // Inserts a definition with the old signature to the end of 256406c3fb27SDimitry Andric // `FReDecl`: 25655f757f3fSDimitry Andric if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl)) 256606c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef)); 256706c3fb27SDimitry Andric else 256806c3fb27SDimitry Andric return {}; // give up 256906c3fb27SDimitry Andric } else { 257006c3fb27SDimitry Andric // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`: 257106c3fb27SDimitry Andric if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) { 257206c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion( 257306c3fb27SDimitry Andric FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt( 257406c3fb27SDimitry Andric FReDecl->getBeginLoc(), " "))); 257506c3fb27SDimitry Andric } 257606c3fb27SDimitry Andric // Inserts a declaration with the new signature to the end of `FReDecl`: 25775f757f3fSDimitry Andric if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl)) 257806c3fb27SDimitry Andric FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl)); 257906c3fb27SDimitry Andric else 258006c3fb27SDimitry Andric return {}; 258106c3fb27SDimitry Andric } 258206c3fb27SDimitry Andric } 258306c3fb27SDimitry Andric return FixIts; 258406c3fb27SDimitry Andric } 258506c3fb27SDimitry Andric 25865f757f3fSDimitry Andric // To fix a `ParmVarDecl` to be of `std::span` type. 258706c3fb27SDimitry Andric static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, 258806c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 25895f757f3fSDimitry Andric if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) { 25905f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)"); 25915f757f3fSDimitry Andric return {}; 25925f757f3fSDimitry Andric } 25935f757f3fSDimitry Andric if (PVD->hasDefaultArg()) { 259406c3fb27SDimitry Andric // FIXME: generate fix-its for default values: 25955f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg"); 259606c3fb27SDimitry Andric return {}; 25975f757f3fSDimitry Andric } 259806c3fb27SDimitry Andric 259906c3fb27SDimitry Andric std::optional<Qualifiers> PteTyQualifiers = std::nullopt; 260006c3fb27SDimitry Andric std::optional<std::string> PteTyText = getPointeeTypeText( 260106c3fb27SDimitry Andric PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers); 260206c3fb27SDimitry Andric 26035f757f3fSDimitry Andric if (!PteTyText) { 26045f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type"); 260506c3fb27SDimitry Andric return {}; 26065f757f3fSDimitry Andric } 260706c3fb27SDimitry Andric 260806c3fb27SDimitry Andric std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName(); 260906c3fb27SDimitry Andric 26105f757f3fSDimitry Andric if (!PVDNameText) { 26115f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name"); 261206c3fb27SDimitry Andric return {}; 26135f757f3fSDimitry Andric } 261406c3fb27SDimitry Andric 261506c3fb27SDimitry Andric std::stringstream SS; 26165f757f3fSDimitry Andric std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx); 261706c3fb27SDimitry Andric 261806c3fb27SDimitry Andric if (PteTyQualifiers) 26195f757f3fSDimitry Andric // Append qualifiers if they exist: 26205f757f3fSDimitry Andric SS << getSpanTypeText(*PteTyText, PteTyQualifiers); 26215f757f3fSDimitry Andric else 26225f757f3fSDimitry Andric SS << getSpanTypeText(*PteTyText); 262306c3fb27SDimitry Andric // Append qualifiers to the type of the parameter: 262406c3fb27SDimitry Andric if (PVD->getType().hasQualifiers()) 26255f757f3fSDimitry Andric SS << ' ' << PVD->getType().getQualifiers().getAsString(); 262606c3fb27SDimitry Andric // Append parameter's name: 26275f757f3fSDimitry Andric SS << ' ' << PVDNameText->str(); 26285f757f3fSDimitry Andric // Add replacement fix-it: 26295f757f3fSDimitry Andric return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())}; 263006c3fb27SDimitry Andric } 263106c3fb27SDimitry Andric 263206c3fb27SDimitry Andric static FixItList fixVariableWithSpan(const VarDecl *VD, 263306c3fb27SDimitry Andric const DeclUseTracker &Tracker, 263406c3fb27SDimitry Andric ASTContext &Ctx, 263506c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 263606c3fb27SDimitry Andric const DeclStmt *DS = Tracker.lookupDecl(VD); 26375f757f3fSDimitry Andric if (!DS) { 2638*0fca6ea1SDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, 2639*0fca6ea1SDimitry Andric " : variables declared this way not implemented yet"); 26405f757f3fSDimitry Andric return {}; 26415f757f3fSDimitry Andric } 264206c3fb27SDimitry Andric if (!DS->isSingleDecl()) { 264306c3fb27SDimitry Andric // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` 26445f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls"); 264506c3fb27SDimitry Andric return {}; 264606c3fb27SDimitry Andric } 264706c3fb27SDimitry Andric // Currently DS is an unused variable but we'll need it when 264806c3fb27SDimitry Andric // non-single decls are implemented, where the pointee type name 264906c3fb27SDimitry Andric // and the '*' are spread around the place. 265006c3fb27SDimitry Andric (void)DS; 265106c3fb27SDimitry Andric 265206c3fb27SDimitry Andric // FIXME: handle cases where DS has multiple declarations 26535f757f3fSDimitry Andric return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler); 265406c3fb27SDimitry Andric } 265506c3fb27SDimitry Andric 2656*0fca6ea1SDimitry Andric static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, 2657*0fca6ea1SDimitry Andric UnsafeBufferUsageHandler &Handler) { 2658*0fca6ea1SDimitry Andric FixItList FixIts{}; 2659*0fca6ea1SDimitry Andric 2660*0fca6ea1SDimitry Andric // Note: the code below expects the declaration to not use any type sugar like 2661*0fca6ea1SDimitry Andric // typedef. 2662*0fca6ea1SDimitry Andric if (auto CAT = dyn_cast<clang::ConstantArrayType>(D->getType())) { 2663*0fca6ea1SDimitry Andric const QualType &ArrayEltT = CAT->getElementType(); 2664*0fca6ea1SDimitry Andric assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!"); 2665*0fca6ea1SDimitry Andric // FIXME: support multi-dimensional arrays 2666*0fca6ea1SDimitry Andric if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType())) 2667*0fca6ea1SDimitry Andric return {}; 2668*0fca6ea1SDimitry Andric 2669*0fca6ea1SDimitry Andric const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D); 2670*0fca6ea1SDimitry Andric 2671*0fca6ea1SDimitry Andric // Get the spelling of the element type as written in the source file 2672*0fca6ea1SDimitry Andric // (including macros, etc.). 2673*0fca6ea1SDimitry Andric auto MaybeElemTypeTxt = 2674*0fca6ea1SDimitry Andric getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(), 2675*0fca6ea1SDimitry Andric Ctx.getLangOpts()); 2676*0fca6ea1SDimitry Andric if (!MaybeElemTypeTxt) 2677*0fca6ea1SDimitry Andric return {}; 2678*0fca6ea1SDimitry Andric const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim(); 2679*0fca6ea1SDimitry Andric 2680*0fca6ea1SDimitry Andric // Find the '[' token. 2681*0fca6ea1SDimitry Andric std::optional<Token> NextTok = Lexer::findNextToken( 2682*0fca6ea1SDimitry Andric IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts()); 2683*0fca6ea1SDimitry Andric while (NextTok && !NextTok->is(tok::l_square) && 2684*0fca6ea1SDimitry Andric NextTok->getLocation() <= D->getSourceRange().getEnd()) 2685*0fca6ea1SDimitry Andric NextTok = Lexer::findNextToken(NextTok->getLocation(), 2686*0fca6ea1SDimitry Andric Ctx.getSourceManager(), Ctx.getLangOpts()); 2687*0fca6ea1SDimitry Andric if (!NextTok) 2688*0fca6ea1SDimitry Andric return {}; 2689*0fca6ea1SDimitry Andric const SourceLocation LSqBracketLoc = NextTok->getLocation(); 2690*0fca6ea1SDimitry Andric 2691*0fca6ea1SDimitry Andric // Get the spelling of the array size as written in the source file 2692*0fca6ea1SDimitry Andric // (including macros, etc.). 2693*0fca6ea1SDimitry Andric auto MaybeArraySizeTxt = getRangeText( 2694*0fca6ea1SDimitry Andric {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()}, 2695*0fca6ea1SDimitry Andric Ctx.getSourceManager(), Ctx.getLangOpts()); 2696*0fca6ea1SDimitry Andric if (!MaybeArraySizeTxt) 2697*0fca6ea1SDimitry Andric return {}; 2698*0fca6ea1SDimitry Andric const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim(); 2699*0fca6ea1SDimitry Andric if (ArraySizeTxt.empty()) { 2700*0fca6ea1SDimitry Andric // FIXME: Support array size getting determined from the initializer. 2701*0fca6ea1SDimitry Andric // Examples: 2702*0fca6ea1SDimitry Andric // int arr1[] = {0, 1, 2}; 2703*0fca6ea1SDimitry Andric // int arr2{3, 4, 5}; 2704*0fca6ea1SDimitry Andric // We might be able to preserve the non-specified size with `auto` and 2705*0fca6ea1SDimitry Andric // `std::to_array`: 2706*0fca6ea1SDimitry Andric // auto arr1 = std::to_array<int>({0, 1, 2}); 2707*0fca6ea1SDimitry Andric return {}; 2708*0fca6ea1SDimitry Andric } 2709*0fca6ea1SDimitry Andric 2710*0fca6ea1SDimitry Andric std::optional<StringRef> IdentText = 2711*0fca6ea1SDimitry Andric getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts()); 2712*0fca6ea1SDimitry Andric 2713*0fca6ea1SDimitry Andric if (!IdentText) { 2714*0fca6ea1SDimitry Andric DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier"); 2715*0fca6ea1SDimitry Andric return {}; 2716*0fca6ea1SDimitry Andric } 2717*0fca6ea1SDimitry Andric 2718*0fca6ea1SDimitry Andric SmallString<32> Replacement; 2719*0fca6ea1SDimitry Andric raw_svector_ostream OS(Replacement); 2720*0fca6ea1SDimitry Andric OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> " 2721*0fca6ea1SDimitry Andric << IdentText->str(); 2722*0fca6ea1SDimitry Andric 2723*0fca6ea1SDimitry Andric FixIts.push_back(FixItHint::CreateReplacement( 2724*0fca6ea1SDimitry Andric SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str())); 2725*0fca6ea1SDimitry Andric } 2726*0fca6ea1SDimitry Andric 2727*0fca6ea1SDimitry Andric return FixIts; 2728*0fca6ea1SDimitry Andric } 2729*0fca6ea1SDimitry Andric 2730*0fca6ea1SDimitry Andric static FixItList fixVariableWithArray(const VarDecl *VD, 2731*0fca6ea1SDimitry Andric const DeclUseTracker &Tracker, 2732*0fca6ea1SDimitry Andric const ASTContext &Ctx, 2733*0fca6ea1SDimitry Andric UnsafeBufferUsageHandler &Handler) { 2734*0fca6ea1SDimitry Andric const DeclStmt *DS = Tracker.lookupDecl(VD); 2735*0fca6ea1SDimitry Andric assert(DS && "Fixing non-local variables not implemented yet!"); 2736*0fca6ea1SDimitry Andric if (!DS->isSingleDecl()) { 2737*0fca6ea1SDimitry Andric // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` 2738*0fca6ea1SDimitry Andric return {}; 2739*0fca6ea1SDimitry Andric } 2740*0fca6ea1SDimitry Andric // Currently DS is an unused variable but we'll need it when 2741*0fca6ea1SDimitry Andric // non-single decls are implemented, where the pointee type name 2742*0fca6ea1SDimitry Andric // and the '*' are spread around the place. 2743*0fca6ea1SDimitry Andric (void)DS; 2744*0fca6ea1SDimitry Andric 2745*0fca6ea1SDimitry Andric // FIXME: handle cases where DS has multiple declarations 2746*0fca6ea1SDimitry Andric return fixVarDeclWithArray(VD, Ctx, Handler); 2747*0fca6ea1SDimitry Andric } 2748*0fca6ea1SDimitry Andric 274906c3fb27SDimitry Andric // TODO: we should be consistent to use `std::nullopt` to represent no-fix due 275006c3fb27SDimitry Andric // to any unexpected problem. 275106c3fb27SDimitry Andric static FixItList 2752*0fca6ea1SDimitry Andric fixVariable(const VarDecl *VD, FixitStrategy::Kind K, 275306c3fb27SDimitry Andric /* The function decl under analysis */ const Decl *D, 275406c3fb27SDimitry Andric const DeclUseTracker &Tracker, ASTContext &Ctx, 275506c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler) { 275606c3fb27SDimitry Andric if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) { 275706c3fb27SDimitry Andric auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext()); 27585f757f3fSDimitry Andric if (!FD || FD != D) { 275906c3fb27SDimitry Andric // `FD != D` means that `PVD` belongs to a function that is not being 276006c3fb27SDimitry Andric // analyzed currently. Thus `FD` may not be complete. 27615f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed"); 276206c3fb27SDimitry Andric return {}; 27635f757f3fSDimitry Andric } 276406c3fb27SDimitry Andric 276506c3fb27SDimitry Andric // TODO If function has a try block we can't change params unless we check 276606c3fb27SDimitry Andric // also its catch block for their use. 276706c3fb27SDimitry Andric // FIXME We might support static class methods, some select methods, 276806c3fb27SDimitry Andric // operators and possibly lamdas. 276906c3fb27SDimitry Andric if (FD->isMain() || FD->isConstexpr() || 277006c3fb27SDimitry Andric FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate || 277106c3fb27SDimitry Andric FD->isVariadic() || 277206c3fb27SDimitry Andric // also covers call-operator of lamdas 277306c3fb27SDimitry Andric isa<CXXMethodDecl>(FD) || 277406c3fb27SDimitry Andric // skip when the function body is a try-block 277506c3fb27SDimitry Andric (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) || 27765f757f3fSDimitry Andric FD->isOverloadedOperator()) { 27775f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl"); 277806c3fb27SDimitry Andric return {}; // TODO test all these cases 277906c3fb27SDimitry Andric } 27805f757f3fSDimitry Andric } 278106c3fb27SDimitry Andric 278206c3fb27SDimitry Andric switch (K) { 2783*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Span: { 278406c3fb27SDimitry Andric if (VD->getType()->isPointerType()) { 278506c3fb27SDimitry Andric if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) 278606c3fb27SDimitry Andric return fixParamWithSpan(PVD, Ctx, Handler); 278706c3fb27SDimitry Andric 278806c3fb27SDimitry Andric if (VD->isLocalVarDecl()) 278906c3fb27SDimitry Andric return fixVariableWithSpan(VD, Tracker, Ctx, Handler); 279006c3fb27SDimitry Andric } 27915f757f3fSDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer"); 279206c3fb27SDimitry Andric return {}; 279306c3fb27SDimitry Andric } 2794*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Array: { 2795*0fca6ea1SDimitry Andric if (VD->isLocalVarDecl() && 2796*0fca6ea1SDimitry Andric isa<clang::ConstantArrayType>(VD->getType().getCanonicalType())) 2797*0fca6ea1SDimitry Andric return fixVariableWithArray(VD, Tracker, Ctx, Handler); 2798*0fca6ea1SDimitry Andric 2799*0fca6ea1SDimitry Andric DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array"); 2800*0fca6ea1SDimitry Andric return {}; 2801*0fca6ea1SDimitry Andric } 2802*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Iterator: 2803*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Vector: 2804*0fca6ea1SDimitry Andric llvm_unreachable("FixitStrategy not implemented yet!"); 2805*0fca6ea1SDimitry Andric case FixitStrategy::Kind::Wontfix: 280606c3fb27SDimitry Andric llvm_unreachable("Invalid strategy!"); 280706c3fb27SDimitry Andric } 280806c3fb27SDimitry Andric llvm_unreachable("Unknown strategy!"); 280906c3fb27SDimitry Andric } 281006c3fb27SDimitry Andric 281106c3fb27SDimitry Andric // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the 281206c3fb27SDimitry Andric // `RemoveRange` of 'h' overlaps with a macro use. 281306c3fb27SDimitry Andric static bool overlapWithMacro(const FixItList &FixIts) { 281406c3fb27SDimitry Andric // FIXME: For now we only check if the range (or the first token) is (part of) 281506c3fb27SDimitry Andric // a macro expansion. Ideally, we want to check for all tokens in the range. 281606c3fb27SDimitry Andric return llvm::any_of(FixIts, [](const FixItHint &Hint) { 281706c3fb27SDimitry Andric auto Range = Hint.RemoveRange; 281806c3fb27SDimitry Andric if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID()) 281906c3fb27SDimitry Andric // If the range (or the first token) is (part of) a macro expansion: 282006c3fb27SDimitry Andric return true; 282106c3fb27SDimitry Andric return false; 282206c3fb27SDimitry Andric }); 282306c3fb27SDimitry Andric } 282406c3fb27SDimitry Andric 28255f757f3fSDimitry Andric // Returns true iff `VD` is a parameter of the declaration `D`: 28265f757f3fSDimitry Andric static bool isParameterOf(const VarDecl *VD, const Decl *D) { 28275f757f3fSDimitry Andric return isa<ParmVarDecl>(VD) && 28285f757f3fSDimitry Andric VD->getDeclContext() == dyn_cast<DeclContext>(D); 282906c3fb27SDimitry Andric } 283006c3fb27SDimitry Andric 28315f757f3fSDimitry Andric // Erases variables in `FixItsForVariable`, if such a variable has an unfixable 28325f757f3fSDimitry Andric // group mate. A variable `v` is unfixable iff `FixItsForVariable` does not 28335f757f3fSDimitry Andric // contain `v`. 28345f757f3fSDimitry Andric static void eraseVarsForUnfixableGroupMates( 28355f757f3fSDimitry Andric std::map<const VarDecl *, FixItList> &FixItsForVariable, 28365f757f3fSDimitry Andric const VariableGroupsManager &VarGrpMgr) { 28375f757f3fSDimitry Andric // Variables will be removed from `FixItsForVariable`: 28385f757f3fSDimitry Andric SmallVector<const VarDecl *, 8> ToErase; 28395f757f3fSDimitry Andric 28405f757f3fSDimitry Andric for (const auto &[VD, Ignore] : FixItsForVariable) { 28415f757f3fSDimitry Andric VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD); 28425f757f3fSDimitry Andric if (llvm::any_of(Grp, 28435f757f3fSDimitry Andric [&FixItsForVariable](const VarDecl *GrpMember) -> bool { 28445f757f3fSDimitry Andric return !FixItsForVariable.count(GrpMember); 28455f757f3fSDimitry Andric })) { 28465f757f3fSDimitry Andric // At least one group member cannot be fixed, so we have to erase the 28475f757f3fSDimitry Andric // whole group: 28485f757f3fSDimitry Andric for (const VarDecl *Member : Grp) 28495f757f3fSDimitry Andric ToErase.push_back(Member); 28505f757f3fSDimitry Andric } 28515f757f3fSDimitry Andric } 28525f757f3fSDimitry Andric for (auto *VarToErase : ToErase) 28535f757f3fSDimitry Andric FixItsForVariable.erase(VarToErase); 28545f757f3fSDimitry Andric } 28555f757f3fSDimitry Andric 28565f757f3fSDimitry Andric // Returns the fix-its that create bounds-safe function overloads for the 28575f757f3fSDimitry Andric // function `D`, if `D`'s parameters will be changed to safe-types through 28585f757f3fSDimitry Andric // fix-its in `FixItsForVariable`. 28595f757f3fSDimitry Andric // 28605f757f3fSDimitry Andric // NOTE: In case `D`'s parameters will be changed but bounds-safe function 28615f757f3fSDimitry Andric // overloads cannot created, the whole group that contains the parameters will 28625f757f3fSDimitry Andric // be erased from `FixItsForVariable`. 28635f757f3fSDimitry Andric static FixItList createFunctionOverloadsForParms( 28645f757f3fSDimitry Andric std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */, 28655f757f3fSDimitry Andric const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, 2866*0fca6ea1SDimitry Andric const FixitStrategy &S, ASTContext &Ctx, 2867*0fca6ea1SDimitry Andric UnsafeBufferUsageHandler &Handler) { 28685f757f3fSDimitry Andric FixItList FixItsSharedByParms{}; 28695f757f3fSDimitry Andric 28705f757f3fSDimitry Andric std::optional<FixItList> OverloadFixes = 28715f757f3fSDimitry Andric createOverloadsForFixedParams(S, FD, Ctx, Handler); 28725f757f3fSDimitry Andric 28735f757f3fSDimitry Andric if (OverloadFixes) { 28745f757f3fSDimitry Andric FixItsSharedByParms.append(*OverloadFixes); 28755f757f3fSDimitry Andric } else { 28765f757f3fSDimitry Andric // Something wrong in generating `OverloadFixes`, need to remove the 28775f757f3fSDimitry Andric // whole group, where parameters are in, from `FixItsForVariable` (Note 28785f757f3fSDimitry Andric // that all parameters should be in the same group): 28795f757f3fSDimitry Andric for (auto *Member : VarGrpMgr.getGroupOfParms()) 28805f757f3fSDimitry Andric FixItsForVariable.erase(Member); 28815f757f3fSDimitry Andric } 28825f757f3fSDimitry Andric return FixItsSharedByParms; 28835f757f3fSDimitry Andric } 28845f757f3fSDimitry Andric 28855f757f3fSDimitry Andric // Constructs self-contained fix-its for each variable in `FixablesForAllVars`. 2886bdd1243dSDimitry Andric static std::map<const VarDecl *, FixItList> 2887*0fca6ea1SDimitry Andric getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S, 288806c3fb27SDimitry Andric ASTContext &Ctx, 288906c3fb27SDimitry Andric /* The function decl under analysis */ const Decl *D, 289006c3fb27SDimitry Andric const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, 28915f757f3fSDimitry Andric const VariableGroupsManager &VarGrpMgr) { 28925f757f3fSDimitry Andric // `FixItsForVariable` will map each variable to a set of fix-its directly 28935f757f3fSDimitry Andric // associated to the variable itself. Fix-its of distinct variables in 28945f757f3fSDimitry Andric // `FixItsForVariable` are disjoint. 2895bdd1243dSDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariable; 28965f757f3fSDimitry Andric 28975f757f3fSDimitry Andric // Populate `FixItsForVariable` with fix-its directly associated with each 28985f757f3fSDimitry Andric // variable. Fix-its directly associated to a variable 'v' are the ones 28995f757f3fSDimitry Andric // produced by the `FixableGadget`s whose claimed variable is 'v'. 290006c3fb27SDimitry Andric for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) { 290106c3fb27SDimitry Andric FixItsForVariable[VD] = 290206c3fb27SDimitry Andric fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler); 290306c3fb27SDimitry Andric // If we fail to produce Fix-It for the declaration we have to skip the 290406c3fb27SDimitry Andric // variable entirely. 290506c3fb27SDimitry Andric if (FixItsForVariable[VD].empty()) { 290606c3fb27SDimitry Andric FixItsForVariable.erase(VD); 290706c3fb27SDimitry Andric continue; 290806c3fb27SDimitry Andric } 2909bdd1243dSDimitry Andric for (const auto &F : Fixables) { 291006c3fb27SDimitry Andric std::optional<FixItList> Fixits = F->getFixits(S); 291106c3fb27SDimitry Andric 29125f757f3fSDimitry Andric if (Fixits) { 2913bdd1243dSDimitry Andric FixItsForVariable[VD].insert(FixItsForVariable[VD].end(), 29145f757f3fSDimitry Andric Fixits->begin(), Fixits->end()); 29155f757f3fSDimitry Andric continue; 29165f757f3fSDimitry Andric } 29175f757f3fSDimitry Andric #ifndef NDEBUG 29185f757f3fSDimitry Andric Handler.addDebugNoteForVar( 2919*0fca6ea1SDimitry Andric VD, F->getSourceLoc(), 29205f757f3fSDimitry Andric ("gadget '" + F->getDebugName() + "' refused to produce a fix") 29215f757f3fSDimitry Andric .str()); 29225f757f3fSDimitry Andric #endif 292306c3fb27SDimitry Andric FixItsForVariable.erase(VD); 29245f757f3fSDimitry Andric break; 29255f757f3fSDimitry Andric } 29265f757f3fSDimitry Andric } 29275f757f3fSDimitry Andric 29285f757f3fSDimitry Andric // `FixItsForVariable` now contains only variables that can be 29295f757f3fSDimitry Andric // fixed. A variable can be fixed if its' declaration and all Fixables 29305f757f3fSDimitry Andric // associated to it can all be fixed. 29315f757f3fSDimitry Andric 29325f757f3fSDimitry Andric // To further remove from `FixItsForVariable` variables whose group mates 29335f757f3fSDimitry Andric // cannot be fixed... 29345f757f3fSDimitry Andric eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr); 29355f757f3fSDimitry Andric // Now `FixItsForVariable` gets further reduced: a variable is in 29365f757f3fSDimitry Andric // `FixItsForVariable` iff it can be fixed and all its group mates can be 29375f757f3fSDimitry Andric // fixed. 29385f757f3fSDimitry Andric 29395f757f3fSDimitry Andric // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`. 29405f757f3fSDimitry Andric // That is, when fixing multiple parameters in one step, these fix-its will 29415f757f3fSDimitry Andric // be applied only once (instead of being applied per parameter). 29425f757f3fSDimitry Andric FixItList FixItsSharedByParms{}; 29435f757f3fSDimitry Andric 29445f757f3fSDimitry Andric if (auto *FD = dyn_cast<FunctionDecl>(D)) 29455f757f3fSDimitry Andric FixItsSharedByParms = createFunctionOverloadsForParms( 29465f757f3fSDimitry Andric FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler); 29475f757f3fSDimitry Andric 29485f757f3fSDimitry Andric // The map that maps each variable `v` to fix-its for the whole group where 29495f757f3fSDimitry Andric // `v` is in: 29505f757f3fSDimitry Andric std::map<const VarDecl *, FixItList> FinalFixItsForVariable{ 29515f757f3fSDimitry Andric FixItsForVariable}; 29525f757f3fSDimitry Andric 29535f757f3fSDimitry Andric for (auto &[Var, Ignore] : FixItsForVariable) { 29545f757f3fSDimitry Andric bool AnyParm = false; 29555f757f3fSDimitry Andric const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm); 29565f757f3fSDimitry Andric 29575f757f3fSDimitry Andric for (const VarDecl *GrpMate : VarGroupForVD) { 29585f757f3fSDimitry Andric if (Var == GrpMate) 295906c3fb27SDimitry Andric continue; 29605f757f3fSDimitry Andric if (FixItsForVariable.count(GrpMate)) 29615f757f3fSDimitry Andric FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]); 296206c3fb27SDimitry Andric } 29635f757f3fSDimitry Andric if (AnyParm) { 29645f757f3fSDimitry Andric // This assertion should never fail. Otherwise we have a bug. 29655f757f3fSDimitry Andric assert(!FixItsSharedByParms.empty() && 29665f757f3fSDimitry Andric "Should not try to fix a parameter that does not belong to a " 29675f757f3fSDimitry Andric "FunctionDecl"); 29685f757f3fSDimitry Andric FinalFixItsForVariable[Var].append(FixItsSharedByParms); 29695f757f3fSDimitry Andric } 29705f757f3fSDimitry Andric } 29715f757f3fSDimitry Andric // Fix-its that will be applied in one step shall NOT: 29725f757f3fSDimitry Andric // 1. overlap with macros or/and templates; or 29735f757f3fSDimitry Andric // 2. conflict with each other. 29745f757f3fSDimitry Andric // Otherwise, the fix-its will be dropped. 29755f757f3fSDimitry Andric for (auto Iter = FinalFixItsForVariable.begin(); 29765f757f3fSDimitry Andric Iter != FinalFixItsForVariable.end();) 29775f757f3fSDimitry Andric if (overlapWithMacro(Iter->second) || 29785f757f3fSDimitry Andric clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) { 29795f757f3fSDimitry Andric Iter = FinalFixItsForVariable.erase(Iter); 29805f757f3fSDimitry Andric } else 29815f757f3fSDimitry Andric Iter++; 29825f757f3fSDimitry Andric return FinalFixItsForVariable; 298306c3fb27SDimitry Andric } 298406c3fb27SDimitry Andric 29855f757f3fSDimitry Andric template <typename VarDeclIterTy> 2986*0fca6ea1SDimitry Andric static FixitStrategy 29875f757f3fSDimitry Andric getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) { 2988*0fca6ea1SDimitry Andric FixitStrategy S; 2989bdd1243dSDimitry Andric for (const VarDecl *VD : UnsafeVars) { 2990*0fca6ea1SDimitry Andric if (isa<ConstantArrayType>(VD->getType().getCanonicalType())) 2991*0fca6ea1SDimitry Andric S.set(VD, FixitStrategy::Kind::Array); 2992*0fca6ea1SDimitry Andric else 2993*0fca6ea1SDimitry Andric S.set(VD, FixitStrategy::Kind::Span); 2994bdd1243dSDimitry Andric } 2995bdd1243dSDimitry Andric return S; 2996bdd1243dSDimitry Andric } 2997bdd1243dSDimitry Andric 29985f757f3fSDimitry Andric // Manages variable groups: 29995f757f3fSDimitry Andric class VariableGroupsManagerImpl : public VariableGroupsManager { 30005f757f3fSDimitry Andric const std::vector<VarGrpTy> Groups; 30015f757f3fSDimitry Andric const std::map<const VarDecl *, unsigned> &VarGrpMap; 30025f757f3fSDimitry Andric const llvm::SetVector<const VarDecl *> &GrpsUnionForParms; 30035f757f3fSDimitry Andric 30045f757f3fSDimitry Andric public: 30055f757f3fSDimitry Andric VariableGroupsManagerImpl( 30065f757f3fSDimitry Andric const std::vector<VarGrpTy> &Groups, 30075f757f3fSDimitry Andric const std::map<const VarDecl *, unsigned> &VarGrpMap, 30085f757f3fSDimitry Andric const llvm::SetVector<const VarDecl *> &GrpsUnionForParms) 30095f757f3fSDimitry Andric : Groups(Groups), VarGrpMap(VarGrpMap), 30105f757f3fSDimitry Andric GrpsUnionForParms(GrpsUnionForParms) {} 30115f757f3fSDimitry Andric 30125f757f3fSDimitry Andric VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override { 30135f757f3fSDimitry Andric if (GrpsUnionForParms.contains(Var)) { 30145f757f3fSDimitry Andric if (HasParm) 30155f757f3fSDimitry Andric *HasParm = true; 30165f757f3fSDimitry Andric return GrpsUnionForParms.getArrayRef(); 30175f757f3fSDimitry Andric } 30185f757f3fSDimitry Andric if (HasParm) 30195f757f3fSDimitry Andric *HasParm = false; 30205f757f3fSDimitry Andric 30215f757f3fSDimitry Andric auto It = VarGrpMap.find(Var); 30225f757f3fSDimitry Andric 30235f757f3fSDimitry Andric if (It == VarGrpMap.end()) 30245f757f3fSDimitry Andric return std::nullopt; 30255f757f3fSDimitry Andric return Groups[It->second]; 30265f757f3fSDimitry Andric } 30275f757f3fSDimitry Andric 30285f757f3fSDimitry Andric VarGrpRef getGroupOfParms() const override { 30295f757f3fSDimitry Andric return GrpsUnionForParms.getArrayRef(); 30305f757f3fSDimitry Andric } 30315f757f3fSDimitry Andric }; 30325f757f3fSDimitry Andric 3033bdd1243dSDimitry Andric void clang::checkUnsafeBufferUsage(const Decl *D, 303406c3fb27SDimitry Andric UnsafeBufferUsageHandler &Handler, 303506c3fb27SDimitry Andric bool EmitSuggestions) { 30365f757f3fSDimitry Andric #ifndef NDEBUG 30375f757f3fSDimitry Andric Handler.clearDebugNotes(); 30385f757f3fSDimitry Andric #endif 3039bdd1243dSDimitry Andric 30405f757f3fSDimitry Andric assert(D && D->getBody()); 3041*0fca6ea1SDimitry Andric // We do not want to visit a Lambda expression defined inside a method 3042*0fca6ea1SDimitry Andric // independently. Instead, it should be visited along with the outer method. 30435f757f3fSDimitry Andric // FIXME: do we want to do the same thing for `BlockDecl`s? 304406c3fb27SDimitry Andric if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) { 304506c3fb27SDimitry Andric if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass()) 304606c3fb27SDimitry Andric return; 3047bdd1243dSDimitry Andric } 3048bdd1243dSDimitry Andric 304906c3fb27SDimitry Andric // Do not emit fixit suggestions for functions declared in an 305006c3fb27SDimitry Andric // extern "C" block. 305106c3fb27SDimitry Andric if (const auto *FD = dyn_cast<FunctionDecl>(D)) { 305206c3fb27SDimitry Andric for (FunctionDecl *FReDecl : FD->redecls()) { 305306c3fb27SDimitry Andric if (FReDecl->isExternC()) { 305406c3fb27SDimitry Andric EmitSuggestions = false; 305506c3fb27SDimitry Andric break; 305606c3fb27SDimitry Andric } 305706c3fb27SDimitry Andric } 305806c3fb27SDimitry Andric } 305906c3fb27SDimitry Andric 306006c3fb27SDimitry Andric WarningGadgetSets UnsafeOps; 306106c3fb27SDimitry Andric FixableGadgetSets FixablesForAllVars; 306206c3fb27SDimitry Andric 306306c3fb27SDimitry Andric auto [FixableGadgets, WarningGadgets, Tracker] = 306406c3fb27SDimitry Andric findGadgets(D, Handler, EmitSuggestions); 306506c3fb27SDimitry Andric 306606c3fb27SDimitry Andric if (!EmitSuggestions) { 306706c3fb27SDimitry Andric // Our job is very easy without suggestions. Just warn about 306806c3fb27SDimitry Andric // every problematic operation and consider it done. No need to deal 306906c3fb27SDimitry Andric // with fixable gadgets, no need to group operations by variable. 307006c3fb27SDimitry Andric for (const auto &G : WarningGadgets) { 3071*0fca6ea1SDimitry Andric G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false, 30721db9f3b2SDimitry Andric D->getASTContext()); 307306c3fb27SDimitry Andric } 307406c3fb27SDimitry Andric 307506c3fb27SDimitry Andric // This return guarantees that most of the machine doesn't run when 307606c3fb27SDimitry Andric // suggestions aren't requested. 307706c3fb27SDimitry Andric assert(FixableGadgets.size() == 0 && 307806c3fb27SDimitry Andric "Fixable gadgets found but suggestions not requested!"); 307906c3fb27SDimitry Andric return; 308006c3fb27SDimitry Andric } 308106c3fb27SDimitry Andric 30825f757f3fSDimitry Andric // If no `WarningGadget`s ever matched, there is no unsafe operations in the 30835f757f3fSDimitry Andric // function under the analysis. No need to fix any Fixables. 30845f757f3fSDimitry Andric if (!WarningGadgets.empty()) { 30855f757f3fSDimitry Andric // Gadgets "claim" variables they're responsible for. Once this loop 30865f757f3fSDimitry Andric // finishes, the tracker will only track DREs that weren't claimed by any 30875f757f3fSDimitry Andric // gadgets, i.e. not understood by the analysis. 30885f757f3fSDimitry Andric for (const auto &G : FixableGadgets) { 30895f757f3fSDimitry Andric for (const auto *DRE : G->getClaimedVarUseSites()) { 30905f757f3fSDimitry Andric Tracker.claimUse(DRE); 30915f757f3fSDimitry Andric } 30925f757f3fSDimitry Andric } 30935f757f3fSDimitry Andric } 30945f757f3fSDimitry Andric 30955f757f3fSDimitry Andric // If no `WarningGadget`s ever matched, there is no unsafe operations in the 30965f757f3fSDimitry Andric // function under the analysis. Thus, it early returns here as there is 30975f757f3fSDimitry Andric // nothing needs to be fixed. 30985f757f3fSDimitry Andric // 30995f757f3fSDimitry Andric // Note this claim is based on the assumption that there is no unsafe 31005f757f3fSDimitry Andric // variable whose declaration is invisible from the analyzing function. 31015f757f3fSDimitry Andric // Otherwise, we need to consider if the uses of those unsafe varuables needs 31025f757f3fSDimitry Andric // fix. 31035f757f3fSDimitry Andric // So far, we are not fixing any global variables or class members. And, 31045f757f3fSDimitry Andric // lambdas will be analyzed along with the enclosing function. So this early 31055f757f3fSDimitry Andric // return is correct for now. 31065f757f3fSDimitry Andric if (WarningGadgets.empty()) 31075f757f3fSDimitry Andric return; 31085f757f3fSDimitry Andric 310906c3fb27SDimitry Andric UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); 311006c3fb27SDimitry Andric FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets)); 311106c3fb27SDimitry Andric 311206c3fb27SDimitry Andric std::map<const VarDecl *, FixItList> FixItsForVariableGroup; 311306c3fb27SDimitry Andric 3114bdd1243dSDimitry Andric // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. 311506c3fb27SDimitry Andric for (auto it = FixablesForAllVars.byVar.cbegin(); 311606c3fb27SDimitry Andric it != FixablesForAllVars.byVar.cend();) { 311706c3fb27SDimitry Andric // FIXME: need to deal with global variables later 31185f757f3fSDimitry Andric if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) { 31195f757f3fSDimitry Andric #ifndef NDEBUG 3120*0fca6ea1SDimitry Andric Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(), 3121*0fca6ea1SDimitry Andric ("failed to produce fixit for '" + 3122*0fca6ea1SDimitry Andric it->first->getNameAsString() + 31235f757f3fSDimitry Andric "' : neither local nor a parameter")); 31245f757f3fSDimitry Andric #endif 31255f757f3fSDimitry Andric it = FixablesForAllVars.byVar.erase(it); 31265f757f3fSDimitry Andric } else if (it->first->getType().getCanonicalType()->isReferenceType()) { 31275f757f3fSDimitry Andric #ifndef NDEBUG 31285f757f3fSDimitry Andric Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(), 31295f757f3fSDimitry Andric ("failed to produce fixit for '" + 31305f757f3fSDimitry Andric it->first->getNameAsString() + 31315f757f3fSDimitry Andric "' : has a reference type")); 31325f757f3fSDimitry Andric #endif 31335f757f3fSDimitry Andric it = FixablesForAllVars.byVar.erase(it); 31345f757f3fSDimitry Andric } else if (Tracker.hasUnclaimedUses(it->first)) { 31355f757f3fSDimitry Andric it = FixablesForAllVars.byVar.erase(it); 31365f757f3fSDimitry Andric } else if (it->first->isInitCapture()) { 31375f757f3fSDimitry Andric #ifndef NDEBUG 3138*0fca6ea1SDimitry Andric Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(), 3139*0fca6ea1SDimitry Andric ("failed to produce fixit for '" + 3140*0fca6ea1SDimitry Andric it->first->getNameAsString() + 31415f757f3fSDimitry Andric "' : init capture")); 31425f757f3fSDimitry Andric #endif 314306c3fb27SDimitry Andric it = FixablesForAllVars.byVar.erase(it); 3144bdd1243dSDimitry Andric } else { 3145bdd1243dSDimitry Andric ++it; 3146bdd1243dSDimitry Andric } 3147bdd1243dSDimitry Andric } 3148bdd1243dSDimitry Andric 3149*0fca6ea1SDimitry Andric #ifndef NDEBUG 3150*0fca6ea1SDimitry Andric for (const auto &it : UnsafeOps.byVar) { 3151*0fca6ea1SDimitry Andric const VarDecl *const UnsafeVD = it.first; 3152*0fca6ea1SDimitry Andric auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD); 3153*0fca6ea1SDimitry Andric if (UnclaimedDREs.empty()) 3154*0fca6ea1SDimitry Andric continue; 3155*0fca6ea1SDimitry Andric const auto UnfixedVDName = UnsafeVD->getNameAsString(); 3156*0fca6ea1SDimitry Andric for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) { 3157*0fca6ea1SDimitry Andric std::string UnclaimedUseTrace = 3158*0fca6ea1SDimitry Andric getDREAncestorString(UnclaimedDRE, D->getASTContext()); 3159*0fca6ea1SDimitry Andric 3160*0fca6ea1SDimitry Andric Handler.addDebugNoteForVar( 3161*0fca6ea1SDimitry Andric UnsafeVD, UnclaimedDRE->getBeginLoc(), 3162*0fca6ea1SDimitry Andric ("failed to produce fixit for '" + UnfixedVDName + 3163*0fca6ea1SDimitry Andric "' : has an unclaimed use\nThe unclaimed DRE trace: " + 3164*0fca6ea1SDimitry Andric UnclaimedUseTrace)); 3165*0fca6ea1SDimitry Andric } 3166*0fca6ea1SDimitry Andric } 3167*0fca6ea1SDimitry Andric #endif 3168*0fca6ea1SDimitry Andric 316906c3fb27SDimitry Andric // Fixpoint iteration for pointer assignments 31705f757f3fSDimitry Andric using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>; 317106c3fb27SDimitry Andric DepMapTy DependenciesMap{}; 317206c3fb27SDimitry Andric DepMapTy PtrAssignmentGraph{}; 317306c3fb27SDimitry Andric 317406c3fb27SDimitry Andric for (auto it : FixablesForAllVars.byVar) { 317506c3fb27SDimitry Andric for (const FixableGadget *fixable : it.second) { 317606c3fb27SDimitry Andric std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair = 317706c3fb27SDimitry Andric fixable->getStrategyImplications(); 317806c3fb27SDimitry Andric if (ImplPair) { 31795f757f3fSDimitry Andric std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair); 318006c3fb27SDimitry Andric PtrAssignmentGraph[Impl.first].insert(Impl.second); 318106c3fb27SDimitry Andric } 318206c3fb27SDimitry Andric } 318306c3fb27SDimitry Andric } 318406c3fb27SDimitry Andric 318506c3fb27SDimitry Andric /* 318606c3fb27SDimitry Andric The following code does a BFS traversal of the `PtrAssignmentGraph` 318706c3fb27SDimitry Andric considering all unsafe vars as starting nodes and constructs an undirected 318806c3fb27SDimitry Andric graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner 318906c3fb27SDimitry Andric elimiates all variables that are unreachable from any unsafe var. In other 319006c3fb27SDimitry Andric words, this removes all dependencies that don't include any unsafe variable 319106c3fb27SDimitry Andric and consequently don't need any fixit generation. 319206c3fb27SDimitry Andric Note: A careful reader would observe that the code traverses 319306c3fb27SDimitry Andric `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and 319406c3fb27SDimitry Andric `Adj` and not between `CurrentVar` and `Adj`. Both approaches would 319506c3fb27SDimitry Andric achieve the same result but the one used here dramatically cuts the 319606c3fb27SDimitry Andric amount of hoops the second part of the algorithm needs to jump, given that 319706c3fb27SDimitry Andric a lot of these connections become "direct". The reader is advised not to 319806c3fb27SDimitry Andric imagine how the graph is transformed because of using `Var` instead of 319906c3fb27SDimitry Andric `CurrentVar`. The reader can continue reading as if `CurrentVar` was used, 320006c3fb27SDimitry Andric and think about why it's equivalent later. 320106c3fb27SDimitry Andric */ 320206c3fb27SDimitry Andric std::set<const VarDecl *> VisitedVarsDirected{}; 320306c3fb27SDimitry Andric for (const auto &[Var, ignore] : UnsafeOps.byVar) { 320406c3fb27SDimitry Andric if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) { 320506c3fb27SDimitry Andric 320606c3fb27SDimitry Andric std::queue<const VarDecl *> QueueDirected{}; 320706c3fb27SDimitry Andric QueueDirected.push(Var); 320806c3fb27SDimitry Andric while (!QueueDirected.empty()) { 320906c3fb27SDimitry Andric const VarDecl *CurrentVar = QueueDirected.front(); 321006c3fb27SDimitry Andric QueueDirected.pop(); 321106c3fb27SDimitry Andric VisitedVarsDirected.insert(CurrentVar); 321206c3fb27SDimitry Andric auto AdjacentNodes = PtrAssignmentGraph[CurrentVar]; 321306c3fb27SDimitry Andric for (const VarDecl *Adj : AdjacentNodes) { 321406c3fb27SDimitry Andric if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) { 321506c3fb27SDimitry Andric QueueDirected.push(Adj); 321606c3fb27SDimitry Andric } 321706c3fb27SDimitry Andric DependenciesMap[Var].insert(Adj); 321806c3fb27SDimitry Andric DependenciesMap[Adj].insert(Var); 321906c3fb27SDimitry Andric } 322006c3fb27SDimitry Andric } 322106c3fb27SDimitry Andric } 322206c3fb27SDimitry Andric } 322306c3fb27SDimitry Andric 32245f757f3fSDimitry Andric // `Groups` stores the set of Connected Components in the graph. 32255f757f3fSDimitry Andric std::vector<VarGrpTy> Groups; 32265f757f3fSDimitry Andric // `VarGrpMap` maps variables that need fix to the groups (indexes) that the 32275f757f3fSDimitry Andric // variables belong to. Group indexes refer to the elements in `Groups`. 32285f757f3fSDimitry Andric // `VarGrpMap` is complete in that every variable that needs fix is in it. 32295f757f3fSDimitry Andric std::map<const VarDecl *, unsigned> VarGrpMap; 32305f757f3fSDimitry Andric // The union group over the ones in "Groups" that contain parameters of `D`: 32315f757f3fSDimitry Andric llvm::SetVector<const VarDecl *> 32325f757f3fSDimitry Andric GrpsUnionForParms; // these variables need to be fixed in one step 32335f757f3fSDimitry Andric 323406c3fb27SDimitry Andric // Group Connected Components for Unsafe Vars 323506c3fb27SDimitry Andric // (Dependencies based on pointer assignments) 323606c3fb27SDimitry Andric std::set<const VarDecl *> VisitedVars{}; 323706c3fb27SDimitry Andric for (const auto &[Var, ignore] : UnsafeOps.byVar) { 323806c3fb27SDimitry Andric if (VisitedVars.find(Var) == VisitedVars.end()) { 32395f757f3fSDimitry Andric VarGrpTy &VarGroup = Groups.emplace_back(); 324006c3fb27SDimitry Andric std::queue<const VarDecl *> Queue{}; 32415f757f3fSDimitry Andric 324206c3fb27SDimitry Andric Queue.push(Var); 324306c3fb27SDimitry Andric while (!Queue.empty()) { 324406c3fb27SDimitry Andric const VarDecl *CurrentVar = Queue.front(); 324506c3fb27SDimitry Andric Queue.pop(); 324606c3fb27SDimitry Andric VisitedVars.insert(CurrentVar); 324706c3fb27SDimitry Andric VarGroup.push_back(CurrentVar); 324806c3fb27SDimitry Andric auto AdjacentNodes = DependenciesMap[CurrentVar]; 324906c3fb27SDimitry Andric for (const VarDecl *Adj : AdjacentNodes) { 325006c3fb27SDimitry Andric if (VisitedVars.find(Adj) == VisitedVars.end()) { 325106c3fb27SDimitry Andric Queue.push(Adj); 325206c3fb27SDimitry Andric } 325306c3fb27SDimitry Andric } 325406c3fb27SDimitry Andric } 32555f757f3fSDimitry Andric 32565f757f3fSDimitry Andric bool HasParm = false; 32575f757f3fSDimitry Andric unsigned GrpIdx = Groups.size() - 1; 32585f757f3fSDimitry Andric 325906c3fb27SDimitry Andric for (const VarDecl *V : VarGroup) { 32605f757f3fSDimitry Andric VarGrpMap[V] = GrpIdx; 32615f757f3fSDimitry Andric if (!HasParm && isParameterOf(V, D)) 32625f757f3fSDimitry Andric HasParm = true; 326306c3fb27SDimitry Andric } 32645f757f3fSDimitry Andric if (HasParm) 32655f757f3fSDimitry Andric GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end()); 326606c3fb27SDimitry Andric } 326706c3fb27SDimitry Andric } 326806c3fb27SDimitry Andric 32695f757f3fSDimitry Andric // Remove a `FixableGadget` if the associated variable is not in the graph 32705f757f3fSDimitry Andric // computed above. We do not want to generate fix-its for such variables, 32715f757f3fSDimitry Andric // since they are neither warned nor reachable from a warned one. 32725f757f3fSDimitry Andric // 32735f757f3fSDimitry Andric // Note a variable is not warned if it is not directly used in any unsafe 32745f757f3fSDimitry Andric // operation. A variable `v` is NOT reachable from an unsafe variable, if it 32755f757f3fSDimitry Andric // does not exist another variable `u` such that `u` is warned and fixing `u` 32765f757f3fSDimitry Andric // (transitively) implicates fixing `v`. 32775f757f3fSDimitry Andric // 32785f757f3fSDimitry Andric // For example, 32795f757f3fSDimitry Andric // ``` 32805f757f3fSDimitry Andric // void f(int * p) { 32815f757f3fSDimitry Andric // int * a = p; *p = 0; 32825f757f3fSDimitry Andric // } 32835f757f3fSDimitry Andric // ``` 32845f757f3fSDimitry Andric // `*p = 0` is a fixable gadget associated with a variable `p` that is neither 32855f757f3fSDimitry Andric // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of 32865f757f3fSDimitry Andric // the function above, `p` becomes reachable from a warned variable. 32875f757f3fSDimitry Andric for (auto I = FixablesForAllVars.byVar.begin(); 32885f757f3fSDimitry Andric I != FixablesForAllVars.byVar.end();) { 32895f757f3fSDimitry Andric // Note `VisitedVars` contain all the variables in the graph: 32905f757f3fSDimitry Andric if (!VisitedVars.count((*I).first)) { 32915f757f3fSDimitry Andric // no such var in graph: 32925f757f3fSDimitry Andric I = FixablesForAllVars.byVar.erase(I); 32935f757f3fSDimitry Andric } else 32945f757f3fSDimitry Andric ++I; 32955f757f3fSDimitry Andric } 329606c3fb27SDimitry Andric 32975f757f3fSDimitry Andric // We assign strategies to variables that are 1) in the graph and 2) can be 32985f757f3fSDimitry Andric // fixed. Other variables have the default "Won't fix" strategy. 3299*0fca6ea1SDimitry Andric FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range( 33005f757f3fSDimitry Andric VisitedVars, [&FixablesForAllVars](const VarDecl *V) { 33015f757f3fSDimitry Andric // If a warned variable has no "Fixable", it is considered unfixable: 33025f757f3fSDimitry Andric return FixablesForAllVars.byVar.count(V); 33035f757f3fSDimitry Andric })); 33045f757f3fSDimitry Andric VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms); 33055f757f3fSDimitry Andric 33065f757f3fSDimitry Andric if (isa<NamedDecl>(D)) 33075f757f3fSDimitry Andric // The only case where `D` is not a `NamedDecl` is when `D` is a 33085f757f3fSDimitry Andric // `BlockDecl`. Let's not fix variables in blocks for now 330906c3fb27SDimitry Andric FixItsForVariableGroup = 331006c3fb27SDimitry Andric getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D, 33115f757f3fSDimitry Andric Tracker, Handler, VarGrpMgr); 3312bdd1243dSDimitry Andric 3313bdd1243dSDimitry Andric for (const auto &G : UnsafeOps.noVar) { 3314*0fca6ea1SDimitry Andric G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false, 33151db9f3b2SDimitry Andric D->getASTContext()); 3316bdd1243dSDimitry Andric } 3317bdd1243dSDimitry Andric 3318bdd1243dSDimitry Andric for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) { 331906c3fb27SDimitry Andric auto FixItsIt = FixItsForVariableGroup.find(VD); 33205f757f3fSDimitry Andric Handler.handleUnsafeVariableGroup(VD, VarGrpMgr, 332106c3fb27SDimitry Andric FixItsIt != FixItsForVariableGroup.end() 3322bdd1243dSDimitry Andric ? std::move(FixItsIt->second) 33235f757f3fSDimitry Andric : FixItList{}, 3324*0fca6ea1SDimitry Andric D, NaiveStrategy); 3325bdd1243dSDimitry Andric for (const auto &G : WarningGadgets) { 3326*0fca6ea1SDimitry Andric G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true, 33271db9f3b2SDimitry Andric D->getASTContext()); 3328bdd1243dSDimitry Andric } 3329bdd1243dSDimitry Andric } 3330bdd1243dSDimitry Andric } 3331