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