| //===- BugReporterVisitors.h - Generate PathDiagnostics ---------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file declares BugReporterVisitors, which are used to generate enhanced |
| // diagnostic traces. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H |
| #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H |
| |
| #include "clang/Analysis/ProgramPoint.h" |
| #include "clang/Basic/LLVM.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
| #include "llvm/ADT/FoldingSet.h" |
| #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "llvm/ADT/StringRef.h" |
| #include <list> |
| #include <memory> |
| #include <utility> |
| |
| namespace clang { |
| |
| class BinaryOperator; |
| class CFGBlock; |
| class DeclRefExpr; |
| class Expr; |
| class Stmt; |
| |
| namespace ento { |
| |
| class PathSensitiveBugReport; |
| class BugReporterContext; |
| class ExplodedNode; |
| class MemRegion; |
| class PathDiagnosticPiece; |
| using PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>; |
| |
| /// BugReporterVisitors are used to add custom diagnostics along a path. |
| class BugReporterVisitor : public llvm::FoldingSetNode { |
| public: |
| BugReporterVisitor() = default; |
| BugReporterVisitor(const BugReporterVisitor &) = default; |
| BugReporterVisitor(BugReporterVisitor &&) {} |
| virtual ~BugReporterVisitor(); |
| |
| /// Return a diagnostic piece which should be associated with the |
| /// given node. |
| /// Note that this function does *not* get run on the very last node |
| /// of the report, as the PathDiagnosticPiece associated with the |
| /// last node should be unique. |
| /// Use \ref getEndPath to customize the note associated with the report |
| /// end instead. |
| /// |
| /// The last parameter can be used to register a new visitor with the given |
| /// BugReport while processing a node. |
| virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR) = 0; |
| |
| /// Last function called on the visitor, no further calls to VisitNode |
| /// would follow. |
| virtual void finalizeVisitor(BugReporterContext &BRC, |
| const ExplodedNode *EndPathNode, |
| PathSensitiveBugReport &BR); |
| |
| /// Provide custom definition for the final diagnostic piece on the |
| /// path - the piece, which is displayed before the path is expanded. |
| /// |
| /// NOTE that this function can be implemented on at most one used visitor, |
| /// and otherwise it crahes at runtime. |
| virtual PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, |
| const ExplodedNode *N, |
| PathSensitiveBugReport &BR); |
| |
| virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; |
| |
| /// Generates the default final diagnostic piece. |
| static PathDiagnosticPieceRef |
| getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, |
| const PathSensitiveBugReport &BR); |
| }; |
| |
| namespace bugreporter { |
| |
| /// Specifies the type of tracking for an expression. |
| enum class TrackingKind { |
| /// Default tracking kind -- specifies that as much information should be |
| /// gathered about the tracked expression value as possible. |
| Thorough, |
| /// Specifies that a more moderate tracking should be used for the expression |
| /// value. This will essentially make sure that functions relevant to it |
| /// aren't pruned, but otherwise relies on the user reading the code or |
| /// following the arrows. |
| Condition |
| }; |
| |
| /// Defines a set of options altering tracking behavior. |
| struct TrackingOptions { |
| /// Specifies the kind of tracking. |
| TrackingKind Kind = TrackingKind::Thorough; |
| /// Specifies whether we should employ false positive suppression |
| /// (inlined defensive checks, returned null). |
| bool EnableNullFPSuppression = true; |
| }; |
| |
| /// Describes an event when the value got stored into a memory region. |
| /// |
| /// As opposed to checker checkBind API, it reacts also to binds |
| /// generated by the checker as well. It can be useful when the binding |
| /// happened as a result of evalCall, for example. |
| struct StoreInfo { |
| enum Kind { |
| /// The value got stored into the region during initialization: |
| /// int x = 42; |
| Initialization, |
| /// The value got stored into the region during assignment: |
| /// int x; |
| /// x = 42; |
| Assignment, |
| /// The value got stored into the parameter region as the result |
| /// of a call. |
| CallArgument, |
| /// The value got stored into the region as block capture. |
| /// Block data is modeled as a separate region, thus whenever |
| /// the analyzer sees a captured variable, its value is copied |
| /// into a special block region. |
| BlockCapture |
| }; |
| |
| /// The type of store operation. |
| Kind StoreKind; |
| /// The node where the store happened. |
| const ExplodedNode *StoreSite; |
| /// The expression where the value comes from. |
| /// NOTE: might be null. |
| const Expr *SourceOfTheValue; |
| /// Symbolic value that is being stored. |
| SVal Value; |
| /// Memory regions involved in the store operation. |
| /// Dest <- Origin |
| /// NOTE: Origin might be null, when the stored value doesn't come |
| /// from another region. |
| const MemRegion *Dest, *Origin; |
| }; |
| |
| class Tracker; |
| using TrackerRef = llvm::IntrusiveRefCntPtr<Tracker>; |
| |
| class ExpressionHandler; |
| class StoreHandler; |
| |
| /// A generalized component for tracking expressions, values, and stores. |
| /// |
| /// Tracker aimes at providing a sensible set of default behaviors that can be |
| /// used by any checker, while providing mechanisms to hook into any part of the |
| /// tracking process and insert checker-specific logic. |
| class Tracker : public llvm::RefCountedBase<Tracker> { |
| private: |
| using ExpressionHandlerPtr = std::unique_ptr<ExpressionHandler>; |
| using StoreHandlerPtr = std::unique_ptr<StoreHandler>; |
| |
| PathSensitiveBugReport &Report; |
| std::list<ExpressionHandlerPtr> ExpressionHandlers; |
| std::list<StoreHandlerPtr> StoreHandlers; |
| |
| protected: |
| /// \param Report The bug report to which visitors should be attached. |
| Tracker(PathSensitiveBugReport &Report); |
| |
| public: |
| virtual ~Tracker() = default; |
| |
| static TrackerRef create(PathSensitiveBugReport &Report) { |
| return new Tracker(Report); |
| } |
| |
| PathSensitiveBugReport &getReport() { return Report; } |
| |
| /// Describes a tracking result with the most basic information of what was |
| /// actually done (or not done). |
| struct Result { |
| /// Usually it means that the tracker added visitors. |
| bool FoundSomethingToTrack = false; |
| /// Signifies that the tracking was interrupted at some point. |
| /// Usually this information is important only for sub-trackers. |
| bool WasInterrupted = false; |
| |
| /// Combines the current result with the given result. |
| void combineWith(const Result &Other) { |
| // If we found something in one of the cases, we can |
| // say we found something overall. |
| FoundSomethingToTrack |= Other.FoundSomethingToTrack; |
| // The same goes to the interruption. |
| WasInterrupted |= Other.WasInterrupted; |
| } |
| }; |
| |
| /// Track expression value back to its point of origin. |
| /// |
| /// \param E The expression value which we are tracking |
| /// \param N A node "downstream" from the evaluation of the statement. |
| /// \param Opts Tracking options specifying how we want to track the value. |
| virtual Result track(const Expr *E, const ExplodedNode *N, |
| TrackingOptions Opts = {}); |
| |
| /// Track how the value got stored into the given region and where it came |
| /// from. |
| /// |
| /// \param V We're searching for the store where \c R received this value. |
| /// \param R The region we're tracking. |
| /// \param Opts Tracking options specifying how we want to track the value. |
| /// \param Origin Only adds notes when the last store happened in a |
| /// different stackframe to this one. Disregarded if the tracking kind |
| /// is thorough. |
| /// This is useful, because for non-tracked regions, notes about |
| /// changes to its value in a nested stackframe could be pruned, and |
| /// this visitor can prevent that without polluting the bugpath too |
| /// much. |
| virtual Result track(SVal V, const MemRegion *R, TrackingOptions Opts = {}, |
| const StackFrameContext *Origin = nullptr); |
| |
| /// Handle the store operation and produce the note. |
| /// |
| /// \param SI The information fully describing the store. |
| /// \param Opts Tracking options specifying how we got to it. |
| /// |
| /// NOTE: this method is designed for sub-trackers and visitors. |
| virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC, |
| TrackingOptions Opts); |
| |
| /// Add custom expression handler with the highest priority. |
| /// |
| /// It means that it will be asked for handling first, and can prevent |
| /// other handlers from running if decides to interrupt. |
| void addHighPriorityHandler(ExpressionHandlerPtr SH) { |
| ExpressionHandlers.push_front(std::move(SH)); |
| } |
| |
| /// Add custom expression handler with the lowest priority. |
| /// |
| /// It means that it will be asked for handling last, and other handlers can |
| /// prevent it from running if any of them decides to interrupt. |
| void addLowPriorityHandler(ExpressionHandlerPtr SH) { |
| ExpressionHandlers.push_back(std::move(SH)); |
| } |
| |
| /// Add custom store handler with the highest priority. |
| /// |
| /// It means that it will be asked for handling first, and will prevent |
| /// other handlers from running if it produces non-null note. |
| void addHighPriorityHandler(StoreHandlerPtr SH) { |
| StoreHandlers.push_front(std::move(SH)); |
| } |
| |
| /// Add custom store handler with the lowest priority. |
| /// |
| /// It means that it will be asked for handling last, only |
| /// if all other handlers failed to produce the note. |
| void addLowPriorityHandler(StoreHandlerPtr SH) { |
| StoreHandlers.push_back(std::move(SH)); |
| } |
| |
| /// Add custom expression/store handler with the highest priority |
| /// |
| /// See other overloads for explanation. |
| template <class HandlerType, class... Args> |
| void addHighPriorityHandler(Args &&... ConstructorArgs) { |
| addHighPriorityHandler(std::make_unique<HandlerType>( |
| *this, std::forward<Args>(ConstructorArgs)...)); |
| } |
| |
| /// Add custom expression/store handler with the lowest priority |
| /// |
| /// See other overloads for explanation. |
| template <class HandlerType, class... Args> |
| void addLowPriorityHandler(Args &&... ConstructorArgs) { |
| addLowPriorityHandler(std::make_unique<HandlerType>( |
| *this, std::forward<Args>(ConstructorArgs)...)); |
| } |
| }; |
| |
| /// Handles expressions during the tracking. |
| class ExpressionHandler { |
| private: |
| Tracker &ParentTracker; |
| |
| public: |
| ExpressionHandler(Tracker &ParentTracker) : ParentTracker(ParentTracker) {} |
| virtual ~ExpressionHandler() {} |
| |
| /// Handle the given expression from the given node. |
| /// |
| /// \param E The expression value which we are tracking |
| /// \param Original A node "downstream" where the tracking started. |
| /// \param ExprNode A node where the evaluation of \c E actually happens. |
| /// \param Opts Tracking options specifying how we are tracking the value. |
| virtual Tracker::Result handle(const Expr *E, const ExplodedNode *Original, |
| const ExplodedNode *ExprNode, |
| TrackingOptions Opts) = 0; |
| |
| /// \Return the tracker that initiated the process. |
| Tracker &getParentTracker() { return ParentTracker; } |
| }; |
| |
| /// Handles stores during the tracking. |
| class StoreHandler { |
| private: |
| Tracker &ParentTracker; |
| |
| public: |
| StoreHandler(Tracker &ParentTracker) : ParentTracker(ParentTracker) {} |
| virtual ~StoreHandler() {} |
| |
| /// Handle the given store and produce the node. |
| /// |
| /// \param SI The information fully describing the store. |
| /// \param Opts Tracking options specifying how we are tracking the value. |
| /// |
| /// \return the produced note, null if the handler doesn't support this kind |
| /// of stores. |
| virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC, |
| TrackingOptions Opts) = 0; |
| |
| Tracker &getParentTracker() { return ParentTracker; } |
| |
| protected: |
| PathDiagnosticPieceRef constructNote(StoreInfo SI, BugReporterContext &BRC, |
| StringRef NodeText); |
| }; |
| |
| /// Visitor that tracks expressions and values. |
| class TrackingBugReporterVisitor : public BugReporterVisitor { |
| private: |
| TrackerRef ParentTracker; |
| |
| public: |
| TrackingBugReporterVisitor(TrackerRef ParentTracker) |
| : ParentTracker(ParentTracker) {} |
| |
| Tracker &getParentTracker() { return *ParentTracker; } |
| }; |
| |
| /// Attempts to add visitors to track expression value back to its point of |
| /// origin. |
| /// |
| /// \param N A node "downstream" from the evaluation of the statement. |
| /// \param E The expression value which we are tracking |
| /// \param R The bug report to which visitors should be attached. |
| /// \param Opts Tracking options specifying how we are tracking the value. |
| /// |
| /// \return Whether or not the function was able to add visitors for this |
| /// statement. Note that returning \c true does not actually imply |
| /// that any visitors were added. |
| bool trackExpressionValue(const ExplodedNode *N, const Expr *E, |
| PathSensitiveBugReport &R, TrackingOptions Opts = {}); |
| |
| /// Track how the value got stored into the given region and where it came |
| /// from. |
| /// |
| /// \param V We're searching for the store where \c R received this value. |
| /// \param R The region we're tracking. |
| /// \param Opts Tracking options specifying how we want to track the value. |
| /// \param Origin Only adds notes when the last store happened in a |
| /// different stackframe to this one. Disregarded if the tracking kind |
| /// is thorough. |
| /// This is useful, because for non-tracked regions, notes about |
| /// changes to its value in a nested stackframe could be pruned, and |
| /// this visitor can prevent that without polluting the bugpath too |
| /// much. |
| void trackStoredValue(KnownSVal V, const MemRegion *R, |
| PathSensitiveBugReport &Report, TrackingOptions Opts = {}, |
| const StackFrameContext *Origin = nullptr); |
| |
| const Expr *getDerefExpr(const Stmt *S); |
| |
| } // namespace bugreporter |
| |
| class TrackConstraintBRVisitor final : public BugReporterVisitor { |
| DefinedSVal Constraint; |
| bool Assumption; |
| bool IsSatisfied = false; |
| bool IsZeroCheck; |
| |
| /// We should start tracking from the last node along the path in which the |
| /// value is constrained. |
| bool IsTrackingTurnedOn = false; |
| |
| public: |
| TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) |
| : Constraint(constraint), Assumption(assumption), |
| IsZeroCheck(!Assumption && Constraint.getAs<Loc>()) {} |
| |
| void Profile(llvm::FoldingSetNodeID &ID) const override; |
| |
| /// Return the tag associated with this visitor. This tag will be used |
| /// to make all PathDiagnosticPieces created by this visitor. |
| static const char *getTag(); |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR) override; |
| |
| private: |
| /// Checks if the constraint is valid in the current state. |
| bool isUnderconstrained(const ExplodedNode *N) const; |
| }; |
| |
| /// \class NilReceiverBRVisitor |
| /// Prints path notes when a message is sent to a nil receiver. |
| class NilReceiverBRVisitor final : public BugReporterVisitor { |
| public: |
| void Profile(llvm::FoldingSetNodeID &ID) const override { |
| static int x = 0; |
| ID.AddPointer(&x); |
| } |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR) override; |
| |
| /// If the statement is a message send expression with nil receiver, returns |
| /// the receiver expression. Returns NULL otherwise. |
| static const Expr *getNilReceiver(const Stmt *S, const ExplodedNode *N); |
| }; |
| |
| /// Visitor that tries to report interesting diagnostics from conditions. |
| class ConditionBRVisitor final : public BugReporterVisitor { |
| // FIXME: constexpr initialization isn't supported by MSVC2013. |
| constexpr static llvm::StringLiteral GenericTrueMessage = |
| "Assuming the condition is true"; |
| constexpr static llvm::StringLiteral GenericFalseMessage = |
| "Assuming the condition is false"; |
| |
| public: |
| void Profile(llvm::FoldingSetNodeID &ID) const override { |
| static int x = 0; |
| ID.AddPointer(&x); |
| } |
| |
| /// Return the tag associated with this visitor. This tag will be used |
| /// to make all PathDiagnosticPieces created by this visitor. |
| static const char *getTag(); |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR) override; |
| |
| PathDiagnosticPieceRef VisitNodeImpl(const ExplodedNode *N, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR); |
| |
| PathDiagnosticPieceRef |
| VisitTerminator(const Stmt *Term, const ExplodedNode *N, |
| const CFGBlock *SrcBlk, const CFGBlock *DstBlk, |
| PathSensitiveBugReport &R, BugReporterContext &BRC); |
| |
| PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &R, |
| const ExplodedNode *N, bool TookTrue); |
| |
| PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &R, |
| const ExplodedNode *N, bool TookTrue, |
| bool IsAssuming); |
| |
| PathDiagnosticPieceRef |
| VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, |
| BugReporterContext &BRC, PathSensitiveBugReport &R, |
| const ExplodedNode *N, bool TookTrue, bool IsAssuming); |
| |
| PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const MemberExpr *ME, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &R, |
| const ExplodedNode *N, bool TookTrue, |
| bool IsAssuming); |
| |
| PathDiagnosticPieceRef |
| VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr, |
| BugReporterContext &BRC, PathSensitiveBugReport &R, |
| const ExplodedNode *N, bool TookTrue); |
| |
| /// Tries to print the value of the given expression. |
| /// |
| /// \param CondVarExpr The expression to print its value. |
| /// \param Out The stream to print. |
| /// \param N The node where we encountered the condition. |
| /// \param TookTrue Whether we took the \c true branch of the condition. |
| /// |
| /// \return Whether the print was successful. (The printing is successful if |
| /// we model the value and we could obtain it.) |
| bool printValue(const Expr *CondVarExpr, raw_ostream &Out, |
| const ExplodedNode *N, bool TookTrue, bool IsAssuming); |
| |
| bool patternMatch(const Expr *Ex, |
| const Expr *ParentEx, |
| raw_ostream &Out, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &R, |
| const ExplodedNode *N, |
| Optional<bool> &prunable, |
| bool IsSameFieldName); |
| |
| static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece); |
| }; |
| |
| /// Suppress reports that might lead to known false positives. |
| /// |
| /// Currently this suppresses reports based on locations of bugs. |
| class LikelyFalsePositiveSuppressionBRVisitor final |
| : public BugReporterVisitor { |
| public: |
| static void *getTag() { |
| static int Tag = 0; |
| return static_cast<void *>(&Tag); |
| } |
| |
| void Profile(llvm::FoldingSetNodeID &ID) const override { |
| ID.AddPointer(getTag()); |
| } |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *, BugReporterContext &, |
| PathSensitiveBugReport &) override { |
| return nullptr; |
| } |
| |
| void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, |
| PathSensitiveBugReport &BR) override; |
| }; |
| |
| /// When a region containing undefined value or '0' value is passed |
| /// as an argument in a call, marks the call as interesting. |
| /// |
| /// As a result, BugReporter will not prune the path through the function even |
| /// if the region's contents are not modified/accessed by the call. |
| class UndefOrNullArgVisitor final : public BugReporterVisitor { |
| /// The interesting memory region this visitor is tracking. |
| const MemRegion *R; |
| |
| public: |
| UndefOrNullArgVisitor(const MemRegion *InR) : R(InR) {} |
| |
| void Profile(llvm::FoldingSetNodeID &ID) const override { |
| static int Tag = 0; |
| ID.AddPointer(&Tag); |
| ID.AddPointer(R); |
| } |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR) override; |
| }; |
| |
| class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { |
| /// The symbolic value for which we are tracking constraints. |
| /// This value is constrained to null in the end of path. |
| DefinedSVal V; |
| |
| /// Track if we found the node where the constraint was first added. |
| bool IsSatisfied = false; |
| |
| /// Since the visitors can be registered on nodes previous to the last |
| /// node in the BugReport, but the path traversal always starts with the last |
| /// node, the visitor invariant (that we start with a node in which V is null) |
| /// might not hold when node visitation starts. We are going to start tracking |
| /// from the last node in which the value is null. |
| bool IsTrackingTurnedOn = false; |
| |
| public: |
| SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N); |
| |
| void Profile(llvm::FoldingSetNodeID &ID) const override; |
| |
| /// Return the tag associated with this visitor. This tag will be used |
| /// to make all PathDiagnosticPieces created by this visitor. |
| static const char *getTag(); |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR) override; |
| }; |
| |
| /// The bug visitor will walk all the nodes in a path and collect all the |
| /// constraints. When it reaches the root node, will create a refutation |
| /// manager and check if the constraints are satisfiable |
| class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor { |
| private: |
| /// Holds the constraints in a given path |
| ConstraintMap Constraints; |
| |
| public: |
| FalsePositiveRefutationBRVisitor(); |
| |
| void Profile(llvm::FoldingSetNodeID &ID) const override; |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &BR) override; |
| |
| void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, |
| PathSensitiveBugReport &BR) override; |
| void addConstraints(const ExplodedNode *N, |
| bool OverwriteConstraintsOnExistingSyms); |
| }; |
| |
| /// The visitor detects NoteTags and displays the event notes they contain. |
| class TagVisitor : public BugReporterVisitor { |
| public: |
| void Profile(llvm::FoldingSetNodeID &ID) const override; |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| BugReporterContext &BRC, |
| PathSensitiveBugReport &R) override; |
| }; |
| |
| class ObjCMethodCall; |
| class CXXConstructorCall; |
| |
| /// Put a diagnostic on return statement (or on } in its absence) of all inlined |
| /// functions for which some property remained unchanged. |
| /// Resulting diagnostics may read such as "Returning without writing to X". |
| /// |
| /// Descendants can define what a "state change is", like a change of value |
| /// to a memory region, liveness, etc. For function calls where the state did |
| /// not change as defined, a custom note may be constructed. |
| /// |
| /// For a minimal example, check out |
| /// clang/unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp. |
| class NoStateChangeFuncVisitor : public BugReporterVisitor { |
| private: |
| /// Frames modifying the state as defined in \c wasModifiedBeforeCallExit. |
| /// This visitor generates a note only if a function does *not* change the |
| /// state that way. This information is not immediately available |
| /// by looking at the node associated with the exit from the function |
| /// (usually the return statement). To avoid recomputing the same information |
| /// many times (going up the path for each node and checking whether the |
| /// region was written into) we instead lazily compute the stack frames |
| /// along the path. |
| // TODO: Can't we just use a map instead? This is likely not as cheap as it |
| // makes the code difficult to read. |
| llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifying; |
| llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated; |
| |
| /// Check and lazily calculate whether the state is modified in the stack |
| /// frame to which \p CallExitBeginN belongs. |
| /// The calculation is cached in FramesModifying. |
| bool isModifiedInFrame(const ExplodedNode *CallExitBeginN); |
| |
| void markFrameAsModifying(const StackFrameContext *SCtx); |
| |
| /// Write to \c FramesModifying all stack frames along the path in the current |
| /// stack frame which modifies the state. |
| void findModifyingFrames(const ExplodedNode *const CallExitBeginN); |
| |
| protected: |
| bugreporter::TrackingKind TKind; |
| |
| /// \return Whether the state was modified from the current node, \p CurrN, to |
| /// the end of the stack frame, at \p CallExitBeginN. \p CurrN and |
| /// \p CallExitBeginN are always in the same stack frame. |
| /// Clients should override this callback when a state change is important |
| /// not only on the entire function call, but inside of it as well. |
| /// Example: we may want to leave a note about the lack of locking/unlocking |
| /// on a particular mutex, but not if inside the function its state was |
| /// changed, but also restored. wasModifiedInFunction() wouldn't know of this |
| /// change. |
| virtual bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN, |
| const ExplodedNode *CallExitBeginN) { |
| return false; |
| } |
| |
| /// \return Whether the state was modified in the inlined function call in |
| /// between \p CallEnterN and \p CallExitEndN. Mind that the stack frame |
| /// retrieved from a CallEnterN and CallExitEndN is the *caller's* stack |
| /// frame! The inlined function's stack should be retrieved from either the |
| /// immediate successor to \p CallEnterN or immediate predecessor to |
| /// \p CallExitEndN. |
| /// Clients should override this function if a state changes local to the |
| /// inlined function are not interesting, only the change occuring as a |
| /// result of it. |
| /// Example: we want to leave a not about a leaked resource object not being |
| /// deallocated / its ownership changed inside a function, and we don't care |
| /// if it was assigned to a local variable (its change in ownership is |
| /// inconsequential). |
| virtual bool wasModifiedInFunction(const ExplodedNode *CallEnterN, |
| const ExplodedNode *CallExitEndN) { |
| return false; |
| } |
| |
| /// Consume the information on the non-modifying stack frame in order to |
| /// either emit a note or not. May suppress the report entirely. |
| /// \return Diagnostics piece for the unmodified state in the current |
| /// function, if it decides to emit one. A good description might start with |
| /// "Returning without...". |
| virtual PathDiagnosticPieceRef |
| maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, |
| const ObjCMethodCall &Call, |
| const ExplodedNode *N) = 0; |
| |
| /// Consume the information on the non-modifying stack frame in order to |
| /// either emit a note or not. May suppress the report entirely. |
| /// \return Diagnostics piece for the unmodified state in the current |
| /// function, if it decides to emit one. A good description might start with |
| /// "Returning without...". |
| virtual PathDiagnosticPieceRef |
| maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, |
| const CXXConstructorCall &Call, |
| const ExplodedNode *N) = 0; |
| |
| /// Consume the information on the non-modifying stack frame in order to |
| /// either emit a note or not. May suppress the report entirely. |
| /// \return Diagnostics piece for the unmodified state in the current |
| /// function, if it decides to emit one. A good description might start with |
| /// "Returning without...". |
| virtual PathDiagnosticPieceRef |
| maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, |
| const ExplodedNode *N) = 0; |
| |
| public: |
| NoStateChangeFuncVisitor(bugreporter::TrackingKind TKind) : TKind(TKind) {} |
| |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| BugReporterContext &BR, |
| PathSensitiveBugReport &R) override final; |
| }; |
| |
| } // namespace ento |
| } // namespace clang |
| |
| #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H |