1 //===- BugReporterVisitors.h - Generate PathDiagnostics ---------*- 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 // This file declares BugReporterVisitors, which are used to generate enhanced 10 // diagnostic traces. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H 15 #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H 16 17 #include "clang/Analysis/ProgramPoint.h" 18 #include "clang/Basic/LLVM.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 21 #include "llvm/ADT/FoldingSet.h" 22 #include "llvm/ADT/IntrusiveRefCntPtr.h" 23 #include "llvm/ADT/STLExtras.h" 24 #include "llvm/ADT/SmallPtrSet.h" 25 #include "llvm/ADT/StringRef.h" 26 #include <list> 27 #include <memory> 28 #include <utility> 29 30 namespace clang { 31 32 class BinaryOperator; 33 class CFGBlock; 34 class DeclRefExpr; 35 class Expr; 36 class Stmt; 37 38 namespace ento { 39 40 class PathSensitiveBugReport; 41 class BugReporterContext; 42 class ExplodedNode; 43 class MemRegion; 44 class PathDiagnosticPiece; 45 using PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>; 46 47 /// BugReporterVisitors are used to add custom diagnostics along a path. 48 class BugReporterVisitor : public llvm::FoldingSetNode { 49 public: 50 BugReporterVisitor() = default; 51 BugReporterVisitor(const BugReporterVisitor &) = default; 52 BugReporterVisitor(BugReporterVisitor &&) {} 53 virtual ~BugReporterVisitor(); 54 55 /// Return a diagnostic piece which should be associated with the 56 /// given node. 57 /// Note that this function does *not* get run on the very last node 58 /// of the report, as the PathDiagnosticPiece associated with the 59 /// last node should be unique. 60 /// Use \ref getEndPath to customize the note associated with the report 61 /// end instead. 62 /// 63 /// The last parameter can be used to register a new visitor with the given 64 /// BugReport while processing a node. 65 virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 66 BugReporterContext &BRC, 67 PathSensitiveBugReport &BR) = 0; 68 69 /// Last function called on the visitor, no further calls to VisitNode 70 /// would follow. 71 virtual void finalizeVisitor(BugReporterContext &BRC, 72 const ExplodedNode *EndPathNode, 73 PathSensitiveBugReport &BR); 74 75 /// Provide custom definition for the final diagnostic piece on the 76 /// path - the piece, which is displayed before the path is expanded. 77 /// 78 /// NOTE that this function can be implemented on at most one used visitor, 79 /// and otherwise it crahes at runtime. 80 virtual PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, 81 const ExplodedNode *N, 82 PathSensitiveBugReport &BR); 83 84 virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; 85 86 /// Generates the default final diagnostic piece. 87 static PathDiagnosticPieceRef 88 getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, 89 const PathSensitiveBugReport &BR); 90 }; 91 92 namespace bugreporter { 93 94 /// Specifies the type of tracking for an expression. 95 enum class TrackingKind { 96 /// Default tracking kind -- specifies that as much information should be 97 /// gathered about the tracked expression value as possible. 98 Thorough, 99 /// Specifies that a more moderate tracking should be used for the expression 100 /// value. This will essentially make sure that functions relevant to it 101 /// aren't pruned, but otherwise relies on the user reading the code or 102 /// following the arrows. 103 Condition 104 }; 105 106 /// Defines a set of options altering tracking behavior. 107 struct TrackingOptions { 108 /// Specifies the kind of tracking. 109 TrackingKind Kind = TrackingKind::Thorough; 110 /// Specifies whether we should employ false positive suppression 111 /// (inlined defensive checks, returned null). 112 bool EnableNullFPSuppression = true; 113 }; 114 115 /// Describes an event when the value got stored into a memory region. 116 /// 117 /// As opposed to checker checkBind API, it reacts also to binds 118 /// generated by the checker as well. It can be useful when the binding 119 /// happened as a result of evalCall, for example. 120 struct StoreInfo { 121 enum Kind { 122 /// The value got stored into the region during initialization: 123 /// int x = 42; 124 Initialization, 125 /// The value got stored into the region during assignment: 126 /// int x; 127 /// x = 42; 128 Assignment, 129 /// The value got stored into the parameter region as the result 130 /// of a call. 131 CallArgument, 132 /// The value got stored into the region as block capture. 133 /// Block data is modeled as a separate region, thus whenever 134 /// the analyzer sees a captured variable, its value is copied 135 /// into a special block region. 136 BlockCapture 137 }; 138 139 /// The type of store operation. 140 Kind StoreKind; 141 /// The node where the store happened. 142 const ExplodedNode *StoreSite; 143 /// The expression where the value comes from. 144 /// NOTE: might be null. 145 const Expr *SourceOfTheValue; 146 /// Symbolic value that is being stored. 147 SVal Value; 148 /// Memory regions involved in the store operation. 149 /// Dest <- Origin 150 /// NOTE: Origin might be null, when the stored value doesn't come 151 /// from another region. 152 const MemRegion *Dest, *Origin; 153 }; 154 155 class Tracker; 156 using TrackerRef = llvm::IntrusiveRefCntPtr<Tracker>; 157 158 class ExpressionHandler; 159 class StoreHandler; 160 161 /// A generalized component for tracking expressions, values, and stores. 162 /// 163 /// Tracker aimes at providing a sensible set of default behaviors that can be 164 /// used by any checker, while providing mechanisms to hook into any part of the 165 /// tracking process and insert checker-specific logic. 166 class Tracker : public llvm::RefCountedBase<Tracker> { 167 private: 168 using ExpressionHandlerPtr = std::unique_ptr<ExpressionHandler>; 169 using StoreHandlerPtr = std::unique_ptr<StoreHandler>; 170 171 PathSensitiveBugReport &Report; 172 std::list<ExpressionHandlerPtr> ExpressionHandlers; 173 std::list<StoreHandlerPtr> StoreHandlers; 174 175 protected: 176 /// \param Report The bug report to which visitors should be attached. 177 Tracker(PathSensitiveBugReport &Report); 178 179 public: 180 virtual ~Tracker() = default; 181 182 static TrackerRef create(PathSensitiveBugReport &Report) { 183 return new Tracker(Report); 184 } 185 186 PathSensitiveBugReport &getReport() { return Report; } 187 188 /// Describes a tracking result with the most basic information of what was 189 /// actually done (or not done). 190 struct Result { 191 /// Usually it means that the tracker added visitors. 192 bool FoundSomethingToTrack = false; 193 /// Signifies that the tracking was interrupted at some point. 194 /// Usually this information is important only for sub-trackers. 195 bool WasInterrupted = false; 196 197 /// Combines the current result with the given result. 198 void combineWith(const Result &Other) { 199 // If we found something in one of the cases, we can 200 // say we found something overall. 201 FoundSomethingToTrack |= Other.FoundSomethingToTrack; 202 // The same goes to the interruption. 203 WasInterrupted |= Other.WasInterrupted; 204 } 205 }; 206 207 /// Track expression value back to its point of origin. 208 /// 209 /// \param E The expression value which we are tracking 210 /// \param N A node "downstream" from the evaluation of the statement. 211 /// \param Opts Tracking options specifying how we want to track the value. 212 virtual Result track(const Expr *E, const ExplodedNode *N, 213 TrackingOptions Opts = {}); 214 215 /// Track how the value got stored into the given region and where it came 216 /// from. 217 /// 218 /// \param V We're searching for the store where \c R received this value. 219 /// \param R The region we're tracking. 220 /// \param Opts Tracking options specifying how we want to track the value. 221 /// \param Origin Only adds notes when the last store happened in a 222 /// different stackframe to this one. Disregarded if the tracking kind 223 /// is thorough. 224 /// This is useful, because for non-tracked regions, notes about 225 /// changes to its value in a nested stackframe could be pruned, and 226 /// this visitor can prevent that without polluting the bugpath too 227 /// much. 228 virtual Result track(SVal V, const MemRegion *R, TrackingOptions Opts = {}, 229 const StackFrameContext *Origin = nullptr); 230 231 /// Handle the store operation and produce the note. 232 /// 233 /// \param SI The information fully describing the store. 234 /// \param Opts Tracking options specifying how we got to it. 235 /// 236 /// NOTE: this method is designed for sub-trackers and visitors. 237 virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC, 238 TrackingOptions Opts); 239 240 /// Add custom expression handler with the highest priority. 241 /// 242 /// It means that it will be asked for handling first, and can prevent 243 /// other handlers from running if decides to interrupt. 244 void addHighPriorityHandler(ExpressionHandlerPtr SH) { 245 ExpressionHandlers.push_front(std::move(SH)); 246 } 247 248 /// Add custom expression handler with the lowest priority. 249 /// 250 /// It means that it will be asked for handling last, and other handlers can 251 /// prevent it from running if any of them decides to interrupt. 252 void addLowPriorityHandler(ExpressionHandlerPtr SH) { 253 ExpressionHandlers.push_back(std::move(SH)); 254 } 255 256 /// Add custom store handler with the highest priority. 257 /// 258 /// It means that it will be asked for handling first, and will prevent 259 /// other handlers from running if it produces non-null note. 260 void addHighPriorityHandler(StoreHandlerPtr SH) { 261 StoreHandlers.push_front(std::move(SH)); 262 } 263 264 /// Add custom store handler with the lowest priority. 265 /// 266 /// It means that it will be asked for handling last, only 267 /// if all other handlers failed to produce the note. 268 void addLowPriorityHandler(StoreHandlerPtr SH) { 269 StoreHandlers.push_back(std::move(SH)); 270 } 271 272 /// Add custom expression/store handler with the highest priority 273 /// 274 /// See other overloads for explanation. 275 template <class HandlerType, class... Args> 276 void addHighPriorityHandler(Args &&... ConstructorArgs) { 277 addHighPriorityHandler(std::make_unique<HandlerType>( 278 *this, std::forward<Args>(ConstructorArgs)...)); 279 } 280 281 /// Add custom expression/store handler with the lowest priority 282 /// 283 /// See other overloads for explanation. 284 template <class HandlerType, class... Args> 285 void addLowPriorityHandler(Args &&... ConstructorArgs) { 286 addLowPriorityHandler(std::make_unique<HandlerType>( 287 *this, std::forward<Args>(ConstructorArgs)...)); 288 } 289 }; 290 291 /// Handles expressions during the tracking. 292 class ExpressionHandler { 293 private: 294 Tracker &ParentTracker; 295 296 public: 297 ExpressionHandler(Tracker &ParentTracker) : ParentTracker(ParentTracker) {} 298 virtual ~ExpressionHandler() {} 299 300 /// Handle the given expression from the given node. 301 /// 302 /// \param E The expression value which we are tracking 303 /// \param Original A node "downstream" where the tracking started. 304 /// \param ExprNode A node where the evaluation of \c E actually happens. 305 /// \param Opts Tracking options specifying how we are tracking the value. 306 virtual Tracker::Result handle(const Expr *E, const ExplodedNode *Original, 307 const ExplodedNode *ExprNode, 308 TrackingOptions Opts) = 0; 309 310 /// \Return the tracker that initiated the process. 311 Tracker &getParentTracker() { return ParentTracker; } 312 }; 313 314 /// Handles stores during the tracking. 315 class StoreHandler { 316 private: 317 Tracker &ParentTracker; 318 319 public: 320 StoreHandler(Tracker &ParentTracker) : ParentTracker(ParentTracker) {} 321 virtual ~StoreHandler() {} 322 323 /// Handle the given store and produce the node. 324 /// 325 /// \param SI The information fully describing the store. 326 /// \param Opts Tracking options specifying how we are tracking the value. 327 /// 328 /// \return the produced note, null if the handler doesn't support this kind 329 /// of stores. 330 virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC, 331 TrackingOptions Opts) = 0; 332 333 Tracker &getParentTracker() { return ParentTracker; } 334 335 protected: 336 PathDiagnosticPieceRef constructNote(StoreInfo SI, BugReporterContext &BRC, 337 StringRef NodeText); 338 }; 339 340 /// Visitor that tracks expressions and values. 341 class TrackingBugReporterVisitor : public BugReporterVisitor { 342 private: 343 TrackerRef ParentTracker; 344 345 public: 346 TrackingBugReporterVisitor(TrackerRef ParentTracker) 347 : ParentTracker(ParentTracker) {} 348 349 Tracker &getParentTracker() { return *ParentTracker; } 350 }; 351 352 /// Attempts to add visitors to track expression value back to its point of 353 /// origin. 354 /// 355 /// \param N A node "downstream" from the evaluation of the statement. 356 /// \param E The expression value which we are tracking 357 /// \param R The bug report to which visitors should be attached. 358 /// \param Opts Tracking options specifying how we are tracking the value. 359 /// 360 /// \return Whether or not the function was able to add visitors for this 361 /// statement. Note that returning \c true does not actually imply 362 /// that any visitors were added. 363 bool trackExpressionValue(const ExplodedNode *N, const Expr *E, 364 PathSensitiveBugReport &R, TrackingOptions Opts = {}); 365 366 /// Track how the value got stored into the given region and where it came 367 /// from. 368 /// 369 /// \param V We're searching for the store where \c R received this value. 370 /// \param R The region we're tracking. 371 /// \param Opts Tracking options specifying how we want to track the value. 372 /// \param Origin Only adds notes when the last store happened in a 373 /// different stackframe to this one. Disregarded if the tracking kind 374 /// is thorough. 375 /// This is useful, because for non-tracked regions, notes about 376 /// changes to its value in a nested stackframe could be pruned, and 377 /// this visitor can prevent that without polluting the bugpath too 378 /// much. 379 void trackStoredValue(KnownSVal V, const MemRegion *R, 380 PathSensitiveBugReport &Report, TrackingOptions Opts = {}, 381 const StackFrameContext *Origin = nullptr); 382 383 const Expr *getDerefExpr(const Stmt *S); 384 385 } // namespace bugreporter 386 387 class TrackConstraintBRVisitor final : public BugReporterVisitor { 388 DefinedSVal Constraint; 389 bool Assumption; 390 bool IsSatisfied = false; 391 bool IsZeroCheck; 392 393 /// We should start tracking from the last node along the path in which the 394 /// value is constrained. 395 bool IsTrackingTurnedOn = false; 396 397 public: 398 TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) 399 : Constraint(constraint), Assumption(assumption), 400 IsZeroCheck(!Assumption && isa<Loc>(Constraint)) {} 401 402 void Profile(llvm::FoldingSetNodeID &ID) const override; 403 404 /// Return the tag associated with this visitor. This tag will be used 405 /// to make all PathDiagnosticPieces created by this visitor. 406 static const char *getTag(); 407 408 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 409 BugReporterContext &BRC, 410 PathSensitiveBugReport &BR) override; 411 412 private: 413 /// Checks if the constraint is valid in the current state. 414 bool isUnderconstrained(const ExplodedNode *N) const; 415 }; 416 417 /// \class NilReceiverBRVisitor 418 /// Prints path notes when a message is sent to a nil receiver. 419 class NilReceiverBRVisitor final : public BugReporterVisitor { 420 public: 421 void Profile(llvm::FoldingSetNodeID &ID) const override { 422 static int x = 0; 423 ID.AddPointer(&x); 424 } 425 426 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 427 BugReporterContext &BRC, 428 PathSensitiveBugReport &BR) override; 429 430 /// If the statement is a message send expression with nil receiver, returns 431 /// the receiver expression. Returns NULL otherwise. 432 static const Expr *getNilReceiver(const Stmt *S, const ExplodedNode *N); 433 }; 434 435 /// Visitor that tries to report interesting diagnostics from conditions. 436 class ConditionBRVisitor final : public BugReporterVisitor { 437 // FIXME: constexpr initialization isn't supported by MSVC2013. 438 constexpr static llvm::StringLiteral GenericTrueMessage = 439 "Assuming the condition is true"; 440 constexpr static llvm::StringLiteral GenericFalseMessage = 441 "Assuming the condition is false"; 442 443 public: 444 void Profile(llvm::FoldingSetNodeID &ID) const override { 445 static int x = 0; 446 ID.AddPointer(&x); 447 } 448 449 /// Return the tag associated with this visitor. This tag will be used 450 /// to make all PathDiagnosticPieces created by this visitor. 451 static const char *getTag(); 452 453 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 454 BugReporterContext &BRC, 455 PathSensitiveBugReport &BR) override; 456 457 PathDiagnosticPieceRef VisitNodeImpl(const ExplodedNode *N, 458 BugReporterContext &BRC, 459 PathSensitiveBugReport &BR); 460 461 PathDiagnosticPieceRef 462 VisitTerminator(const Stmt *Term, const ExplodedNode *N, 463 const CFGBlock *SrcBlk, const CFGBlock *DstBlk, 464 PathSensitiveBugReport &R, BugReporterContext &BRC); 465 466 PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, 467 BugReporterContext &BRC, 468 PathSensitiveBugReport &R, 469 const ExplodedNode *N, bool TookTrue); 470 471 PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, 472 BugReporterContext &BRC, 473 PathSensitiveBugReport &R, 474 const ExplodedNode *N, bool TookTrue, 475 bool IsAssuming); 476 477 PathDiagnosticPieceRef 478 VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, 479 BugReporterContext &BRC, PathSensitiveBugReport &R, 480 const ExplodedNode *N, bool TookTrue, bool IsAssuming); 481 482 PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const MemberExpr *ME, 483 BugReporterContext &BRC, 484 PathSensitiveBugReport &R, 485 const ExplodedNode *N, bool TookTrue, 486 bool IsAssuming); 487 488 PathDiagnosticPieceRef 489 VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr, 490 BugReporterContext &BRC, PathSensitiveBugReport &R, 491 const ExplodedNode *N, bool TookTrue); 492 493 /// Tries to print the value of the given expression. 494 /// 495 /// \param CondVarExpr The expression to print its value. 496 /// \param Out The stream to print. 497 /// \param N The node where we encountered the condition. 498 /// \param TookTrue Whether we took the \c true branch of the condition. 499 /// 500 /// \return Whether the print was successful. (The printing is successful if 501 /// we model the value and we could obtain it.) 502 bool printValue(const Expr *CondVarExpr, raw_ostream &Out, 503 const ExplodedNode *N, bool TookTrue, bool IsAssuming); 504 505 bool patternMatch(const Expr *Ex, 506 const Expr *ParentEx, 507 raw_ostream &Out, 508 BugReporterContext &BRC, 509 PathSensitiveBugReport &R, 510 const ExplodedNode *N, 511 Optional<bool> &prunable, 512 bool IsSameFieldName); 513 514 static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece); 515 }; 516 517 /// Suppress reports that might lead to known false positives. 518 /// 519 /// Currently this suppresses reports based on locations of bugs. 520 class LikelyFalsePositiveSuppressionBRVisitor final 521 : public BugReporterVisitor { 522 public: 523 static void *getTag() { 524 static int Tag = 0; 525 return static_cast<void *>(&Tag); 526 } 527 528 void Profile(llvm::FoldingSetNodeID &ID) const override { 529 ID.AddPointer(getTag()); 530 } 531 532 PathDiagnosticPieceRef VisitNode(const ExplodedNode *, BugReporterContext &, 533 PathSensitiveBugReport &) override { 534 return nullptr; 535 } 536 537 void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, 538 PathSensitiveBugReport &BR) override; 539 }; 540 541 /// When a region containing undefined value or '0' value is passed 542 /// as an argument in a call, marks the call as interesting. 543 /// 544 /// As a result, BugReporter will not prune the path through the function even 545 /// if the region's contents are not modified/accessed by the call. 546 class UndefOrNullArgVisitor final : public BugReporterVisitor { 547 /// The interesting memory region this visitor is tracking. 548 const MemRegion *R; 549 550 public: 551 UndefOrNullArgVisitor(const MemRegion *InR) : R(InR) {} 552 553 void Profile(llvm::FoldingSetNodeID &ID) const override { 554 static int Tag = 0; 555 ID.AddPointer(&Tag); 556 ID.AddPointer(R); 557 } 558 559 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 560 BugReporterContext &BRC, 561 PathSensitiveBugReport &BR) override; 562 }; 563 564 class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { 565 /// The symbolic value for which we are tracking constraints. 566 /// This value is constrained to null in the end of path. 567 DefinedSVal V; 568 569 /// Track if we found the node where the constraint was first added. 570 bool IsSatisfied = false; 571 572 /// Since the visitors can be registered on nodes previous to the last 573 /// node in the BugReport, but the path traversal always starts with the last 574 /// node, the visitor invariant (that we start with a node in which V is null) 575 /// might not hold when node visitation starts. We are going to start tracking 576 /// from the last node in which the value is null. 577 bool IsTrackingTurnedOn = false; 578 579 public: 580 SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N); 581 582 void Profile(llvm::FoldingSetNodeID &ID) const override; 583 584 /// Return the tag associated with this visitor. This tag will be used 585 /// to make all PathDiagnosticPieces created by this visitor. 586 static const char *getTag(); 587 588 PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 589 BugReporterContext &BRC, 590 PathSensitiveBugReport &BR) override; 591 }; 592 593 /// The bug visitor will walk all the nodes in a path and collect all the 594 /// constraints. When it reaches the root node, will create a refutation 595 /// manager and check if the constraints are satisfiable 596 class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor { 597 private: 598 /// Holds the constraints in a given path 599 ConstraintMap Constraints; 600 601 public: 602 FalsePositiveRefutationBRVisitor(); 603 604 void Profile(llvm::FoldingSetNodeID &ID) const override; 605 606 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 607 BugReporterContext &BRC, 608 PathSensitiveBugReport &BR) override; 609 610 void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, 611 PathSensitiveBugReport &BR) override; 612 void addConstraints(const ExplodedNode *N, 613 bool OverwriteConstraintsOnExistingSyms); 614 }; 615 616 /// The visitor detects NoteTags and displays the event notes they contain. 617 class TagVisitor : public BugReporterVisitor { 618 public: 619 void Profile(llvm::FoldingSetNodeID &ID) const override; 620 621 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 622 BugReporterContext &BRC, 623 PathSensitiveBugReport &R) override; 624 }; 625 626 class ObjCMethodCall; 627 class CXXConstructorCall; 628 629 /// Put a diagnostic on return statement (or on } in its absence) of all inlined 630 /// functions for which some property remained unchanged. 631 /// Resulting diagnostics may read such as "Returning without writing to X". 632 /// 633 /// Descendants can define what a "state change is", like a change of value 634 /// to a memory region, liveness, etc. For function calls where the state did 635 /// not change as defined, a custom note may be constructed. 636 /// 637 /// For a minimal example, check out 638 /// clang/unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp. 639 class NoStateChangeFuncVisitor : public BugReporterVisitor { 640 private: 641 /// Frames modifying the state as defined in \c wasModifiedBeforeCallExit. 642 /// This visitor generates a note only if a function does *not* change the 643 /// state that way. This information is not immediately available 644 /// by looking at the node associated with the exit from the function 645 /// (usually the return statement). To avoid recomputing the same information 646 /// many times (going up the path for each node and checking whether the 647 /// region was written into) we instead lazily compute the stack frames 648 /// along the path. 649 // TODO: Can't we just use a map instead? This is likely not as cheap as it 650 // makes the code difficult to read. 651 llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifying; 652 llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated; 653 654 /// Check and lazily calculate whether the state is modified in the stack 655 /// frame to which \p CallExitBeginN belongs. 656 /// The calculation is cached in FramesModifying. 657 bool isModifiedInFrame(const ExplodedNode *CallExitBeginN); 658 659 void markFrameAsModifying(const StackFrameContext *SCtx); 660 661 /// Write to \c FramesModifying all stack frames along the path in the current 662 /// stack frame which modifies the state. 663 void findModifyingFrames(const ExplodedNode *const CallExitBeginN); 664 665 protected: 666 bugreporter::TrackingKind TKind; 667 668 /// \return Whether the state was modified from the current node, \p CurrN, to 669 /// the end of the stack frame, at \p CallExitBeginN. \p CurrN and 670 /// \p CallExitBeginN are always in the same stack frame. 671 /// Clients should override this callback when a state change is important 672 /// not only on the entire function call, but inside of it as well. 673 /// Example: we may want to leave a note about the lack of locking/unlocking 674 /// on a particular mutex, but not if inside the function its state was 675 /// changed, but also restored. wasModifiedInFunction() wouldn't know of this 676 /// change. 677 virtual bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN, 678 const ExplodedNode *CallExitBeginN) { 679 return false; 680 } 681 682 /// \return Whether the state was modified in the inlined function call in 683 /// between \p CallEnterN and \p CallExitEndN. Mind that the stack frame 684 /// retrieved from a CallEnterN and CallExitEndN is the *caller's* stack 685 /// frame! The inlined function's stack should be retrieved from either the 686 /// immediate successor to \p CallEnterN or immediate predecessor to 687 /// \p CallExitEndN. 688 /// Clients should override this function if a state changes local to the 689 /// inlined function are not interesting, only the change occuring as a 690 /// result of it. 691 /// Example: we want to leave a not about a leaked resource object not being 692 /// deallocated / its ownership changed inside a function, and we don't care 693 /// if it was assigned to a local variable (its change in ownership is 694 /// inconsequential). 695 virtual bool wasModifiedInFunction(const ExplodedNode *CallEnterN, 696 const ExplodedNode *CallExitEndN) { 697 return false; 698 } 699 700 /// Consume the information on the non-modifying stack frame in order to 701 /// either emit a note or not. May suppress the report entirely. 702 /// \return Diagnostics piece for the unmodified state in the current 703 /// function, if it decides to emit one. A good description might start with 704 /// "Returning without...". 705 virtual PathDiagnosticPieceRef 706 maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, 707 const ObjCMethodCall &Call, 708 const ExplodedNode *N) = 0; 709 710 /// Consume the information on the non-modifying stack frame in order to 711 /// either emit a note or not. May suppress the report entirely. 712 /// \return Diagnostics piece for the unmodified state in the current 713 /// function, if it decides to emit one. A good description might start with 714 /// "Returning without...". 715 virtual PathDiagnosticPieceRef 716 maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, 717 const CXXConstructorCall &Call, 718 const ExplodedNode *N) = 0; 719 720 /// Consume the information on the non-modifying stack frame in order to 721 /// either emit a note or not. May suppress the report entirely. 722 /// \return Diagnostics piece for the unmodified state in the current 723 /// function, if it decides to emit one. A good description might start with 724 /// "Returning without...". 725 virtual PathDiagnosticPieceRef 726 maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, 727 const ExplodedNode *N) = 0; 728 729 public: 730 NoStateChangeFuncVisitor(bugreporter::TrackingKind TKind) : TKind(TKind) {} 731 732 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 733 BugReporterContext &BR, 734 PathSensitiveBugReport &R) final; 735 }; 736 737 } // namespace ento 738 } // namespace clang 739 740 #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H 741