1 //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/AST/Decl.h" 12 #include "clang/AST/Expr.h" 13 #include "clang/AST/RecursiveASTVisitor.h" 14 #include "clang/AST/Stmt.h" 15 #include "clang/AST/StmtVisitor.h" 16 #include "clang/ASTMatchers/ASTMatchFinder.h" 17 #include "clang/ASTMatchers/ASTMatchers.h" 18 #include "clang/Basic/CharInfo.h" 19 #include "clang/Basic/SourceLocation.h" 20 #include "clang/Lex/Lexer.h" 21 #include "clang/Lex/Preprocessor.h" 22 #include "llvm/ADT/APSInt.h" 23 #include "llvm/ADT/SmallVector.h" 24 #include "llvm/ADT/StringRef.h" 25 #include "llvm/Support/Casting.h" 26 #include <memory> 27 #include <optional> 28 #include <queue> 29 #include <sstream> 30 31 using namespace llvm; 32 using namespace clang; 33 using namespace ast_matchers; 34 35 #ifndef NDEBUG 36 namespace { 37 class StmtDebugPrinter 38 : public ConstStmtVisitor<StmtDebugPrinter, std::string> { 39 public: 40 std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); } 41 42 std::string VisitBinaryOperator(const BinaryOperator *BO) { 43 return "BinaryOperator(" + BO->getOpcodeStr().str() + ")"; 44 } 45 46 std::string VisitUnaryOperator(const UnaryOperator *UO) { 47 return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")"; 48 } 49 50 std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) { 51 return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")"; 52 } 53 }; 54 55 // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form: 56 // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...". 57 static std::string getDREAncestorString(const DeclRefExpr *DRE, 58 ASTContext &Ctx) { 59 std::stringstream SS; 60 const Stmt *St = DRE; 61 StmtDebugPrinter StmtPriner; 62 63 do { 64 SS << StmtPriner.Visit(St); 65 66 DynTypedNodeList StParents = Ctx.getParents(*St); 67 68 if (StParents.size() > 1) 69 return "unavailable due to multiple parents"; 70 if (StParents.size() == 0) 71 break; 72 St = StParents.begin()->get<Stmt>(); 73 if (St) 74 SS << " ==> "; 75 } while (St); 76 return SS.str(); 77 } 78 } // namespace 79 #endif /* NDEBUG */ 80 81 namespace clang::ast_matchers { 82 // A `RecursiveASTVisitor` that traverses all descendants of a given node "n" 83 // except for those belonging to a different callable of "n". 84 class MatchDescendantVisitor 85 : public RecursiveASTVisitor<MatchDescendantVisitor> { 86 public: 87 typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase; 88 89 // Creates an AST visitor that matches `Matcher` on all 90 // descendants of a given node "n" except for the ones 91 // belonging to a different callable of "n". 92 MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, 93 internal::ASTMatchFinder *Finder, 94 internal::BoundNodesTreeBuilder *Builder, 95 internal::ASTMatchFinder::BindKind Bind, 96 const bool ignoreUnevaluatedContext) 97 : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind), 98 Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {} 99 100 // Returns true if a match is found in a subtree of `DynNode`, which belongs 101 // to the same callable of `DynNode`. 102 bool findMatch(const DynTypedNode &DynNode) { 103 Matches = false; 104 if (const Stmt *StmtNode = DynNode.get<Stmt>()) { 105 TraverseStmt(const_cast<Stmt *>(StmtNode)); 106 *Builder = ResultBindings; 107 return Matches; 108 } 109 return false; 110 } 111 112 // The following are overriding methods from the base visitor class. 113 // They are public only to allow CRTP to work. They are *not *part 114 // of the public API of this class. 115 116 // For the matchers so far used in safe buffers, we only need to match 117 // `Stmt`s. To override more as needed. 118 119 bool TraverseDecl(Decl *Node) { 120 if (!Node) 121 return true; 122 if (!match(*Node)) 123 return false; 124 // To skip callables: 125 if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node)) 126 return true; 127 // Traverse descendants 128 return VisitorBase::TraverseDecl(Node); 129 } 130 131 bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) { 132 // These are unevaluated, except the result expression. 133 if (ignoreUnevaluatedContext) 134 return TraverseStmt(Node->getResultExpr()); 135 return VisitorBase::TraverseGenericSelectionExpr(Node); 136 } 137 138 bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { 139 // Unevaluated context. 140 if (ignoreUnevaluatedContext) 141 return true; 142 return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node); 143 } 144 145 bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) { 146 // Unevaluated context. 147 if (ignoreUnevaluatedContext) 148 return true; 149 return VisitorBase::TraverseTypeOfExprTypeLoc(Node); 150 } 151 152 bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) { 153 // Unevaluated context. 154 if (ignoreUnevaluatedContext) 155 return true; 156 return VisitorBase::TraverseDecltypeTypeLoc(Node); 157 } 158 159 bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) { 160 // Unevaluated context. 161 if (ignoreUnevaluatedContext) 162 return true; 163 return VisitorBase::TraverseCXXNoexceptExpr(Node); 164 } 165 166 bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) { 167 // Unevaluated context. 168 if (ignoreUnevaluatedContext) 169 return true; 170 return VisitorBase::TraverseCXXTypeidExpr(Node); 171 } 172 173 bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) { 174 if (!Node) 175 return true; 176 if (!match(*Node)) 177 return false; 178 return VisitorBase::TraverseStmt(Node); 179 } 180 181 bool shouldVisitTemplateInstantiations() const { return true; } 182 bool shouldVisitImplicitCode() const { 183 // TODO: let's ignore implicit code for now 184 return false; 185 } 186 187 private: 188 // Sets 'Matched' to true if 'Matcher' matches 'Node' 189 // 190 // Returns 'true' if traversal should continue after this function 191 // returns, i.e. if no match is found or 'Bind' is 'BK_All'. 192 template <typename T> bool match(const T &Node) { 193 internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder); 194 195 if (Matcher->matches(DynTypedNode::create(Node), Finder, 196 &RecursiveBuilder)) { 197 ResultBindings.addMatch(RecursiveBuilder); 198 Matches = true; 199 if (Bind != internal::ASTMatchFinder::BK_All) 200 return false; // Abort as soon as a match is found. 201 } 202 return true; 203 } 204 205 const internal::DynTypedMatcher *const Matcher; 206 internal::ASTMatchFinder *const Finder; 207 internal::BoundNodesTreeBuilder *const Builder; 208 internal::BoundNodesTreeBuilder ResultBindings; 209 const internal::ASTMatchFinder::BindKind Bind; 210 bool Matches; 211 bool ignoreUnevaluatedContext; 212 }; 213 214 // Because we're dealing with raw pointers, let's define what we mean by that. 215 static auto hasPointerType() { 216 return hasType(hasCanonicalType(pointerType())); 217 } 218 219 static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); } 220 221 AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>, 222 innerMatcher) { 223 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 224 225 MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, 226 true); 227 return Visitor.findMatch(DynTypedNode::create(Node)); 228 } 229 230 AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>, 231 innerMatcher) { 232 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher); 233 234 MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, 235 false); 236 return Visitor.findMatch(DynTypedNode::create(Node)); 237 } 238 239 // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region 240 AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *, 241 Handler) { 242 return !Handler->isSafeBufferOptOut(Node.getBeginLoc()); 243 } 244 245 AST_MATCHER_P(Stmt, ignoreUnsafeBufferInContainer, 246 const UnsafeBufferUsageHandler *, Handler) { 247 return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc()); 248 } 249 250 AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) { 251 return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder); 252 } 253 254 // Matches a `UnaryOperator` whose operator is pre-increment: 255 AST_MATCHER(UnaryOperator, isPreInc) { 256 return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc; 257 } 258 259 // Returns a matcher that matches any expression 'e' such that `innerMatcher` 260 // matches 'e' and 'e' is in an Unspecified Lvalue Context. 261 static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) { 262 // clang-format off 263 return 264 expr(anyOf( 265 implicitCastExpr( 266 hasCastKind(CastKind::CK_LValueToRValue), 267 castSubExpr(innerMatcher)), 268 binaryOperator( 269 hasAnyOperatorName("="), 270 hasLHS(innerMatcher) 271 ) 272 )); 273 // clang-format on 274 } 275 276 // Returns a matcher that matches any expression `e` such that `InnerMatcher` 277 // matches `e` and `e` is in an Unspecified Pointer Context (UPC). 278 static internal::Matcher<Stmt> 279 isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) { 280 // A UPC can be 281 // 1. an argument of a function call (except the callee has [[unsafe_...]] 282 // attribute), or 283 // 2. the operand of a pointer-to-(integer or bool) cast operation; or 284 // 3. the operand of a comparator operation; or 285 // 4. the operand of a pointer subtraction operation 286 // (i.e., computing the distance between two pointers); or ... 287 288 // clang-format off 289 auto CallArgMatcher = callExpr( 290 forEachArgumentWithParamType( 291 InnerMatcher, 292 isAnyPointer() /* array also decays to pointer type*/), 293 unless(callee( 294 functionDecl(hasAttr(attr::UnsafeBufferUsage))))); 295 296 auto CastOperandMatcher = 297 castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral), 298 hasCastKind(CastKind::CK_PointerToBoolean)), 299 castSubExpr(allOf(hasPointerType(), InnerMatcher))); 300 301 auto CompOperandMatcher = 302 binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="), 303 eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)), 304 hasRHS(allOf(hasPointerType(), InnerMatcher)))); 305 306 // A matcher that matches pointer subtractions: 307 auto PtrSubtractionMatcher = 308 binaryOperator(hasOperatorName("-"), 309 // Note that here we need both LHS and RHS to be 310 // pointer. Then the inner matcher can match any of 311 // them: 312 allOf(hasLHS(hasPointerType()), 313 hasRHS(hasPointerType())), 314 eachOf(hasLHS(InnerMatcher), 315 hasRHS(InnerMatcher))); 316 // clang-format on 317 318 return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher, 319 PtrSubtractionMatcher)); 320 // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we 321 // don't have to check that.) 322 } 323 324 // Returns a matcher that matches any expression 'e' such that `innerMatcher` 325 // matches 'e' and 'e' is in an unspecified untyped context (i.e the expression 326 // 'e' isn't evaluated to an RValue). For example, consider the following code: 327 // int *p = new int[4]; 328 // int *q = new int[4]; 329 // if ((p = q)) {} 330 // p = q; 331 // The expression `p = q` in the conditional of the `if` statement 332 // `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;` 333 // in the assignment statement is in an untyped context. 334 static internal::Matcher<Stmt> 335 isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) { 336 // An unspecified context can be 337 // 1. A compound statement, 338 // 2. The body of an if statement 339 // 3. Body of a loop 340 auto CompStmt = compoundStmt(forEach(InnerMatcher)); 341 auto IfStmtThen = ifStmt(hasThen(InnerMatcher)); 342 auto IfStmtElse = ifStmt(hasElse(InnerMatcher)); 343 // FIXME: Handle loop bodies. 344 return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse)); 345 } 346 347 // Given a two-param std::span construct call, matches iff the call has the 348 // following forms: 349 // 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE 350 // 2. `std::span<T>{new T, 1}` 351 // 3. `std::span<T>{&var, 1}` 352 // 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size 353 // `n` 354 // 5. `std::span<T>{any, 0}` 355 AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { 356 assert(Node.getNumArgs() == 2 && 357 "expecting a two-parameter std::span constructor"); 358 const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit(); 359 const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit(); 360 auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) { 361 if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext())) 362 if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) { 363 return APSInt::compareValues(*E0CV, *E1CV) == 0; 364 } 365 return false; 366 }; 367 auto AreSameDRE = [](const Expr *E0, const Expr *E1) { 368 if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0)) 369 if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) { 370 return DRE0->getDecl() == DRE1->getDecl(); 371 } 372 return false; 373 }; 374 std::optional<APSInt> Arg1CV = 375 Arg1->getIntegerConstantExpr(Finder->getASTContext()); 376 377 if (Arg1CV && Arg1CV->isZero()) 378 // Check form 5: 379 return true; 380 switch (Arg0->IgnoreImplicit()->getStmtClass()) { 381 case Stmt::CXXNewExprClass: 382 if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) { 383 // Check form 1: 384 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) || 385 HaveEqualConstantValues(*Size, Arg1); 386 } 387 // TODO: what's placeholder type? avoid it for now. 388 if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) { 389 // Check form 2: 390 return Arg1CV && Arg1CV->isOne(); 391 } 392 break; 393 case Stmt::UnaryOperatorClass: 394 if (cast<UnaryOperator>(Arg0)->getOpcode() == 395 UnaryOperator::Opcode::UO_AddrOf) 396 // Check form 3: 397 return Arg1CV && Arg1CV->isOne(); 398 break; 399 default: 400 break; 401 } 402 403 QualType Arg0Ty = Arg0->IgnoreImplicit()->getType(); 404 405 if (Arg0Ty->isConstantArrayType()) { 406 const APSInt ConstArrSize = 407 APSInt(cast<ConstantArrayType>(Arg0Ty)->getSize()); 408 409 // Check form 4: 410 return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0; 411 } 412 return false; 413 } 414 415 AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) { 416 // FIXME: Proper solution: 417 // - refactor Sema::CheckArrayAccess 418 // - split safe/OOB/unknown decision logic from diagnostics emitting code 419 // - e. g. "Try harder to find a NamedDecl to point at in the note." 420 // already duplicated 421 // - call both from Sema and from here 422 423 const auto *BaseDRE = 424 dyn_cast<DeclRefExpr>(Node.getBase()->IgnoreParenImpCasts()); 425 if (!BaseDRE) 426 return false; 427 if (!BaseDRE->getDecl()) 428 return false; 429 const auto *CATy = Finder->getASTContext().getAsConstantArrayType( 430 BaseDRE->getDecl()->getType()); 431 if (!CATy) 432 return false; 433 434 if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) { 435 const APInt ArrIdx = IdxLit->getValue(); 436 // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a 437 // bug 438 if (ArrIdx.isNonNegative() && 439 ArrIdx.getLimitedValue() < CATy->getLimitedSize()) 440 return true; 441 } 442 443 return false; 444 } 445 446 } // namespace clang::ast_matchers 447 448 namespace { 449 // Because the analysis revolves around variables and their types, we'll need to 450 // track uses of variables (aka DeclRefExprs). 451 using DeclUseList = SmallVector<const DeclRefExpr *, 1>; 452 453 // Convenience typedef. 454 using FixItList = SmallVector<FixItHint, 4>; 455 } // namespace 456 457 namespace { 458 /// Gadget is an individual operation in the code that may be of interest to 459 /// this analysis. Each (non-abstract) subclass corresponds to a specific 460 /// rigid AST structure that constitutes an operation on a pointer-type object. 461 /// Discovery of a gadget in the code corresponds to claiming that we understand 462 /// what this part of code is doing well enough to potentially improve it. 463 /// Gadgets can be warning (immediately deserving a warning) or fixable (not 464 /// always deserving a warning per se, but requires our attention to identify 465 /// it warrants a fixit). 466 class Gadget { 467 public: 468 enum class Kind { 469 #define GADGET(x) x, 470 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 471 }; 472 473 /// Common type of ASTMatchers used for discovering gadgets. 474 /// Useful for implementing the static matcher() methods 475 /// that are expected from all non-abstract subclasses. 476 using Matcher = decltype(stmt()); 477 478 Gadget(Kind K) : K(K) {} 479 480 Kind getKind() const { return K; } 481 482 #ifndef NDEBUG 483 StringRef getDebugName() const { 484 switch (K) { 485 #define GADGET(x) \ 486 case Kind::x: \ 487 return #x; 488 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 489 } 490 llvm_unreachable("Unhandled Gadget::Kind enum"); 491 } 492 #endif 493 494 virtual bool isWarningGadget() const = 0; 495 // TODO remove this method from WarningGadget interface. It's only used for 496 // debug prints in FixableGadget. 497 virtual SourceLocation getSourceLoc() const = 0; 498 499 /// Returns the list of pointer-type variables on which this gadget performs 500 /// its operation. Typically, there's only one variable. This isn't a list 501 /// of all DeclRefExprs in the gadget's AST! 502 virtual DeclUseList getClaimedVarUseSites() const = 0; 503 504 virtual ~Gadget() = default; 505 506 private: 507 Kind K; 508 }; 509 510 /// Warning gadgets correspond to unsafe code patterns that warrants 511 /// an immediate warning. 512 class WarningGadget : public Gadget { 513 public: 514 WarningGadget(Kind K) : Gadget(K) {} 515 516 static bool classof(const Gadget *G) { return G->isWarningGadget(); } 517 bool isWarningGadget() const final { return true; } 518 519 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 520 bool IsRelatedToDecl, 521 ASTContext &Ctx) const = 0; 522 }; 523 524 /// Fixable gadgets correspond to code patterns that aren't always unsafe but 525 /// need to be properly recognized in order to emit fixes. For example, if a raw 526 /// pointer-type variable is replaced by a safe C++ container, every use of such 527 /// variable must be carefully considered and possibly updated. 528 class FixableGadget : public Gadget { 529 public: 530 FixableGadget(Kind K) : Gadget(K) {} 531 532 static bool classof(const Gadget *G) { return !G->isWarningGadget(); } 533 bool isWarningGadget() const final { return false; } 534 535 /// Returns a fixit that would fix the current gadget according to 536 /// the current strategy. Returns std::nullopt if the fix cannot be produced; 537 /// returns an empty list if no fixes are necessary. 538 virtual std::optional<FixItList> getFixits(const FixitStrategy &) const { 539 return std::nullopt; 540 } 541 542 /// Returns a list of two elements where the first element is the LHS of a 543 /// pointer assignment statement and the second element is the RHS. This 544 /// two-element list represents the fact that the LHS buffer gets its bounds 545 /// information from the RHS buffer. This information will be used later to 546 /// group all those variables whose types must be modified together to prevent 547 /// type mismatches. 548 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 549 getStrategyImplications() const { 550 return std::nullopt; 551 } 552 }; 553 554 static auto toSupportedVariable() { return to(varDecl()); } 555 556 using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>; 557 using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>; 558 559 /// An increment of a pointer-type value is unsafe as it may run the pointer 560 /// out of bounds. 561 class IncrementGadget : public WarningGadget { 562 static constexpr const char *const OpTag = "op"; 563 const UnaryOperator *Op; 564 565 public: 566 IncrementGadget(const MatchFinder::MatchResult &Result) 567 : WarningGadget(Kind::Increment), 568 Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 569 570 static bool classof(const Gadget *G) { 571 return G->getKind() == Kind::Increment; 572 } 573 574 static Matcher matcher() { 575 return stmt( 576 unaryOperator(hasOperatorName("++"), 577 hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))) 578 .bind(OpTag)); 579 } 580 581 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 582 bool IsRelatedToDecl, 583 ASTContext &Ctx) const override { 584 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 585 } 586 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 587 588 DeclUseList getClaimedVarUseSites() const override { 589 SmallVector<const DeclRefExpr *, 2> Uses; 590 if (const auto *DRE = 591 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 592 Uses.push_back(DRE); 593 } 594 595 return std::move(Uses); 596 } 597 }; 598 599 /// A decrement of a pointer-type value is unsafe as it may run the pointer 600 /// out of bounds. 601 class DecrementGadget : public WarningGadget { 602 static constexpr const char *const OpTag = "op"; 603 const UnaryOperator *Op; 604 605 public: 606 DecrementGadget(const MatchFinder::MatchResult &Result) 607 : WarningGadget(Kind::Decrement), 608 Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {} 609 610 static bool classof(const Gadget *G) { 611 return G->getKind() == Kind::Decrement; 612 } 613 614 static Matcher matcher() { 615 return stmt( 616 unaryOperator(hasOperatorName("--"), 617 hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))) 618 .bind(OpTag)); 619 } 620 621 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 622 bool IsRelatedToDecl, 623 ASTContext &Ctx) const override { 624 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 625 } 626 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 627 628 DeclUseList getClaimedVarUseSites() const override { 629 if (const auto *DRE = 630 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) { 631 return {DRE}; 632 } 633 634 return {}; 635 } 636 }; 637 638 /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as 639 /// it doesn't have any bounds checks for the array. 640 class ArraySubscriptGadget : public WarningGadget { 641 static constexpr const char *const ArraySubscrTag = "ArraySubscript"; 642 const ArraySubscriptExpr *ASE; 643 644 public: 645 ArraySubscriptGadget(const MatchFinder::MatchResult &Result) 646 : WarningGadget(Kind::ArraySubscript), 647 ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {} 648 649 static bool classof(const Gadget *G) { 650 return G->getKind() == Kind::ArraySubscript; 651 } 652 653 static Matcher matcher() { 654 // clang-format off 655 return stmt(arraySubscriptExpr( 656 hasBase(ignoringParenImpCasts( 657 anyOf(hasPointerType(), hasArrayType()))), 658 unless(anyOf( 659 isSafeArraySubscript(), 660 hasIndex( 661 anyOf(integerLiteral(equals(0)), arrayInitIndexExpr()) 662 ) 663 ))).bind(ArraySubscrTag)); 664 // clang-format on 665 } 666 667 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 668 bool IsRelatedToDecl, 669 ASTContext &Ctx) const override { 670 Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx); 671 } 672 SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); } 673 674 DeclUseList getClaimedVarUseSites() const override { 675 if (const auto *DRE = 676 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) { 677 return {DRE}; 678 } 679 680 return {}; 681 } 682 }; 683 684 /// A pointer arithmetic expression of one of the forms: 685 /// \code 686 /// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n 687 /// \endcode 688 class PointerArithmeticGadget : public WarningGadget { 689 static constexpr const char *const PointerArithmeticTag = "ptrAdd"; 690 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr"; 691 const BinaryOperator *PA; // pointer arithmetic expression 692 const Expr *Ptr; // the pointer expression in `PA` 693 694 public: 695 PointerArithmeticGadget(const MatchFinder::MatchResult &Result) 696 : WarningGadget(Kind::PointerArithmetic), 697 PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)), 698 Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {} 699 700 static bool classof(const Gadget *G) { 701 return G->getKind() == Kind::PointerArithmetic; 702 } 703 704 static Matcher matcher() { 705 auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType())); 706 auto PtrAtRight = 707 allOf(hasOperatorName("+"), 708 hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 709 hasLHS(HasIntegerType)); 710 auto PtrAtLeft = 711 allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"), 712 hasOperatorName("+="), hasOperatorName("-=")), 713 hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)), 714 hasRHS(HasIntegerType)); 715 716 return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight)) 717 .bind(PointerArithmeticTag)); 718 } 719 720 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 721 bool IsRelatedToDecl, 722 ASTContext &Ctx) const override { 723 Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx); 724 } 725 SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); } 726 727 DeclUseList getClaimedVarUseSites() const override { 728 if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) { 729 return {DRE}; 730 } 731 732 return {}; 733 } 734 // FIXME: pointer adding zero should be fine 735 // FIXME: this gadge will need a fix-it 736 }; 737 738 class SpanTwoParamConstructorGadget : public WarningGadget { 739 static constexpr const char *const SpanTwoParamConstructorTag = 740 "spanTwoParamConstructor"; 741 const CXXConstructExpr *Ctor; // the span constructor expression 742 743 public: 744 SpanTwoParamConstructorGadget(const MatchFinder::MatchResult &Result) 745 : WarningGadget(Kind::SpanTwoParamConstructor), 746 Ctor(Result.Nodes.getNodeAs<CXXConstructExpr>( 747 SpanTwoParamConstructorTag)) {} 748 749 static bool classof(const Gadget *G) { 750 return G->getKind() == Kind::SpanTwoParamConstructor; 751 } 752 753 static Matcher matcher() { 754 auto HasTwoParamSpanCtorDecl = hasDeclaration( 755 cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"), 756 parameterCountIs(2))); 757 758 return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl, 759 unless(isSafeSpanTwoParamConstruct())) 760 .bind(SpanTwoParamConstructorTag)); 761 } 762 763 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 764 bool IsRelatedToDecl, 765 ASTContext &Ctx) const override { 766 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx); 767 } 768 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); } 769 770 DeclUseList getClaimedVarUseSites() const override { 771 // If the constructor call is of the form `std::span{var, n}`, `var` is 772 // considered an unsafe variable. 773 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) { 774 if (isa<VarDecl>(DRE->getDecl())) 775 return {DRE}; 776 } 777 return {}; 778 } 779 }; 780 781 /// A pointer initialization expression of the form: 782 /// \code 783 /// int *p = q; 784 /// \endcode 785 class PointerInitGadget : public FixableGadget { 786 private: 787 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS"; 788 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS"; 789 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI` 790 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI` 791 792 public: 793 PointerInitGadget(const MatchFinder::MatchResult &Result) 794 : FixableGadget(Kind::PointerInit), 795 PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)), 796 PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {} 797 798 static bool classof(const Gadget *G) { 799 return G->getKind() == Kind::PointerInit; 800 } 801 802 static Matcher matcher() { 803 auto PtrInitStmt = declStmt(hasSingleDecl( 804 varDecl(hasInitializer(ignoringImpCasts( 805 declRefExpr(hasPointerType(), toSupportedVariable()) 806 .bind(PointerInitRHSTag)))) 807 .bind(PointerInitLHSTag))); 808 809 return stmt(PtrInitStmt); 810 } 811 812 virtual std::optional<FixItList> 813 getFixits(const FixitStrategy &S) const override; 814 SourceLocation getSourceLoc() const override { 815 return PtrInitRHS->getBeginLoc(); 816 } 817 818 virtual DeclUseList getClaimedVarUseSites() const override { 819 return DeclUseList{PtrInitRHS}; 820 } 821 822 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 823 getStrategyImplications() const override { 824 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl())); 825 } 826 }; 827 828 /// A pointer assignment expression of the form: 829 /// \code 830 /// p = q; 831 /// \endcode 832 /// where both `p` and `q` are pointers. 833 class PtrToPtrAssignmentGadget : public FixableGadget { 834 private: 835 static constexpr const char *const PointerAssignLHSTag = "ptrLHS"; 836 static constexpr const char *const PointerAssignRHSTag = "ptrRHS"; 837 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA` 838 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA` 839 840 public: 841 PtrToPtrAssignmentGadget(const MatchFinder::MatchResult &Result) 842 : FixableGadget(Kind::PtrToPtrAssignment), 843 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)), 844 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {} 845 846 static bool classof(const Gadget *G) { 847 return G->getKind() == Kind::PtrToPtrAssignment; 848 } 849 850 static Matcher matcher() { 851 auto PtrAssignExpr = binaryOperator( 852 allOf(hasOperatorName("="), 853 hasRHS(ignoringParenImpCasts( 854 declRefExpr(hasPointerType(), toSupportedVariable()) 855 .bind(PointerAssignRHSTag))), 856 hasLHS(declRefExpr(hasPointerType(), toSupportedVariable()) 857 .bind(PointerAssignLHSTag)))); 858 859 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr)); 860 } 861 862 virtual std::optional<FixItList> 863 getFixits(const FixitStrategy &S) const override; 864 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); } 865 866 virtual DeclUseList getClaimedVarUseSites() const override { 867 return DeclUseList{PtrLHS, PtrRHS}; 868 } 869 870 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 871 getStrategyImplications() const override { 872 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()), 873 cast<VarDecl>(PtrRHS->getDecl())); 874 } 875 }; 876 877 /// An assignment expression of the form: 878 /// \code 879 /// ptr = array; 880 /// \endcode 881 /// where `p` is a pointer and `array` is a constant size array. 882 class CArrayToPtrAssignmentGadget : public FixableGadget { 883 private: 884 static constexpr const char *const PointerAssignLHSTag = "ptrLHS"; 885 static constexpr const char *const PointerAssignRHSTag = "ptrRHS"; 886 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA` 887 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA` 888 889 public: 890 CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult &Result) 891 : FixableGadget(Kind::CArrayToPtrAssignment), 892 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)), 893 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {} 894 895 static bool classof(const Gadget *G) { 896 return G->getKind() == Kind::CArrayToPtrAssignment; 897 } 898 899 static Matcher matcher() { 900 auto PtrAssignExpr = binaryOperator( 901 allOf(hasOperatorName("="), 902 hasRHS(ignoringParenImpCasts( 903 declRefExpr(hasType(hasCanonicalType(constantArrayType())), 904 toSupportedVariable()) 905 .bind(PointerAssignRHSTag))), 906 hasLHS(declRefExpr(hasPointerType(), toSupportedVariable()) 907 .bind(PointerAssignLHSTag)))); 908 909 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr)); 910 } 911 912 virtual std::optional<FixItList> 913 getFixits(const FixitStrategy &S) const override; 914 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); } 915 916 virtual DeclUseList getClaimedVarUseSites() const override { 917 return DeclUseList{PtrLHS, PtrRHS}; 918 } 919 920 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>> 921 getStrategyImplications() const override { 922 return {}; 923 } 924 }; 925 926 /// A call of a function or method that performs unchecked buffer operations 927 /// over one of its pointer parameters. 928 class UnsafeBufferUsageAttrGadget : public WarningGadget { 929 constexpr static const char *const OpTag = "call_expr"; 930 const CallExpr *Op; 931 932 public: 933 UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result) 934 : WarningGadget(Kind::UnsafeBufferUsageAttr), 935 Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {} 936 937 static bool classof(const Gadget *G) { 938 return G->getKind() == Kind::UnsafeBufferUsageAttr; 939 } 940 941 static Matcher matcher() { 942 auto HasUnsafeFnDecl = 943 callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))); 944 return stmt(callExpr(HasUnsafeFnDecl).bind(OpTag)); 945 } 946 947 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 948 bool IsRelatedToDecl, 949 ASTContext &Ctx) const override { 950 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 951 } 952 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 953 954 DeclUseList getClaimedVarUseSites() const override { return {}; } 955 }; 956 957 /// A call of a constructor that performs unchecked buffer operations 958 /// over one of its pointer parameters, or constructs a class object that will 959 /// perform buffer operations that depend on the correctness of the parameters. 960 class UnsafeBufferUsageCtorAttrGadget : public WarningGadget { 961 constexpr static const char *const OpTag = "cxx_construct_expr"; 962 const CXXConstructExpr *Op; 963 964 public: 965 UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult &Result) 966 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr), 967 Op(Result.Nodes.getNodeAs<CXXConstructExpr>(OpTag)) {} 968 969 static bool classof(const Gadget *G) { 970 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr; 971 } 972 973 static Matcher matcher() { 974 auto HasUnsafeCtorDecl = 975 hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage))); 976 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget. 977 auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher(); 978 return stmt( 979 cxxConstructExpr(HasUnsafeCtorDecl, unless(HasTwoParamSpanCtorDecl)) 980 .bind(OpTag)); 981 } 982 983 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 984 bool IsRelatedToDecl, 985 ASTContext &Ctx) const override { 986 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 987 } 988 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 989 990 DeclUseList getClaimedVarUseSites() const override { return {}; } 991 }; 992 993 // Warning gadget for unsafe invocation of span::data method. 994 // Triggers when the pointer returned by the invocation is immediately 995 // cast to a larger type. 996 997 class DataInvocationGadget : public WarningGadget { 998 constexpr static const char *const OpTag = "data_invocation_expr"; 999 const ExplicitCastExpr *Op; 1000 1001 public: 1002 DataInvocationGadget(const MatchFinder::MatchResult &Result) 1003 : WarningGadget(Kind::DataInvocation), 1004 Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {} 1005 1006 static bool classof(const Gadget *G) { 1007 return G->getKind() == Kind::DataInvocation; 1008 } 1009 1010 static Matcher matcher() { 1011 Matcher callExpr = cxxMemberCallExpr( 1012 callee(cxxMethodDecl(hasName("data"), ofClass(hasName("std::span"))))); 1013 return stmt( 1014 explicitCastExpr(anyOf(has(callExpr), has(parenExpr(has(callExpr))))) 1015 .bind(OpTag)); 1016 } 1017 1018 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, 1019 bool IsRelatedToDecl, 1020 ASTContext &Ctx) const override { 1021 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx); 1022 } 1023 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 1024 1025 DeclUseList getClaimedVarUseSites() const override { return {}; } 1026 }; 1027 1028 // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue 1029 // Context (see `isInUnspecifiedLvalueContext`). 1030 // Note here `[]` is the built-in subscript operator. 1031 class ULCArraySubscriptGadget : public FixableGadget { 1032 private: 1033 static constexpr const char *const ULCArraySubscriptTag = 1034 "ArraySubscriptUnderULC"; 1035 const ArraySubscriptExpr *Node; 1036 1037 public: 1038 ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result) 1039 : FixableGadget(Kind::ULCArraySubscript), 1040 Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) { 1041 assert(Node != nullptr && "Expecting a non-null matching result"); 1042 } 1043 1044 static bool classof(const Gadget *G) { 1045 return G->getKind() == Kind::ULCArraySubscript; 1046 } 1047 1048 static Matcher matcher() { 1049 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 1050 auto BaseIsArrayOrPtrDRE = hasBase( 1051 ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable()))); 1052 auto Target = 1053 arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag); 1054 1055 return expr(isInUnspecifiedLvalueContext(Target)); 1056 } 1057 1058 virtual std::optional<FixItList> 1059 getFixits(const FixitStrategy &S) const override; 1060 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 1061 1062 virtual DeclUseList getClaimedVarUseSites() const override { 1063 if (const auto *DRE = 1064 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) { 1065 return {DRE}; 1066 } 1067 return {}; 1068 } 1069 }; 1070 1071 // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the 1072 // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits 1073 // fixit of the form `UPC(DRE.data())`. 1074 class UPCStandalonePointerGadget : public FixableGadget { 1075 private: 1076 static constexpr const char *const DeclRefExprTag = "StandalonePointer"; 1077 const DeclRefExpr *Node; 1078 1079 public: 1080 UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result) 1081 : FixableGadget(Kind::UPCStandalonePointer), 1082 Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) { 1083 assert(Node != nullptr && "Expecting a non-null matching result"); 1084 } 1085 1086 static bool classof(const Gadget *G) { 1087 return G->getKind() == Kind::UPCStandalonePointer; 1088 } 1089 1090 static Matcher matcher() { 1091 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType()); 1092 auto target = expr(ignoringParenImpCasts( 1093 declRefExpr(allOf(ArrayOrPtr, toSupportedVariable())) 1094 .bind(DeclRefExprTag))); 1095 return stmt(isInUnspecifiedPointerContext(target)); 1096 } 1097 1098 virtual std::optional<FixItList> 1099 getFixits(const FixitStrategy &S) const override; 1100 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 1101 1102 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; } 1103 }; 1104 1105 class PointerDereferenceGadget : public FixableGadget { 1106 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 1107 static constexpr const char *const OperatorTag = "op"; 1108 1109 const DeclRefExpr *BaseDeclRefExpr = nullptr; 1110 const UnaryOperator *Op = nullptr; 1111 1112 public: 1113 PointerDereferenceGadget(const MatchFinder::MatchResult &Result) 1114 : FixableGadget(Kind::PointerDereference), 1115 BaseDeclRefExpr( 1116 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 1117 Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {} 1118 1119 static bool classof(const Gadget *G) { 1120 return G->getKind() == Kind::PointerDereference; 1121 } 1122 1123 static Matcher matcher() { 1124 auto Target = 1125 unaryOperator( 1126 hasOperatorName("*"), 1127 has(expr(ignoringParenImpCasts( 1128 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag))))) 1129 .bind(OperatorTag); 1130 1131 return expr(isInUnspecifiedLvalueContext(Target)); 1132 } 1133 1134 DeclUseList getClaimedVarUseSites() const override { 1135 return {BaseDeclRefExpr}; 1136 } 1137 1138 virtual std::optional<FixItList> 1139 getFixits(const FixitStrategy &S) const override; 1140 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); } 1141 }; 1142 1143 // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer 1144 // Context (see `isInUnspecifiedPointerContext`). 1145 // Note here `[]` is the built-in subscript operator. 1146 class UPCAddressofArraySubscriptGadget : public FixableGadget { 1147 private: 1148 static constexpr const char *const UPCAddressofArraySubscriptTag = 1149 "AddressofArraySubscriptUnderUPC"; 1150 const UnaryOperator *Node; // the `&DRE[any]` node 1151 1152 public: 1153 UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result) 1154 : FixableGadget(Kind::ULCArraySubscript), 1155 Node(Result.Nodes.getNodeAs<UnaryOperator>( 1156 UPCAddressofArraySubscriptTag)) { 1157 assert(Node != nullptr && "Expecting a non-null matching result"); 1158 } 1159 1160 static bool classof(const Gadget *G) { 1161 return G->getKind() == Kind::UPCAddressofArraySubscript; 1162 } 1163 1164 static Matcher matcher() { 1165 return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 1166 unaryOperator( 1167 hasOperatorName("&"), 1168 hasUnaryOperand(arraySubscriptExpr(hasBase( 1169 ignoringParenImpCasts(declRefExpr(toSupportedVariable())))))) 1170 .bind(UPCAddressofArraySubscriptTag))))); 1171 } 1172 1173 virtual std::optional<FixItList> 1174 getFixits(const FixitStrategy &) const override; 1175 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 1176 1177 virtual DeclUseList getClaimedVarUseSites() const override { 1178 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr()); 1179 const auto *DRE = 1180 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts()); 1181 return {DRE}; 1182 } 1183 }; 1184 } // namespace 1185 1186 namespace { 1187 // An auxiliary tracking facility for the fixit analysis. It helps connect 1188 // declarations to its uses and make sure we've covered all uses with our 1189 // analysis before we try to fix the declaration. 1190 class DeclUseTracker { 1191 using UseSetTy = SmallSet<const DeclRefExpr *, 16>; 1192 using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>; 1193 1194 // Allocate on the heap for easier move. 1195 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()}; 1196 DefMapTy Defs{}; 1197 1198 public: 1199 DeclUseTracker() = default; 1200 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies. 1201 DeclUseTracker &operator=(const DeclUseTracker &) = delete; 1202 DeclUseTracker(DeclUseTracker &&) = default; 1203 DeclUseTracker &operator=(DeclUseTracker &&) = default; 1204 1205 // Start tracking a freshly discovered DRE. 1206 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); } 1207 1208 // Stop tracking the DRE as it's been fully figured out. 1209 void claimUse(const DeclRefExpr *DRE) { 1210 assert(Uses->count(DRE) && 1211 "DRE not found or claimed by multiple matchers!"); 1212 Uses->erase(DRE); 1213 } 1214 1215 // A variable is unclaimed if at least one use is unclaimed. 1216 bool hasUnclaimedUses(const VarDecl *VD) const { 1217 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs? 1218 return any_of(*Uses, [VD](const DeclRefExpr *DRE) { 1219 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl(); 1220 }); 1221 } 1222 1223 UseSetTy getUnclaimedUses(const VarDecl *VD) const { 1224 UseSetTy ReturnSet; 1225 for (auto use : *Uses) { 1226 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) { 1227 ReturnSet.insert(use); 1228 } 1229 } 1230 return ReturnSet; 1231 } 1232 1233 void discoverDecl(const DeclStmt *DS) { 1234 for (const Decl *D : DS->decls()) { 1235 if (const auto *VD = dyn_cast<VarDecl>(D)) { 1236 // FIXME: Assertion temporarily disabled due to a bug in 1237 // ASTMatcher internal behavior in presence of GNU 1238 // statement-expressions. We need to properly investigate this 1239 // because it can screw up our algorithm in other ways. 1240 // assert(Defs.count(VD) == 0 && "Definition already discovered!"); 1241 Defs[VD] = DS; 1242 } 1243 } 1244 } 1245 1246 const DeclStmt *lookupDecl(const VarDecl *VD) const { 1247 return Defs.lookup(VD); 1248 } 1249 }; 1250 } // namespace 1251 1252 // Representing a pointer type expression of the form `++Ptr` in an Unspecified 1253 // Pointer Context (UPC): 1254 class UPCPreIncrementGadget : public FixableGadget { 1255 private: 1256 static constexpr const char *const UPCPreIncrementTag = 1257 "PointerPreIncrementUnderUPC"; 1258 const UnaryOperator *Node; // the `++Ptr` node 1259 1260 public: 1261 UPCPreIncrementGadget(const MatchFinder::MatchResult &Result) 1262 : FixableGadget(Kind::UPCPreIncrement), 1263 Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) { 1264 assert(Node != nullptr && "Expecting a non-null matching result"); 1265 } 1266 1267 static bool classof(const Gadget *G) { 1268 return G->getKind() == Kind::UPCPreIncrement; 1269 } 1270 1271 static Matcher matcher() { 1272 // Note here we match `++Ptr` for any expression `Ptr` of pointer type. 1273 // Although currently we can only provide fix-its when `Ptr` is a DRE, we 1274 // can have the matcher be general, so long as `getClaimedVarUseSites` does 1275 // things right. 1276 return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts( 1277 unaryOperator(isPreInc(), 1278 hasUnaryOperand(declRefExpr(toSupportedVariable()))) 1279 .bind(UPCPreIncrementTag))))); 1280 } 1281 1282 virtual std::optional<FixItList> 1283 getFixits(const FixitStrategy &S) const override; 1284 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 1285 1286 virtual DeclUseList getClaimedVarUseSites() const override { 1287 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())}; 1288 } 1289 }; 1290 1291 // Representing a pointer type expression of the form `Ptr += n` in an 1292 // Unspecified Untyped Context (UUC): 1293 class UUCAddAssignGadget : public FixableGadget { 1294 private: 1295 static constexpr const char *const UUCAddAssignTag = 1296 "PointerAddAssignUnderUUC"; 1297 static constexpr const char *const OffsetTag = "Offset"; 1298 1299 const BinaryOperator *Node; // the `Ptr += n` node 1300 const Expr *Offset = nullptr; 1301 1302 public: 1303 UUCAddAssignGadget(const MatchFinder::MatchResult &Result) 1304 : FixableGadget(Kind::UUCAddAssign), 1305 Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)), 1306 Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) { 1307 assert(Node != nullptr && "Expecting a non-null matching result"); 1308 } 1309 1310 static bool classof(const Gadget *G) { 1311 return G->getKind() == Kind::UUCAddAssign; 1312 } 1313 1314 static Matcher matcher() { 1315 // clang-format off 1316 return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts( 1317 binaryOperator(hasOperatorName("+="), 1318 hasLHS( 1319 declRefExpr( 1320 hasPointerType(), 1321 toSupportedVariable())), 1322 hasRHS(expr().bind(OffsetTag))) 1323 .bind(UUCAddAssignTag))))); 1324 // clang-format on 1325 } 1326 1327 virtual std::optional<FixItList> 1328 getFixits(const FixitStrategy &S) const override; 1329 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); } 1330 1331 virtual DeclUseList getClaimedVarUseSites() const override { 1332 return {dyn_cast<DeclRefExpr>(Node->getLHS())}; 1333 } 1334 }; 1335 1336 // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 + 1337 // ptr)`: 1338 class DerefSimplePtrArithFixableGadget : public FixableGadget { 1339 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; 1340 static constexpr const char *const DerefOpTag = "DerefOp"; 1341 static constexpr const char *const AddOpTag = "AddOp"; 1342 static constexpr const char *const OffsetTag = "Offset"; 1343 1344 const DeclRefExpr *BaseDeclRefExpr = nullptr; 1345 const UnaryOperator *DerefOp = nullptr; 1346 const BinaryOperator *AddOp = nullptr; 1347 const IntegerLiteral *Offset = nullptr; 1348 1349 public: 1350 DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result) 1351 : FixableGadget(Kind::DerefSimplePtrArithFixable), 1352 BaseDeclRefExpr( 1353 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)), 1354 DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)), 1355 AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)), 1356 Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {} 1357 1358 static Matcher matcher() { 1359 // clang-format off 1360 auto ThePtr = expr(hasPointerType(), 1361 ignoringImpCasts(declRefExpr(toSupportedVariable()). 1362 bind(BaseDeclRefExprTag))); 1363 auto PlusOverPtrAndInteger = expr(anyOf( 1364 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr), 1365 hasRHS(integerLiteral().bind(OffsetTag))) 1366 .bind(AddOpTag), 1367 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr), 1368 hasLHS(integerLiteral().bind(OffsetTag))) 1369 .bind(AddOpTag))); 1370 return isInUnspecifiedLvalueContext(unaryOperator( 1371 hasOperatorName("*"), 1372 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger))) 1373 .bind(DerefOpTag)); 1374 // clang-format on 1375 } 1376 1377 virtual std::optional<FixItList> 1378 getFixits(const FixitStrategy &s) const final; 1379 SourceLocation getSourceLoc() const override { 1380 return DerefOp->getBeginLoc(); 1381 } 1382 1383 virtual DeclUseList getClaimedVarUseSites() const final { 1384 return {BaseDeclRefExpr}; 1385 } 1386 }; 1387 1388 /// Scan the function and return a list of gadgets found with provided kits. 1389 static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker> 1390 findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler, 1391 bool EmitSuggestions) { 1392 1393 struct GadgetFinderCallback : MatchFinder::MatchCallback { 1394 FixableGadgetList FixableGadgets; 1395 WarningGadgetList WarningGadgets; 1396 DeclUseTracker Tracker; 1397 1398 void run(const MatchFinder::MatchResult &Result) override { 1399 // In debug mode, assert that we've found exactly one gadget. 1400 // This helps us avoid conflicts in .bind() tags. 1401 #if NDEBUG 1402 #define NEXT return 1403 #else 1404 [[maybe_unused]] int numFound = 0; 1405 #define NEXT ++numFound 1406 #endif 1407 1408 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) { 1409 Tracker.discoverUse(DRE); 1410 NEXT; 1411 } 1412 1413 if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) { 1414 Tracker.discoverDecl(DS); 1415 NEXT; 1416 } 1417 1418 // Figure out which matcher we've found, and call the appropriate 1419 // subclass constructor. 1420 // FIXME: Can we do this more logarithmically? 1421 #define FIXABLE_GADGET(name) \ 1422 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1423 FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1424 NEXT; \ 1425 } 1426 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1427 #define WARNING_GADGET(name) \ 1428 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \ 1429 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \ 1430 NEXT; \ 1431 } 1432 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1433 1434 assert(numFound >= 1 && "Gadgets not found in match result!"); 1435 assert(numFound <= 1 && "Conflicting bind tags in gadgets!"); 1436 } 1437 }; 1438 1439 MatchFinder M; 1440 GadgetFinderCallback CB; 1441 1442 // clang-format off 1443 M.addMatcher( 1444 stmt( 1445 forEachDescendantEvaluatedStmt(stmt(anyOf( 1446 // Add Gadget::matcher() for every gadget in the registry. 1447 #define WARNING_GADGET(x) \ 1448 allOf(x ## Gadget::matcher().bind(#x), \ 1449 notInSafeBufferOptOut(&Handler)), 1450 #define WARNING_CONTAINER_GADGET(x) \ 1451 allOf(x ## Gadget::matcher().bind(#x), \ 1452 notInSafeBufferOptOut(&Handler), \ 1453 unless(ignoreUnsafeBufferInContainer(&Handler))), 1454 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1455 // Avoid a hanging comma. 1456 unless(stmt()) 1457 ))) 1458 ), 1459 &CB 1460 ); 1461 // clang-format on 1462 1463 if (EmitSuggestions) { 1464 // clang-format off 1465 M.addMatcher( 1466 stmt( 1467 forEachDescendantStmt(stmt(eachOf( 1468 #define FIXABLE_GADGET(x) \ 1469 x ## Gadget::matcher().bind(#x), 1470 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" 1471 // In parallel, match all DeclRefExprs so that to find out 1472 // whether there are any uncovered by gadgets. 1473 declRefExpr(anyOf(hasPointerType(), hasArrayType()), 1474 to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"), 1475 // Also match DeclStmts because we'll need them when fixing 1476 // their underlying VarDecls that otherwise don't have 1477 // any backreferences to DeclStmts. 1478 declStmt().bind("any_ds") 1479 ))) 1480 ), 1481 &CB 1482 ); 1483 // clang-format on 1484 } 1485 1486 M.match(*D->getBody(), D->getASTContext()); 1487 return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets), 1488 std::move(CB.Tracker)}; 1489 } 1490 1491 // Compares AST nodes by source locations. 1492 template <typename NodeTy> struct CompareNode { 1493 bool operator()(const NodeTy *N1, const NodeTy *N2) const { 1494 return N1->getBeginLoc().getRawEncoding() < 1495 N2->getBeginLoc().getRawEncoding(); 1496 } 1497 }; 1498 1499 struct WarningGadgetSets { 1500 std::map<const VarDecl *, std::set<const WarningGadget *>, 1501 // To keep keys sorted by their locations in the map so that the 1502 // order is deterministic: 1503 CompareNode<VarDecl>> 1504 byVar; 1505 // These Gadgets are not related to pointer variables (e. g. temporaries). 1506 llvm::SmallVector<const WarningGadget *, 16> noVar; 1507 }; 1508 1509 static WarningGadgetSets 1510 groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) { 1511 WarningGadgetSets result; 1512 // If some gadgets cover more than one 1513 // variable, they'll appear more than once in the map. 1514 for (auto &G : AllUnsafeOperations) { 1515 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites(); 1516 1517 bool AssociatedWithVarDecl = false; 1518 for (const DeclRefExpr *DRE : ClaimedVarUseSites) { 1519 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 1520 result.byVar[VD].insert(G.get()); 1521 AssociatedWithVarDecl = true; 1522 } 1523 } 1524 1525 if (!AssociatedWithVarDecl) { 1526 result.noVar.push_back(G.get()); 1527 continue; 1528 } 1529 } 1530 return result; 1531 } 1532 1533 struct FixableGadgetSets { 1534 std::map<const VarDecl *, std::set<const FixableGadget *>, 1535 // To keep keys sorted by their locations in the map so that the 1536 // order is deterministic: 1537 CompareNode<VarDecl>> 1538 byVar; 1539 }; 1540 1541 static FixableGadgetSets 1542 groupFixablesByVar(FixableGadgetList &&AllFixableOperations) { 1543 FixableGadgetSets FixablesForUnsafeVars; 1544 for (auto &F : AllFixableOperations) { 1545 DeclUseList DREs = F->getClaimedVarUseSites(); 1546 1547 for (const DeclRefExpr *DRE : DREs) { 1548 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 1549 FixablesForUnsafeVars.byVar[VD].insert(F.get()); 1550 } 1551 } 1552 } 1553 return FixablesForUnsafeVars; 1554 } 1555 1556 bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts, 1557 const SourceManager &SM) { 1558 // A simple interval overlap detection algorithm. Sorts all ranges by their 1559 // begin location then finds the first overlap in one pass. 1560 std::vector<const FixItHint *> All; // a copy of `FixIts` 1561 1562 for (const FixItHint &H : FixIts) 1563 All.push_back(&H); 1564 std::sort(All.begin(), All.end(), 1565 [&SM](const FixItHint *H1, const FixItHint *H2) { 1566 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(), 1567 H2->RemoveRange.getBegin()); 1568 }); 1569 1570 const FixItHint *CurrHint = nullptr; 1571 1572 for (const FixItHint *Hint : All) { 1573 if (!CurrHint || 1574 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(), 1575 Hint->RemoveRange.getBegin())) { 1576 // Either to initialize `CurrHint` or `CurrHint` does not 1577 // overlap with `Hint`: 1578 CurrHint = Hint; 1579 } else 1580 // In case `Hint` overlaps the `CurrHint`, we found at least one 1581 // conflict: 1582 return true; 1583 } 1584 return false; 1585 } 1586 1587 std::optional<FixItList> 1588 PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const { 1589 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl()); 1590 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl()); 1591 switch (S.lookup(LeftVD)) { 1592 case FixitStrategy::Kind::Span: 1593 if (S.lookup(RightVD) == FixitStrategy::Kind::Span) 1594 return FixItList{}; 1595 return std::nullopt; 1596 case FixitStrategy::Kind::Wontfix: 1597 return std::nullopt; 1598 case FixitStrategy::Kind::Iterator: 1599 case FixitStrategy::Kind::Array: 1600 return std::nullopt; 1601 case FixitStrategy::Kind::Vector: 1602 llvm_unreachable("unsupported strategies for FixableGadgets"); 1603 } 1604 return std::nullopt; 1605 } 1606 1607 /// \returns fixit that adds .data() call after \DRE. 1608 static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx, 1609 const DeclRefExpr *DRE); 1610 1611 std::optional<FixItList> 1612 CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const { 1613 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl()); 1614 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl()); 1615 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is 1616 // non-trivial. 1617 // 1618 // CArrayToPtrAssignmentGadget doesn't have strategy implications because 1619 // constant size array propagates its bounds. Because of that LHS and RHS are 1620 // addressed by two different fixits. 1621 // 1622 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs 1623 // to and can't be generally relied on in multi-variable Fixables! 1624 // 1625 // E. g. If an instance of this gadget is fixing variable on LHS then the 1626 // variable on RHS is fixed by a different fixit and its strategy for LHS 1627 // fixit is as if Wontfix. 1628 // 1629 // The only exception is Wontfix strategy for a given variable as that is 1630 // valid for any fixit produced for the given input source code. 1631 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) { 1632 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) { 1633 return FixItList{}; 1634 } 1635 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) { 1636 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) { 1637 return createDataFixit(RightVD->getASTContext(), PtrRHS); 1638 } 1639 } 1640 return std::nullopt; 1641 } 1642 1643 std::optional<FixItList> 1644 PointerInitGadget::getFixits(const FixitStrategy &S) const { 1645 const auto *LeftVD = PtrInitLHS; 1646 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl()); 1647 switch (S.lookup(LeftVD)) { 1648 case FixitStrategy::Kind::Span: 1649 if (S.lookup(RightVD) == FixitStrategy::Kind::Span) 1650 return FixItList{}; 1651 return std::nullopt; 1652 case FixitStrategy::Kind::Wontfix: 1653 return std::nullopt; 1654 case FixitStrategy::Kind::Iterator: 1655 case FixitStrategy::Kind::Array: 1656 return std::nullopt; 1657 case FixitStrategy::Kind::Vector: 1658 llvm_unreachable("unsupported strategies for FixableGadgets"); 1659 } 1660 return std::nullopt; 1661 } 1662 1663 static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, 1664 const ASTContext &Ctx) { 1665 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) { 1666 if (ConstVal->isNegative()) 1667 return false; 1668 } else if (!Expr->getType()->isUnsignedIntegerType()) 1669 return false; 1670 return true; 1671 } 1672 1673 std::optional<FixItList> 1674 ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const { 1675 if (const auto *DRE = 1676 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) 1677 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 1678 switch (S.lookup(VD)) { 1679 case FixitStrategy::Kind::Span: { 1680 1681 // If the index has a negative constant value, we give up as no valid 1682 // fix-it can be generated: 1683 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in! 1684 VD->getASTContext(); 1685 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx)) 1686 return std::nullopt; 1687 // no-op is a good fix-it, otherwise 1688 return FixItList{}; 1689 } 1690 case FixitStrategy::Kind::Array: 1691 return FixItList{}; 1692 case FixitStrategy::Kind::Wontfix: 1693 case FixitStrategy::Kind::Iterator: 1694 case FixitStrategy::Kind::Vector: 1695 llvm_unreachable("unsupported strategies for FixableGadgets"); 1696 } 1697 } 1698 return std::nullopt; 1699 } 1700 1701 static std::optional<FixItList> // forward declaration 1702 fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node); 1703 1704 std::optional<FixItList> 1705 UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const { 1706 auto DREs = getClaimedVarUseSites(); 1707 const auto *VD = cast<VarDecl>(DREs.front()->getDecl()); 1708 1709 switch (S.lookup(VD)) { 1710 case FixitStrategy::Kind::Span: 1711 return fixUPCAddressofArraySubscriptWithSpan(Node); 1712 case FixitStrategy::Kind::Wontfix: 1713 case FixitStrategy::Kind::Iterator: 1714 case FixitStrategy::Kind::Array: 1715 return std::nullopt; 1716 case FixitStrategy::Kind::Vector: 1717 llvm_unreachable("unsupported strategies for FixableGadgets"); 1718 } 1719 return std::nullopt; // something went wrong, no fix-it 1720 } 1721 1722 // FIXME: this function should be customizable through format 1723 static StringRef getEndOfLine() { 1724 static const char *const EOL = "\n"; 1725 return EOL; 1726 } 1727 1728 // Returns the text indicating that the user needs to provide input there: 1729 std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") { 1730 std::string s = std::string("<# "); 1731 s += HintTextToUser; 1732 s += " #>"; 1733 return s; 1734 } 1735 1736 // Return the source location of the last character of the AST `Node`. 1737 template <typename NodeTy> 1738 static std::optional<SourceLocation> 1739 getEndCharLoc(const NodeTy *Node, const SourceManager &SM, 1740 const LangOptions &LangOpts) { 1741 unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts); 1742 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1); 1743 1744 if (Loc.isValid()) 1745 return Loc; 1746 1747 return std::nullopt; 1748 } 1749 1750 // Return the source location just past the last character of the AST `Node`. 1751 template <typename NodeTy> 1752 static std::optional<SourceLocation> getPastLoc(const NodeTy *Node, 1753 const SourceManager &SM, 1754 const LangOptions &LangOpts) { 1755 SourceLocation Loc = 1756 Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts); 1757 if (Loc.isValid()) 1758 return Loc; 1759 return std::nullopt; 1760 } 1761 1762 // Return text representation of an `Expr`. 1763 static std::optional<StringRef> getExprText(const Expr *E, 1764 const SourceManager &SM, 1765 const LangOptions &LangOpts) { 1766 std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts); 1767 1768 if (LastCharLoc) 1769 return Lexer::getSourceText( 1770 CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM, 1771 LangOpts); 1772 1773 return std::nullopt; 1774 } 1775 1776 // Returns the literal text in `SourceRange SR`, if `SR` is a valid range. 1777 static std::optional<StringRef> getRangeText(SourceRange SR, 1778 const SourceManager &SM, 1779 const LangOptions &LangOpts) { 1780 bool Invalid = false; 1781 CharSourceRange CSR = CharSourceRange::getCharRange(SR); 1782 StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid); 1783 1784 if (!Invalid) 1785 return Text; 1786 return std::nullopt; 1787 } 1788 1789 // Returns the begin location of the identifier of the given variable 1790 // declaration. 1791 static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) { 1792 // According to the implementation of `VarDecl`, `VD->getLocation()` actually 1793 // returns the begin location of the identifier of the declaration: 1794 return VD->getLocation(); 1795 } 1796 1797 // Returns the literal text of the identifier of the given variable declaration. 1798 static std::optional<StringRef> 1799 getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM, 1800 const LangOptions &LangOpts) { 1801 SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD); 1802 SourceLocation ParmIdentEndLoc = 1803 Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts); 1804 1805 if (ParmIdentEndLoc.isMacroID() && 1806 !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts)) 1807 return std::nullopt; 1808 return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts); 1809 } 1810 1811 // We cannot fix a variable declaration if it has some other specifiers than the 1812 // type specifier. Because the source ranges of those specifiers could overlap 1813 // with the source range that is being replaced using fix-its. Especially when 1814 // we often cannot obtain accurate source ranges of cv-qualified type 1815 // specifiers. 1816 // FIXME: also deal with type attributes 1817 static bool hasUnsupportedSpecifiers(const VarDecl *VD, 1818 const SourceManager &SM) { 1819 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the 1820 // source range of `VD`: 1821 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool { 1822 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(), 1823 VD->getBeginLoc())) && 1824 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(), 1825 At->getRange().getBegin())); 1826 }); 1827 return VD->isInlineSpecified() || VD->isConstexpr() || 1828 VD->hasConstantInitialization() || !VD->hasLocalStorage() || 1829 AttrRangeOverlapping; 1830 } 1831 1832 // Returns the `SourceRange` of `D`. The reason why this function exists is 1833 // that `D->getSourceRange()` may return a range where the end location is the 1834 // starting location of the last token. The end location of the source range 1835 // returned by this function is the last location of the last token. 1836 static SourceRange getSourceRangeToTokenEnd(const Decl *D, 1837 const SourceManager &SM, 1838 const LangOptions &LangOpts) { 1839 SourceLocation Begin = D->getBeginLoc(); 1840 SourceLocation 1841 End = // `D->getEndLoc` should always return the starting location of the 1842 // last token, so we should get the end of the token 1843 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts); 1844 1845 return SourceRange(Begin, End); 1846 } 1847 1848 // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer 1849 // type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not 1850 // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me 1851 // :( ), `Qualifiers` of the pointee type is returned separately through the 1852 // output parameter `QualifiersToAppend`. 1853 static std::optional<std::string> 1854 getPointeeTypeText(const VarDecl *VD, const SourceManager &SM, 1855 const LangOptions &LangOpts, 1856 std::optional<Qualifiers> *QualifiersToAppend) { 1857 QualType Ty = VD->getType(); 1858 QualType PteTy; 1859 1860 assert(Ty->isPointerType() && !Ty->isFunctionPointerType() && 1861 "Expecting a VarDecl of type of pointer to object type"); 1862 PteTy = Ty->getPointeeType(); 1863 1864 TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc(); 1865 TypeLoc PteTyLoc; 1866 1867 // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns 1868 // the `TypeLoc` of the pointee type: 1869 switch (TyLoc.getTypeLocClass()) { 1870 case TypeLoc::ConstantArray: 1871 case TypeLoc::IncompleteArray: 1872 case TypeLoc::VariableArray: 1873 case TypeLoc::DependentSizedArray: 1874 case TypeLoc::Decayed: 1875 assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a " 1876 "pointer type unless it decays."); 1877 PteTyLoc = TyLoc.getNextTypeLoc(); 1878 break; 1879 case TypeLoc::Pointer: 1880 PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc(); 1881 break; 1882 default: 1883 return std::nullopt; 1884 } 1885 if (PteTyLoc.isNull()) 1886 // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g., 1887 // when the pointer type is `auto`. 1888 return std::nullopt; 1889 1890 SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD); 1891 1892 if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) { 1893 // We are expecting these locations to be valid. But in some cases, they are 1894 // not all valid. It is a Clang bug to me and we are not responsible for 1895 // fixing it. So we will just give up for now when it happens. 1896 return std::nullopt; 1897 } 1898 1899 // Note that TypeLoc.getEndLoc() returns the begin location of the last token: 1900 SourceLocation PteEndOfTokenLoc = 1901 Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts); 1902 1903 if (!PteEndOfTokenLoc.isValid()) 1904 // Sometimes we cannot get the end location of the pointee type, e.g., when 1905 // there are macros involved. 1906 return std::nullopt; 1907 if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) { 1908 // We only deal with the cases where the source text of the pointee type 1909 // appears on the left-hand side of the variable identifier completely, 1910 // including the following forms: 1911 // `T ident`, 1912 // `T ident[]`, where `T` is any type. 1913 // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`. 1914 return std::nullopt; 1915 } 1916 if (PteTy.hasQualifiers()) { 1917 // TypeLoc does not provide source ranges for qualifiers (it says it's 1918 // intentional but seems fishy to me), so we cannot get the full text 1919 // `PteTy` via source ranges. 1920 *QualifiersToAppend = PteTy.getQualifiers(); 1921 } 1922 return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts) 1923 ->str(); 1924 } 1925 1926 // Returns the text of the name (with qualifiers) of a `FunctionDecl`. 1927 static std::optional<StringRef> getFunNameText(const FunctionDecl *FD, 1928 const SourceManager &SM, 1929 const LangOptions &LangOpts) { 1930 SourceLocation BeginLoc = FD->getQualifier() 1931 ? FD->getQualifierLoc().getBeginLoc() 1932 : FD->getNameInfo().getBeginLoc(); 1933 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the 1934 // last token: 1935 SourceLocation EndLoc = Lexer::getLocForEndOfToken( 1936 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts); 1937 SourceRange NameRange{BeginLoc, EndLoc}; 1938 1939 return getRangeText(NameRange, SM, LangOpts); 1940 } 1941 1942 // Returns the text representing a `std::span` type where the element type is 1943 // represented by `EltTyText`. 1944 // 1945 // Note the optional parameter `Qualifiers`: one needs to pass qualifiers 1946 // explicitly if the element type needs to be qualified. 1947 static std::string 1948 getSpanTypeText(StringRef EltTyText, 1949 std::optional<Qualifiers> Quals = std::nullopt) { 1950 const char *const SpanOpen = "std::span<"; 1951 1952 if (Quals) 1953 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>'; 1954 return SpanOpen + EltTyText.str() + '>'; 1955 } 1956 1957 std::optional<FixItList> 1958 DerefSimplePtrArithFixableGadget::getFixits(const FixitStrategy &s) const { 1959 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl()); 1960 1961 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) { 1962 ASTContext &Ctx = VD->getASTContext(); 1963 // std::span can't represent elements before its begin() 1964 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx)) 1965 if (ConstVal->isNegative()) 1966 return std::nullopt; 1967 1968 // note that the expr may (oddly) has multiple layers of parens 1969 // example: 1970 // *((..(pointer + 123)..)) 1971 // goal: 1972 // pointer[123] 1973 // Fix-It: 1974 // remove '*(' 1975 // replace ' + ' with '[' 1976 // replace ')' with ']' 1977 1978 // example: 1979 // *((..(123 + pointer)..)) 1980 // goal: 1981 // 123[pointer] 1982 // Fix-It: 1983 // remove '*(' 1984 // replace ' + ' with '[' 1985 // replace ')' with ']' 1986 1987 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS(); 1988 const SourceManager &SM = Ctx.getSourceManager(); 1989 const LangOptions &LangOpts = Ctx.getLangOpts(); 1990 CharSourceRange StarWithTrailWhitespace = 1991 clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(), 1992 LHS->getBeginLoc()); 1993 1994 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts); 1995 if (!LHSLocation) 1996 return std::nullopt; 1997 1998 CharSourceRange PlusWithSurroundingWhitespace = 1999 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc()); 2000 2001 std::optional<SourceLocation> AddOpLocation = 2002 getPastLoc(AddOp, SM, LangOpts); 2003 std::optional<SourceLocation> DerefOpLocation = 2004 getPastLoc(DerefOp, SM, LangOpts); 2005 2006 if (!AddOpLocation || !DerefOpLocation) 2007 return std::nullopt; 2008 2009 CharSourceRange ClosingParenWithPrecWhitespace = 2010 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation); 2011 2012 return FixItList{ 2013 {FixItHint::CreateRemoval(StarWithTrailWhitespace), 2014 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["), 2015 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}}; 2016 } 2017 return std::nullopt; // something wrong or unsupported, give up 2018 } 2019 2020 std::optional<FixItList> 2021 PointerDereferenceGadget::getFixits(const FixitStrategy &S) const { 2022 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl()); 2023 switch (S.lookup(VD)) { 2024 case FixitStrategy::Kind::Span: { 2025 ASTContext &Ctx = VD->getASTContext(); 2026 SourceManager &SM = Ctx.getSourceManager(); 2027 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0] 2028 // Deletes the *operand 2029 CharSourceRange derefRange = clang::CharSourceRange::getCharRange( 2030 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1)); 2031 // Inserts the [0] 2032 if (auto LocPastOperand = 2033 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) { 2034 return FixItList{{FixItHint::CreateRemoval(derefRange), 2035 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}}; 2036 } 2037 break; 2038 } 2039 case FixitStrategy::Kind::Iterator: 2040 case FixitStrategy::Kind::Array: 2041 return std::nullopt; 2042 case FixitStrategy::Kind::Vector: 2043 llvm_unreachable("FixitStrategy not implemented yet!"); 2044 case FixitStrategy::Kind::Wontfix: 2045 llvm_unreachable("Invalid strategy!"); 2046 } 2047 2048 return std::nullopt; 2049 } 2050 2051 static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx, 2052 const DeclRefExpr *DRE) { 2053 const SourceManager &SM = Ctx.getSourceManager(); 2054 // Inserts the .data() after the DRE 2055 std::optional<SourceLocation> EndOfOperand = 2056 getPastLoc(DRE, SM, Ctx.getLangOpts()); 2057 2058 if (EndOfOperand) 2059 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}}; 2060 2061 return std::nullopt; 2062 } 2063 2064 // Generates fix-its replacing an expression of the form UPC(DRE) with 2065 // `DRE.data()` 2066 std::optional<FixItList> 2067 UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const { 2068 const auto VD = cast<VarDecl>(Node->getDecl()); 2069 switch (S.lookup(VD)) { 2070 case FixitStrategy::Kind::Array: 2071 case FixitStrategy::Kind::Span: { 2072 return createDataFixit(VD->getASTContext(), Node); 2073 // FIXME: Points inside a macro expansion. 2074 break; 2075 } 2076 case FixitStrategy::Kind::Wontfix: 2077 case FixitStrategy::Kind::Iterator: 2078 return std::nullopt; 2079 case FixitStrategy::Kind::Vector: 2080 llvm_unreachable("unsupported strategies for FixableGadgets"); 2081 } 2082 2083 return std::nullopt; 2084 } 2085 2086 // Generates fix-its replacing an expression of the form `&DRE[e]` with 2087 // `&DRE.data()[e]`: 2088 static std::optional<FixItList> 2089 fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) { 2090 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr()); 2091 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts()); 2092 // FIXME: this `getASTContext` call is costly, we should pass the 2093 // ASTContext in: 2094 const ASTContext &Ctx = DRE->getDecl()->getASTContext(); 2095 const Expr *Idx = ArraySub->getIdx(); 2096 const SourceManager &SM = Ctx.getSourceManager(); 2097 const LangOptions &LangOpts = Ctx.getLangOpts(); 2098 std::stringstream SS; 2099 bool IdxIsLitZero = false; 2100 2101 if (auto ICE = Idx->getIntegerConstantExpr(Ctx)) 2102 if ((*ICE).isZero()) 2103 IdxIsLitZero = true; 2104 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts); 2105 if (!DreString) 2106 return std::nullopt; 2107 2108 if (IdxIsLitZero) { 2109 // If the index is literal zero, we produce the most concise fix-it: 2110 SS << (*DreString).str() << ".data()"; 2111 } else { 2112 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts); 2113 if (!IndexString) 2114 return std::nullopt; 2115 2116 SS << "&" << (*DreString).str() << ".data()" 2117 << "[" << (*IndexString).str() << "]"; 2118 } 2119 return FixItList{ 2120 FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())}; 2121 } 2122 2123 std::optional<FixItList> 2124 UUCAddAssignGadget::getFixits(const FixitStrategy &S) const { 2125 DeclUseList DREs = getClaimedVarUseSites(); 2126 2127 if (DREs.size() != 1) 2128 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we 2129 // give up 2130 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) { 2131 if (S.lookup(VD) == FixitStrategy::Kind::Span) { 2132 FixItList Fixes; 2133 2134 const Stmt *AddAssignNode = Node; 2135 StringRef varName = VD->getName(); 2136 const ASTContext &Ctx = VD->getASTContext(); 2137 2138 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx)) 2139 return std::nullopt; 2140 2141 // To transform UUC(p += n) to UUC(p = p.subspan(..)): 2142 bool NotParenExpr = 2143 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc()); 2144 std::string SS = varName.str() + " = " + varName.str() + ".subspan"; 2145 if (NotParenExpr) 2146 SS += "("; 2147 2148 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc( 2149 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts()); 2150 if (!AddAssignLocation) 2151 return std::nullopt; 2152 2153 Fixes.push_back(FixItHint::CreateReplacement( 2154 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()), 2155 SS)); 2156 if (NotParenExpr) 2157 Fixes.push_back(FixItHint::CreateInsertion( 2158 Offset->getEndLoc().getLocWithOffset(1), ")")); 2159 return Fixes; 2160 } 2161 } 2162 return std::nullopt; // Not in the cases that we can handle for now, give up. 2163 } 2164 2165 std::optional<FixItList> 2166 UPCPreIncrementGadget::getFixits(const FixitStrategy &S) const { 2167 DeclUseList DREs = getClaimedVarUseSites(); 2168 2169 if (DREs.size() != 1) 2170 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we 2171 // give up 2172 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) { 2173 if (S.lookup(VD) == FixitStrategy::Kind::Span) { 2174 FixItList Fixes; 2175 std::stringstream SS; 2176 StringRef varName = VD->getName(); 2177 const ASTContext &Ctx = VD->getASTContext(); 2178 2179 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()): 2180 SS << "(" << varName.data() << " = " << varName.data() 2181 << ".subspan(1)).data()"; 2182 std::optional<SourceLocation> PreIncLocation = 2183 getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts()); 2184 if (!PreIncLocation) 2185 return std::nullopt; 2186 2187 Fixes.push_back(FixItHint::CreateReplacement( 2188 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str())); 2189 return Fixes; 2190 } 2191 } 2192 return std::nullopt; // Not in the cases that we can handle for now, give up. 2193 } 2194 2195 // For a non-null initializer `Init` of `T *` type, this function returns 2196 // `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it 2197 // to output stream. 2198 // In many cases, this function cannot figure out the actual extent `S`. It 2199 // then will use a place holder to replace `S` to ask users to fill `S` in. The 2200 // initializer shall be used to initialize a variable of type `std::span<T>`. 2201 // In some cases (e. g. constant size array) the initializer should remain 2202 // unchanged and the function returns empty list. In case the function can't 2203 // provide the right fixit it will return nullopt. 2204 // 2205 // FIXME: Support multi-level pointers 2206 // 2207 // Parameters: 2208 // `Init` a pointer to the initializer expression 2209 // `Ctx` a reference to the ASTContext 2210 static std::optional<FixItList> 2211 FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, 2212 const StringRef UserFillPlaceHolder) { 2213 const SourceManager &SM = Ctx.getSourceManager(); 2214 const LangOptions &LangOpts = Ctx.getLangOpts(); 2215 2216 // If `Init` has a constant value that is (or equivalent to) a 2217 // NULL pointer, we use the default constructor to initialize the span 2218 // object, i.e., a `std:span` variable declaration with no initializer. 2219 // So the fix-it is just to remove the initializer. 2220 if (Init->isNullPointerConstant( 2221 Ctx, 2222 // FIXME: Why does this function not ask for `const ASTContext 2223 // &`? It should. Maybe worth an NFC patch later. 2224 Expr::NullPointerConstantValueDependence:: 2225 NPC_ValueDependentIsNotNull)) { 2226 std::optional<SourceLocation> InitLocation = 2227 getEndCharLoc(Init, SM, LangOpts); 2228 if (!InitLocation) 2229 return std::nullopt; 2230 2231 SourceRange SR(Init->getBeginLoc(), *InitLocation); 2232 2233 return FixItList{FixItHint::CreateRemoval(SR)}; 2234 } 2235 2236 FixItList FixIts{}; 2237 std::string ExtentText = UserFillPlaceHolder.data(); 2238 StringRef One = "1"; 2239 2240 // Insert `{` before `Init`: 2241 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{")); 2242 // Try to get the data extent. Break into different cases: 2243 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) { 2244 // In cases `Init` is `new T[n]` and there is no explicit cast over 2245 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects 2246 // of `T`. So the extent is `n` unless `n` has side effects. Similar but 2247 // simpler for the case where `Init` is `new T`. 2248 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) { 2249 if (!Ext->HasSideEffects(Ctx)) { 2250 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts); 2251 if (!ExtentString) 2252 return std::nullopt; 2253 ExtentText = *ExtentString; 2254 } 2255 } else if (!CxxNew->isArray()) 2256 // Although the initializer is not allocating a buffer, the pointer 2257 // variable could still be used in buffer access operations. 2258 ExtentText = One; 2259 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) { 2260 // std::span has a single parameter constructor for initialization with 2261 // constant size array. The size is auto-deduced as the constructor is a 2262 // function template. The correct fixit is empty - no changes should happen. 2263 return FixItList{}; 2264 } else { 2265 // In cases `Init` is of the form `&Var` after stripping of implicit 2266 // casts, where `&` is the built-in operator, the extent is 1. 2267 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts())) 2268 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf && 2269 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr())) 2270 ExtentText = One; 2271 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`, 2272 // and explicit casting, etc. etc. 2273 } 2274 2275 SmallString<32> StrBuffer{}; 2276 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts); 2277 2278 if (!LocPassInit) 2279 return std::nullopt; 2280 2281 StrBuffer.append(", "); 2282 StrBuffer.append(ExtentText); 2283 StrBuffer.append("}"); 2284 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str())); 2285 return FixIts; 2286 } 2287 2288 #ifndef NDEBUG 2289 #define DEBUG_NOTE_DECL_FAIL(D, Msg) \ 2290 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \ 2291 "failed to produce fixit for declaration '" + \ 2292 (D)->getNameAsString() + "'" + (Msg)) 2293 #else 2294 #define DEBUG_NOTE_DECL_FAIL(D, Msg) 2295 #endif 2296 2297 // For the given variable declaration with a pointer-to-T type, returns the text 2298 // `std::span<T>`. If it is unable to generate the text, returns 2299 // `std::nullopt`. 2300 static std::optional<std::string> 2301 createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx) { 2302 assert(VD->getType()->isPointerType()); 2303 2304 std::optional<Qualifiers> PteTyQualifiers = std::nullopt; 2305 std::optional<std::string> PteTyText = getPointeeTypeText( 2306 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers); 2307 2308 if (!PteTyText) 2309 return std::nullopt; 2310 2311 std::string SpanTyText = "std::span<"; 2312 2313 SpanTyText.append(*PteTyText); 2314 // Append qualifiers to span element type if any: 2315 if (PteTyQualifiers) { 2316 SpanTyText.append(" "); 2317 SpanTyText.append(PteTyQualifiers->getAsString()); 2318 } 2319 SpanTyText.append(">"); 2320 return SpanTyText; 2321 } 2322 2323 // For a `VarDecl` of the form `T * var (= Init)?`, this 2324 // function generates fix-its that 2325 // 1) replace `T * var` with `std::span<T> var`; and 2326 // 2) change `Init` accordingly to a span constructor, if it exists. 2327 // 2328 // FIXME: support Multi-level pointers 2329 // 2330 // Parameters: 2331 // `D` a pointer the variable declaration node 2332 // `Ctx` a reference to the ASTContext 2333 // `UserFillPlaceHolder` the user-input placeholder text 2334 // Returns: 2335 // the non-empty fix-it list, if fix-its are successfuly generated; empty 2336 // list otherwise. 2337 static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, 2338 const StringRef UserFillPlaceHolder, 2339 UnsafeBufferUsageHandler &Handler) { 2340 if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager())) 2341 return {}; 2342 2343 FixItList FixIts{}; 2344 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx); 2345 2346 if (!SpanTyText) { 2347 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type"); 2348 return {}; 2349 } 2350 2351 // Will hold the text for `std::span<T> Ident`: 2352 std::stringstream SS; 2353 2354 SS << *SpanTyText; 2355 // Fix the initializer if it exists: 2356 if (const Expr *Init = D->getInit()) { 2357 std::optional<FixItList> InitFixIts = 2358 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder); 2359 if (!InitFixIts) 2360 return {}; 2361 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()), 2362 std::make_move_iterator(InitFixIts->end())); 2363 } 2364 // For declaration of the form `T * ident = init;`, we want to replace 2365 // `T * ` with `std::span<T>`. 2366 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace 2367 // just `T *` with `std::span<T>`. 2368 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc(); 2369 if (!EndLocForReplacement.isValid()) { 2370 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration"); 2371 return {}; 2372 } 2373 // The only exception is that for `T *ident` we'll add a single space between 2374 // "std::span<T>" and "ident". 2375 // FIXME: The condition is false for identifiers expended from macros. 2376 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D)) 2377 SS << " "; 2378 2379 FixIts.push_back(FixItHint::CreateReplacement( 2380 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str())); 2381 return FixIts; 2382 } 2383 2384 static bool hasConflictingOverload(const FunctionDecl *FD) { 2385 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult(); 2386 } 2387 2388 // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new 2389 // types, this function produces fix-its to make the change self-contained. Let 2390 // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the 2391 // entity defined by the `FunctionDecl` after the change to the parameters. 2392 // Fix-its produced by this function are 2393 // 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration 2394 // of 'F'; 2395 // 2. Create a declaration of "NewF" next to each declaration of `F`; 2396 // 3. Create a definition of "F" (as its' original definition is now belongs 2397 // to "NewF") next to its original definition. The body of the creating 2398 // definition calls to "NewF". 2399 // 2400 // Example: 2401 // 2402 // void f(int *p); // original declaration 2403 // void f(int *p) { // original definition 2404 // p[5]; 2405 // } 2406 // 2407 // To change the parameter `p` to be of `std::span<int>` type, we 2408 // also add overloads: 2409 // 2410 // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl 2411 // void f(std::span<int> p); // added overload decl 2412 // void f(std::span<int> p) { // original def where param is changed 2413 // p[5]; 2414 // } 2415 // [[clang::unsafe_buffer_usage]] void f(int *p) { // added def 2416 // return f(std::span(p, <# size #>)); 2417 // } 2418 // 2419 static std::optional<FixItList> 2420 createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD, 2421 const ASTContext &Ctx, 2422 UnsafeBufferUsageHandler &Handler) { 2423 // FIXME: need to make this conflict checking better: 2424 if (hasConflictingOverload(FD)) 2425 return std::nullopt; 2426 2427 const SourceManager &SM = Ctx.getSourceManager(); 2428 const LangOptions &LangOpts = Ctx.getLangOpts(); 2429 const unsigned NumParms = FD->getNumParams(); 2430 std::vector<std::string> NewTysTexts(NumParms); 2431 std::vector<bool> ParmsMask(NumParms, false); 2432 bool AtLeastOneParmToFix = false; 2433 2434 for (unsigned i = 0; i < NumParms; i++) { 2435 const ParmVarDecl *PVD = FD->getParamDecl(i); 2436 2437 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix) 2438 continue; 2439 if (S.lookup(PVD) != FixitStrategy::Kind::Span) 2440 // Not supported, not suppose to happen: 2441 return std::nullopt; 2442 2443 std::optional<Qualifiers> PteTyQuals = std::nullopt; 2444 std::optional<std::string> PteTyText = 2445 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals); 2446 2447 if (!PteTyText) 2448 // something wrong in obtaining the text of the pointee type, give up 2449 return std::nullopt; 2450 // FIXME: whether we should create std::span type depends on the 2451 // FixitStrategy. 2452 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals); 2453 ParmsMask[i] = true; 2454 AtLeastOneParmToFix = true; 2455 } 2456 if (!AtLeastOneParmToFix) 2457 // No need to create function overloads: 2458 return {}; 2459 // FIXME Respect indentation of the original code. 2460 2461 // A lambda that creates the text representation of a function declaration 2462 // with the new type signatures: 2463 const auto NewOverloadSignatureCreator = 2464 [&SM, &LangOpts, &NewTysTexts, 2465 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> { 2466 std::stringstream SS; 2467 2468 SS << ";"; 2469 SS << getEndOfLine().str(); 2470 // Append: ret-type func-name "(" 2471 if (auto Prefix = getRangeText( 2472 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()), 2473 SM, LangOpts)) 2474 SS << Prefix->str(); 2475 else 2476 return std::nullopt; // give up 2477 // Append: parameter-type-list 2478 const unsigned NumParms = FD->getNumParams(); 2479 2480 for (unsigned i = 0; i < NumParms; i++) { 2481 const ParmVarDecl *Parm = FD->getParamDecl(i); 2482 2483 if (Parm->isImplicit()) 2484 continue; 2485 if (ParmsMask[i]) { 2486 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its 2487 // new type: 2488 SS << NewTysTexts[i]; 2489 // print parameter name if provided: 2490 if (IdentifierInfo *II = Parm->getIdentifier()) 2491 SS << ' ' << II->getName().str(); 2492 } else if (auto ParmTypeText = 2493 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts), 2494 SM, LangOpts)) { 2495 // print the whole `Parm` without modification: 2496 SS << ParmTypeText->str(); 2497 } else 2498 return std::nullopt; // something wrong, give up 2499 if (i != NumParms - 1) 2500 SS << ", "; 2501 } 2502 SS << ")"; 2503 return SS.str(); 2504 }; 2505 2506 // A lambda that creates the text representation of a function definition with 2507 // the original signature: 2508 const auto OldOverloadDefCreator = 2509 [&Handler, &SM, &LangOpts, &NewTysTexts, 2510 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> { 2511 std::stringstream SS; 2512 2513 SS << getEndOfLine().str(); 2514 // Append: attr-name ret-type func-name "(" param-list ")" "{" 2515 if (auto FDPrefix = getRangeText( 2516 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM, 2517 LangOpts)) 2518 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ") 2519 << FDPrefix->str() << "{"; 2520 else 2521 return std::nullopt; 2522 // Append: "return" func-name "(" 2523 if (auto FunQualName = getFunNameText(FD, SM, LangOpts)) 2524 SS << "return " << FunQualName->str() << "("; 2525 else 2526 return std::nullopt; 2527 2528 // Append: arg-list 2529 const unsigned NumParms = FD->getNumParams(); 2530 for (unsigned i = 0; i < NumParms; i++) { 2531 const ParmVarDecl *Parm = FD->getParamDecl(i); 2532 2533 if (Parm->isImplicit()) 2534 continue; 2535 // FIXME: If a parameter has no name, it is unused in the 2536 // definition. So we could just leave it as it is. 2537 if (!Parm->getIdentifier()) 2538 // If a parameter of a function definition has no name: 2539 return std::nullopt; 2540 if (ParmsMask[i]) 2541 // This is our spanified paramter! 2542 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str() 2543 << ", " << getUserFillPlaceHolder("size") << ")"; 2544 else 2545 SS << Parm->getIdentifier()->getName().str(); 2546 if (i != NumParms - 1) 2547 SS << ", "; 2548 } 2549 // finish call and the body 2550 SS << ");}" << getEndOfLine().str(); 2551 // FIXME: 80-char line formatting? 2552 return SS.str(); 2553 }; 2554 2555 FixItList FixIts{}; 2556 for (FunctionDecl *FReDecl : FD->redecls()) { 2557 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts); 2558 2559 if (!Loc) 2560 return {}; 2561 if (FReDecl->isThisDeclarationADefinition()) { 2562 assert(FReDecl == FD && "inconsistent function definition"); 2563 // Inserts a definition with the old signature to the end of 2564 // `FReDecl`: 2565 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl)) 2566 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef)); 2567 else 2568 return {}; // give up 2569 } else { 2570 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`: 2571 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) { 2572 FixIts.emplace_back(FixItHint::CreateInsertion( 2573 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt( 2574 FReDecl->getBeginLoc(), " "))); 2575 } 2576 // Inserts a declaration with the new signature to the end of `FReDecl`: 2577 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl)) 2578 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl)); 2579 else 2580 return {}; 2581 } 2582 } 2583 return FixIts; 2584 } 2585 2586 // To fix a `ParmVarDecl` to be of `std::span` type. 2587 static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, 2588 UnsafeBufferUsageHandler &Handler) { 2589 if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) { 2590 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)"); 2591 return {}; 2592 } 2593 if (PVD->hasDefaultArg()) { 2594 // FIXME: generate fix-its for default values: 2595 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg"); 2596 return {}; 2597 } 2598 2599 std::optional<Qualifiers> PteTyQualifiers = std::nullopt; 2600 std::optional<std::string> PteTyText = getPointeeTypeText( 2601 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers); 2602 2603 if (!PteTyText) { 2604 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type"); 2605 return {}; 2606 } 2607 2608 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName(); 2609 2610 if (!PVDNameText) { 2611 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name"); 2612 return {}; 2613 } 2614 2615 std::stringstream SS; 2616 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx); 2617 2618 if (PteTyQualifiers) 2619 // Append qualifiers if they exist: 2620 SS << getSpanTypeText(*PteTyText, PteTyQualifiers); 2621 else 2622 SS << getSpanTypeText(*PteTyText); 2623 // Append qualifiers to the type of the parameter: 2624 if (PVD->getType().hasQualifiers()) 2625 SS << ' ' << PVD->getType().getQualifiers().getAsString(); 2626 // Append parameter's name: 2627 SS << ' ' << PVDNameText->str(); 2628 // Add replacement fix-it: 2629 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())}; 2630 } 2631 2632 static FixItList fixVariableWithSpan(const VarDecl *VD, 2633 const DeclUseTracker &Tracker, 2634 ASTContext &Ctx, 2635 UnsafeBufferUsageHandler &Handler) { 2636 const DeclStmt *DS = Tracker.lookupDecl(VD); 2637 if (!DS) { 2638 DEBUG_NOTE_DECL_FAIL(VD, 2639 " : variables declared this way not implemented yet"); 2640 return {}; 2641 } 2642 if (!DS->isSingleDecl()) { 2643 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` 2644 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls"); 2645 return {}; 2646 } 2647 // Currently DS is an unused variable but we'll need it when 2648 // non-single decls are implemented, where the pointee type name 2649 // and the '*' are spread around the place. 2650 (void)DS; 2651 2652 // FIXME: handle cases where DS has multiple declarations 2653 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler); 2654 } 2655 2656 static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, 2657 UnsafeBufferUsageHandler &Handler) { 2658 FixItList FixIts{}; 2659 2660 // Note: the code below expects the declaration to not use any type sugar like 2661 // typedef. 2662 if (auto CAT = dyn_cast<clang::ConstantArrayType>(D->getType())) { 2663 const QualType &ArrayEltT = CAT->getElementType(); 2664 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!"); 2665 // FIXME: support multi-dimensional arrays 2666 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType())) 2667 return {}; 2668 2669 const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D); 2670 2671 // Get the spelling of the element type as written in the source file 2672 // (including macros, etc.). 2673 auto MaybeElemTypeTxt = 2674 getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(), 2675 Ctx.getLangOpts()); 2676 if (!MaybeElemTypeTxt) 2677 return {}; 2678 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim(); 2679 2680 // Find the '[' token. 2681 std::optional<Token> NextTok = Lexer::findNextToken( 2682 IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts()); 2683 while (NextTok && !NextTok->is(tok::l_square) && 2684 NextTok->getLocation() <= D->getSourceRange().getEnd()) 2685 NextTok = Lexer::findNextToken(NextTok->getLocation(), 2686 Ctx.getSourceManager(), Ctx.getLangOpts()); 2687 if (!NextTok) 2688 return {}; 2689 const SourceLocation LSqBracketLoc = NextTok->getLocation(); 2690 2691 // Get the spelling of the array size as written in the source file 2692 // (including macros, etc.). 2693 auto MaybeArraySizeTxt = getRangeText( 2694 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()}, 2695 Ctx.getSourceManager(), Ctx.getLangOpts()); 2696 if (!MaybeArraySizeTxt) 2697 return {}; 2698 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim(); 2699 if (ArraySizeTxt.empty()) { 2700 // FIXME: Support array size getting determined from the initializer. 2701 // Examples: 2702 // int arr1[] = {0, 1, 2}; 2703 // int arr2{3, 4, 5}; 2704 // We might be able to preserve the non-specified size with `auto` and 2705 // `std::to_array`: 2706 // auto arr1 = std::to_array<int>({0, 1, 2}); 2707 return {}; 2708 } 2709 2710 std::optional<StringRef> IdentText = 2711 getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts()); 2712 2713 if (!IdentText) { 2714 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier"); 2715 return {}; 2716 } 2717 2718 SmallString<32> Replacement; 2719 raw_svector_ostream OS(Replacement); 2720 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> " 2721 << IdentText->str(); 2722 2723 FixIts.push_back(FixItHint::CreateReplacement( 2724 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str())); 2725 } 2726 2727 return FixIts; 2728 } 2729 2730 static FixItList fixVariableWithArray(const VarDecl *VD, 2731 const DeclUseTracker &Tracker, 2732 const ASTContext &Ctx, 2733 UnsafeBufferUsageHandler &Handler) { 2734 const DeclStmt *DS = Tracker.lookupDecl(VD); 2735 assert(DS && "Fixing non-local variables not implemented yet!"); 2736 if (!DS->isSingleDecl()) { 2737 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt` 2738 return {}; 2739 } 2740 // Currently DS is an unused variable but we'll need it when 2741 // non-single decls are implemented, where the pointee type name 2742 // and the '*' are spread around the place. 2743 (void)DS; 2744 2745 // FIXME: handle cases where DS has multiple declarations 2746 return fixVarDeclWithArray(VD, Ctx, Handler); 2747 } 2748 2749 // TODO: we should be consistent to use `std::nullopt` to represent no-fix due 2750 // to any unexpected problem. 2751 static FixItList 2752 fixVariable(const VarDecl *VD, FixitStrategy::Kind K, 2753 /* The function decl under analysis */ const Decl *D, 2754 const DeclUseTracker &Tracker, ASTContext &Ctx, 2755 UnsafeBufferUsageHandler &Handler) { 2756 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) { 2757 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext()); 2758 if (!FD || FD != D) { 2759 // `FD != D` means that `PVD` belongs to a function that is not being 2760 // analyzed currently. Thus `FD` may not be complete. 2761 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed"); 2762 return {}; 2763 } 2764 2765 // TODO If function has a try block we can't change params unless we check 2766 // also its catch block for their use. 2767 // FIXME We might support static class methods, some select methods, 2768 // operators and possibly lamdas. 2769 if (FD->isMain() || FD->isConstexpr() || 2770 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate || 2771 FD->isVariadic() || 2772 // also covers call-operator of lamdas 2773 isa<CXXMethodDecl>(FD) || 2774 // skip when the function body is a try-block 2775 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) || 2776 FD->isOverloadedOperator()) { 2777 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl"); 2778 return {}; // TODO test all these cases 2779 } 2780 } 2781 2782 switch (K) { 2783 case FixitStrategy::Kind::Span: { 2784 if (VD->getType()->isPointerType()) { 2785 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) 2786 return fixParamWithSpan(PVD, Ctx, Handler); 2787 2788 if (VD->isLocalVarDecl()) 2789 return fixVariableWithSpan(VD, Tracker, Ctx, Handler); 2790 } 2791 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer"); 2792 return {}; 2793 } 2794 case FixitStrategy::Kind::Array: { 2795 if (VD->isLocalVarDecl() && 2796 isa<clang::ConstantArrayType>(VD->getType().getCanonicalType())) 2797 return fixVariableWithArray(VD, Tracker, Ctx, Handler); 2798 2799 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array"); 2800 return {}; 2801 } 2802 case FixitStrategy::Kind::Iterator: 2803 case FixitStrategy::Kind::Vector: 2804 llvm_unreachable("FixitStrategy not implemented yet!"); 2805 case FixitStrategy::Kind::Wontfix: 2806 llvm_unreachable("Invalid strategy!"); 2807 } 2808 llvm_unreachable("Unknown strategy!"); 2809 } 2810 2811 // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the 2812 // `RemoveRange` of 'h' overlaps with a macro use. 2813 static bool overlapWithMacro(const FixItList &FixIts) { 2814 // FIXME: For now we only check if the range (or the first token) is (part of) 2815 // a macro expansion. Ideally, we want to check for all tokens in the range. 2816 return llvm::any_of(FixIts, [](const FixItHint &Hint) { 2817 auto Range = Hint.RemoveRange; 2818 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID()) 2819 // If the range (or the first token) is (part of) a macro expansion: 2820 return true; 2821 return false; 2822 }); 2823 } 2824 2825 // Returns true iff `VD` is a parameter of the declaration `D`: 2826 static bool isParameterOf(const VarDecl *VD, const Decl *D) { 2827 return isa<ParmVarDecl>(VD) && 2828 VD->getDeclContext() == dyn_cast<DeclContext>(D); 2829 } 2830 2831 // Erases variables in `FixItsForVariable`, if such a variable has an unfixable 2832 // group mate. A variable `v` is unfixable iff `FixItsForVariable` does not 2833 // contain `v`. 2834 static void eraseVarsForUnfixableGroupMates( 2835 std::map<const VarDecl *, FixItList> &FixItsForVariable, 2836 const VariableGroupsManager &VarGrpMgr) { 2837 // Variables will be removed from `FixItsForVariable`: 2838 SmallVector<const VarDecl *, 8> ToErase; 2839 2840 for (const auto &[VD, Ignore] : FixItsForVariable) { 2841 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD); 2842 if (llvm::any_of(Grp, 2843 [&FixItsForVariable](const VarDecl *GrpMember) -> bool { 2844 return !FixItsForVariable.count(GrpMember); 2845 })) { 2846 // At least one group member cannot be fixed, so we have to erase the 2847 // whole group: 2848 for (const VarDecl *Member : Grp) 2849 ToErase.push_back(Member); 2850 } 2851 } 2852 for (auto *VarToErase : ToErase) 2853 FixItsForVariable.erase(VarToErase); 2854 } 2855 2856 // Returns the fix-its that create bounds-safe function overloads for the 2857 // function `D`, if `D`'s parameters will be changed to safe-types through 2858 // fix-its in `FixItsForVariable`. 2859 // 2860 // NOTE: In case `D`'s parameters will be changed but bounds-safe function 2861 // overloads cannot created, the whole group that contains the parameters will 2862 // be erased from `FixItsForVariable`. 2863 static FixItList createFunctionOverloadsForParms( 2864 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */, 2865 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, 2866 const FixitStrategy &S, ASTContext &Ctx, 2867 UnsafeBufferUsageHandler &Handler) { 2868 FixItList FixItsSharedByParms{}; 2869 2870 std::optional<FixItList> OverloadFixes = 2871 createOverloadsForFixedParams(S, FD, Ctx, Handler); 2872 2873 if (OverloadFixes) { 2874 FixItsSharedByParms.append(*OverloadFixes); 2875 } else { 2876 // Something wrong in generating `OverloadFixes`, need to remove the 2877 // whole group, where parameters are in, from `FixItsForVariable` (Note 2878 // that all parameters should be in the same group): 2879 for (auto *Member : VarGrpMgr.getGroupOfParms()) 2880 FixItsForVariable.erase(Member); 2881 } 2882 return FixItsSharedByParms; 2883 } 2884 2885 // Constructs self-contained fix-its for each variable in `FixablesForAllVars`. 2886 static std::map<const VarDecl *, FixItList> 2887 getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S, 2888 ASTContext &Ctx, 2889 /* The function decl under analysis */ const Decl *D, 2890 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, 2891 const VariableGroupsManager &VarGrpMgr) { 2892 // `FixItsForVariable` will map each variable to a set of fix-its directly 2893 // associated to the variable itself. Fix-its of distinct variables in 2894 // `FixItsForVariable` are disjoint. 2895 std::map<const VarDecl *, FixItList> FixItsForVariable; 2896 2897 // Populate `FixItsForVariable` with fix-its directly associated with each 2898 // variable. Fix-its directly associated to a variable 'v' are the ones 2899 // produced by the `FixableGadget`s whose claimed variable is 'v'. 2900 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) { 2901 FixItsForVariable[VD] = 2902 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler); 2903 // If we fail to produce Fix-It for the declaration we have to skip the 2904 // variable entirely. 2905 if (FixItsForVariable[VD].empty()) { 2906 FixItsForVariable.erase(VD); 2907 continue; 2908 } 2909 for (const auto &F : Fixables) { 2910 std::optional<FixItList> Fixits = F->getFixits(S); 2911 2912 if (Fixits) { 2913 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(), 2914 Fixits->begin(), Fixits->end()); 2915 continue; 2916 } 2917 #ifndef NDEBUG 2918 Handler.addDebugNoteForVar( 2919 VD, F->getSourceLoc(), 2920 ("gadget '" + F->getDebugName() + "' refused to produce a fix") 2921 .str()); 2922 #endif 2923 FixItsForVariable.erase(VD); 2924 break; 2925 } 2926 } 2927 2928 // `FixItsForVariable` now contains only variables that can be 2929 // fixed. A variable can be fixed if its' declaration and all Fixables 2930 // associated to it can all be fixed. 2931 2932 // To further remove from `FixItsForVariable` variables whose group mates 2933 // cannot be fixed... 2934 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr); 2935 // Now `FixItsForVariable` gets further reduced: a variable is in 2936 // `FixItsForVariable` iff it can be fixed and all its group mates can be 2937 // fixed. 2938 2939 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`. 2940 // That is, when fixing multiple parameters in one step, these fix-its will 2941 // be applied only once (instead of being applied per parameter). 2942 FixItList FixItsSharedByParms{}; 2943 2944 if (auto *FD = dyn_cast<FunctionDecl>(D)) 2945 FixItsSharedByParms = createFunctionOverloadsForParms( 2946 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler); 2947 2948 // The map that maps each variable `v` to fix-its for the whole group where 2949 // `v` is in: 2950 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{ 2951 FixItsForVariable}; 2952 2953 for (auto &[Var, Ignore] : FixItsForVariable) { 2954 bool AnyParm = false; 2955 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm); 2956 2957 for (const VarDecl *GrpMate : VarGroupForVD) { 2958 if (Var == GrpMate) 2959 continue; 2960 if (FixItsForVariable.count(GrpMate)) 2961 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]); 2962 } 2963 if (AnyParm) { 2964 // This assertion should never fail. Otherwise we have a bug. 2965 assert(!FixItsSharedByParms.empty() && 2966 "Should not try to fix a parameter that does not belong to a " 2967 "FunctionDecl"); 2968 FinalFixItsForVariable[Var].append(FixItsSharedByParms); 2969 } 2970 } 2971 // Fix-its that will be applied in one step shall NOT: 2972 // 1. overlap with macros or/and templates; or 2973 // 2. conflict with each other. 2974 // Otherwise, the fix-its will be dropped. 2975 for (auto Iter = FinalFixItsForVariable.begin(); 2976 Iter != FinalFixItsForVariable.end();) 2977 if (overlapWithMacro(Iter->second) || 2978 clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) { 2979 Iter = FinalFixItsForVariable.erase(Iter); 2980 } else 2981 Iter++; 2982 return FinalFixItsForVariable; 2983 } 2984 2985 template <typename VarDeclIterTy> 2986 static FixitStrategy 2987 getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) { 2988 FixitStrategy S; 2989 for (const VarDecl *VD : UnsafeVars) { 2990 if (isa<ConstantArrayType>(VD->getType().getCanonicalType())) 2991 S.set(VD, FixitStrategy::Kind::Array); 2992 else 2993 S.set(VD, FixitStrategy::Kind::Span); 2994 } 2995 return S; 2996 } 2997 2998 // Manages variable groups: 2999 class VariableGroupsManagerImpl : public VariableGroupsManager { 3000 const std::vector<VarGrpTy> Groups; 3001 const std::map<const VarDecl *, unsigned> &VarGrpMap; 3002 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms; 3003 3004 public: 3005 VariableGroupsManagerImpl( 3006 const std::vector<VarGrpTy> &Groups, 3007 const std::map<const VarDecl *, unsigned> &VarGrpMap, 3008 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms) 3009 : Groups(Groups), VarGrpMap(VarGrpMap), 3010 GrpsUnionForParms(GrpsUnionForParms) {} 3011 3012 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override { 3013 if (GrpsUnionForParms.contains(Var)) { 3014 if (HasParm) 3015 *HasParm = true; 3016 return GrpsUnionForParms.getArrayRef(); 3017 } 3018 if (HasParm) 3019 *HasParm = false; 3020 3021 auto It = VarGrpMap.find(Var); 3022 3023 if (It == VarGrpMap.end()) 3024 return std::nullopt; 3025 return Groups[It->second]; 3026 } 3027 3028 VarGrpRef getGroupOfParms() const override { 3029 return GrpsUnionForParms.getArrayRef(); 3030 } 3031 }; 3032 3033 void clang::checkUnsafeBufferUsage(const Decl *D, 3034 UnsafeBufferUsageHandler &Handler, 3035 bool EmitSuggestions) { 3036 #ifndef NDEBUG 3037 Handler.clearDebugNotes(); 3038 #endif 3039 3040 assert(D && D->getBody()); 3041 // We do not want to visit a Lambda expression defined inside a method 3042 // independently. Instead, it should be visited along with the outer method. 3043 // FIXME: do we want to do the same thing for `BlockDecl`s? 3044 if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) { 3045 if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass()) 3046 return; 3047 } 3048 3049 // Do not emit fixit suggestions for functions declared in an 3050 // extern "C" block. 3051 if (const auto *FD = dyn_cast<FunctionDecl>(D)) { 3052 for (FunctionDecl *FReDecl : FD->redecls()) { 3053 if (FReDecl->isExternC()) { 3054 EmitSuggestions = false; 3055 break; 3056 } 3057 } 3058 } 3059 3060 WarningGadgetSets UnsafeOps; 3061 FixableGadgetSets FixablesForAllVars; 3062 3063 auto [FixableGadgets, WarningGadgets, Tracker] = 3064 findGadgets(D, Handler, EmitSuggestions); 3065 3066 if (!EmitSuggestions) { 3067 // Our job is very easy without suggestions. Just warn about 3068 // every problematic operation and consider it done. No need to deal 3069 // with fixable gadgets, no need to group operations by variable. 3070 for (const auto &G : WarningGadgets) { 3071 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false, 3072 D->getASTContext()); 3073 } 3074 3075 // This return guarantees that most of the machine doesn't run when 3076 // suggestions aren't requested. 3077 assert(FixableGadgets.size() == 0 && 3078 "Fixable gadgets found but suggestions not requested!"); 3079 return; 3080 } 3081 3082 // If no `WarningGadget`s ever matched, there is no unsafe operations in the 3083 // function under the analysis. No need to fix any Fixables. 3084 if (!WarningGadgets.empty()) { 3085 // Gadgets "claim" variables they're responsible for. Once this loop 3086 // finishes, the tracker will only track DREs that weren't claimed by any 3087 // gadgets, i.e. not understood by the analysis. 3088 for (const auto &G : FixableGadgets) { 3089 for (const auto *DRE : G->getClaimedVarUseSites()) { 3090 Tracker.claimUse(DRE); 3091 } 3092 } 3093 } 3094 3095 // If no `WarningGadget`s ever matched, there is no unsafe operations in the 3096 // function under the analysis. Thus, it early returns here as there is 3097 // nothing needs to be fixed. 3098 // 3099 // Note this claim is based on the assumption that there is no unsafe 3100 // variable whose declaration is invisible from the analyzing function. 3101 // Otherwise, we need to consider if the uses of those unsafe varuables needs 3102 // fix. 3103 // So far, we are not fixing any global variables or class members. And, 3104 // lambdas will be analyzed along with the enclosing function. So this early 3105 // return is correct for now. 3106 if (WarningGadgets.empty()) 3107 return; 3108 3109 UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); 3110 FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets)); 3111 3112 std::map<const VarDecl *, FixItList> FixItsForVariableGroup; 3113 3114 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. 3115 for (auto it = FixablesForAllVars.byVar.cbegin(); 3116 it != FixablesForAllVars.byVar.cend();) { 3117 // FIXME: need to deal with global variables later 3118 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) { 3119 #ifndef NDEBUG 3120 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(), 3121 ("failed to produce fixit for '" + 3122 it->first->getNameAsString() + 3123 "' : neither local nor a parameter")); 3124 #endif 3125 it = FixablesForAllVars.byVar.erase(it); 3126 } else if (it->first->getType().getCanonicalType()->isReferenceType()) { 3127 #ifndef NDEBUG 3128 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(), 3129 ("failed to produce fixit for '" + 3130 it->first->getNameAsString() + 3131 "' : has a reference type")); 3132 #endif 3133 it = FixablesForAllVars.byVar.erase(it); 3134 } else if (Tracker.hasUnclaimedUses(it->first)) { 3135 it = FixablesForAllVars.byVar.erase(it); 3136 } else if (it->first->isInitCapture()) { 3137 #ifndef NDEBUG 3138 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(), 3139 ("failed to produce fixit for '" + 3140 it->first->getNameAsString() + 3141 "' : init capture")); 3142 #endif 3143 it = FixablesForAllVars.byVar.erase(it); 3144 } else { 3145 ++it; 3146 } 3147 } 3148 3149 #ifndef NDEBUG 3150 for (const auto &it : UnsafeOps.byVar) { 3151 const VarDecl *const UnsafeVD = it.first; 3152 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD); 3153 if (UnclaimedDREs.empty()) 3154 continue; 3155 const auto UnfixedVDName = UnsafeVD->getNameAsString(); 3156 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) { 3157 std::string UnclaimedUseTrace = 3158 getDREAncestorString(UnclaimedDRE, D->getASTContext()); 3159 3160 Handler.addDebugNoteForVar( 3161 UnsafeVD, UnclaimedDRE->getBeginLoc(), 3162 ("failed to produce fixit for '" + UnfixedVDName + 3163 "' : has an unclaimed use\nThe unclaimed DRE trace: " + 3164 UnclaimedUseTrace)); 3165 } 3166 } 3167 #endif 3168 3169 // Fixpoint iteration for pointer assignments 3170 using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>; 3171 DepMapTy DependenciesMap{}; 3172 DepMapTy PtrAssignmentGraph{}; 3173 3174 for (auto it : FixablesForAllVars.byVar) { 3175 for (const FixableGadget *fixable : it.second) { 3176 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair = 3177 fixable->getStrategyImplications(); 3178 if (ImplPair) { 3179 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair); 3180 PtrAssignmentGraph[Impl.first].insert(Impl.second); 3181 } 3182 } 3183 } 3184 3185 /* 3186 The following code does a BFS traversal of the `PtrAssignmentGraph` 3187 considering all unsafe vars as starting nodes and constructs an undirected 3188 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner 3189 elimiates all variables that are unreachable from any unsafe var. In other 3190 words, this removes all dependencies that don't include any unsafe variable 3191 and consequently don't need any fixit generation. 3192 Note: A careful reader would observe that the code traverses 3193 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and 3194 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would 3195 achieve the same result but the one used here dramatically cuts the 3196 amount of hoops the second part of the algorithm needs to jump, given that 3197 a lot of these connections become "direct". The reader is advised not to 3198 imagine how the graph is transformed because of using `Var` instead of 3199 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used, 3200 and think about why it's equivalent later. 3201 */ 3202 std::set<const VarDecl *> VisitedVarsDirected{}; 3203 for (const auto &[Var, ignore] : UnsafeOps.byVar) { 3204 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) { 3205 3206 std::queue<const VarDecl *> QueueDirected{}; 3207 QueueDirected.push(Var); 3208 while (!QueueDirected.empty()) { 3209 const VarDecl *CurrentVar = QueueDirected.front(); 3210 QueueDirected.pop(); 3211 VisitedVarsDirected.insert(CurrentVar); 3212 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar]; 3213 for (const VarDecl *Adj : AdjacentNodes) { 3214 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) { 3215 QueueDirected.push(Adj); 3216 } 3217 DependenciesMap[Var].insert(Adj); 3218 DependenciesMap[Adj].insert(Var); 3219 } 3220 } 3221 } 3222 } 3223 3224 // `Groups` stores the set of Connected Components in the graph. 3225 std::vector<VarGrpTy> Groups; 3226 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the 3227 // variables belong to. Group indexes refer to the elements in `Groups`. 3228 // `VarGrpMap` is complete in that every variable that needs fix is in it. 3229 std::map<const VarDecl *, unsigned> VarGrpMap; 3230 // The union group over the ones in "Groups" that contain parameters of `D`: 3231 llvm::SetVector<const VarDecl *> 3232 GrpsUnionForParms; // these variables need to be fixed in one step 3233 3234 // Group Connected Components for Unsafe Vars 3235 // (Dependencies based on pointer assignments) 3236 std::set<const VarDecl *> VisitedVars{}; 3237 for (const auto &[Var, ignore] : UnsafeOps.byVar) { 3238 if (VisitedVars.find(Var) == VisitedVars.end()) { 3239 VarGrpTy &VarGroup = Groups.emplace_back(); 3240 std::queue<const VarDecl *> Queue{}; 3241 3242 Queue.push(Var); 3243 while (!Queue.empty()) { 3244 const VarDecl *CurrentVar = Queue.front(); 3245 Queue.pop(); 3246 VisitedVars.insert(CurrentVar); 3247 VarGroup.push_back(CurrentVar); 3248 auto AdjacentNodes = DependenciesMap[CurrentVar]; 3249 for (const VarDecl *Adj : AdjacentNodes) { 3250 if (VisitedVars.find(Adj) == VisitedVars.end()) { 3251 Queue.push(Adj); 3252 } 3253 } 3254 } 3255 3256 bool HasParm = false; 3257 unsigned GrpIdx = Groups.size() - 1; 3258 3259 for (const VarDecl *V : VarGroup) { 3260 VarGrpMap[V] = GrpIdx; 3261 if (!HasParm && isParameterOf(V, D)) 3262 HasParm = true; 3263 } 3264 if (HasParm) 3265 GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end()); 3266 } 3267 } 3268 3269 // Remove a `FixableGadget` if the associated variable is not in the graph 3270 // computed above. We do not want to generate fix-its for such variables, 3271 // since they are neither warned nor reachable from a warned one. 3272 // 3273 // Note a variable is not warned if it is not directly used in any unsafe 3274 // operation. A variable `v` is NOT reachable from an unsafe variable, if it 3275 // does not exist another variable `u` such that `u` is warned and fixing `u` 3276 // (transitively) implicates fixing `v`. 3277 // 3278 // For example, 3279 // ``` 3280 // void f(int * p) { 3281 // int * a = p; *p = 0; 3282 // } 3283 // ``` 3284 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither 3285 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of 3286 // the function above, `p` becomes reachable from a warned variable. 3287 for (auto I = FixablesForAllVars.byVar.begin(); 3288 I != FixablesForAllVars.byVar.end();) { 3289 // Note `VisitedVars` contain all the variables in the graph: 3290 if (!VisitedVars.count((*I).first)) { 3291 // no such var in graph: 3292 I = FixablesForAllVars.byVar.erase(I); 3293 } else 3294 ++I; 3295 } 3296 3297 // We assign strategies to variables that are 1) in the graph and 2) can be 3298 // fixed. Other variables have the default "Won't fix" strategy. 3299 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range( 3300 VisitedVars, [&FixablesForAllVars](const VarDecl *V) { 3301 // If a warned variable has no "Fixable", it is considered unfixable: 3302 return FixablesForAllVars.byVar.count(V); 3303 })); 3304 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms); 3305 3306 if (isa<NamedDecl>(D)) 3307 // The only case where `D` is not a `NamedDecl` is when `D` is a 3308 // `BlockDecl`. Let's not fix variables in blocks for now 3309 FixItsForVariableGroup = 3310 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D, 3311 Tracker, Handler, VarGrpMgr); 3312 3313 for (const auto &G : UnsafeOps.noVar) { 3314 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false, 3315 D->getASTContext()); 3316 } 3317 3318 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) { 3319 auto FixItsIt = FixItsForVariableGroup.find(VD); 3320 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr, 3321 FixItsIt != FixItsForVariableGroup.end() 3322 ? std::move(FixItsIt->second) 3323 : FixItList{}, 3324 D, NaiveStrategy); 3325 for (const auto &G : WarningGadgets) { 3326 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true, 3327 D->getASTContext()); 3328 } 3329 } 3330 } 3331