diff options
Diffstat (limited to 'include/clang/StaticAnalyzer')
22 files changed, 924 insertions, 1460 deletions
diff --git a/include/clang/StaticAnalyzer/Checkers/Checkers.td b/include/clang/StaticAnalyzer/Checkers/Checkers.td index 2b29efba66a4..4d52655045b3 100644 --- a/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -504,6 +504,15 @@ def MoveChecker: Checker<"Move">, ]>, Documentation<HasDocumentation>; +def VirtualCallModeling : Checker<"VirtualCallModeling">, + HelpText<"Auxiliary modeling for the virtual method call checkers">, + Documentation<NotDocumented>, + Hidden; + +def PureVirtualCallChecker : Checker<"PureVirtualCall">, + HelpText<"Check pure virtual function calls during construction/destruction">, + Dependencies<[VirtualCallModeling]>, + Documentation<HasDocumentation>; } // end: "cplusplus" let ParentPackage = CplusplusOptIn in { @@ -552,14 +561,22 @@ def UninitializedObjectChecker: Checker<"UninitializedObject">, Documentation<HasAlphaDocumentation>; def VirtualCallChecker : Checker<"VirtualCall">, - HelpText<"Check virtual function calls during construction or destruction">, + HelpText<"Check virtual function calls during construction/destruction">, CheckerOptions<[ CmdLineOption<Boolean, + "ShowFixIts", + "Enable fix-it hints for this checker", + "false", + InAlpha>, + CmdLineOption<Boolean, "PureOnly", - "Whether to only report calls to pure virtual methods.", + "Disables the checker. Keeps cplusplus.PureVirtualCall " + "enabled. This option is only provided for backwards " + "compatibility.", "false", - Released> + InAlpha> ]>, + Dependencies<[VirtualCallModeling]>, Documentation<HasDocumentation>; } // end: "optin.cplusplus" @@ -636,6 +653,19 @@ let ParentPackage = DeadCode in { def DeadStoresChecker : Checker<"DeadStores">, HelpText<"Check for values stored to variables that are never read " "afterwards">, + CheckerOptions<[ + CmdLineOption<Boolean, + "WarnForDeadNestedAssignments", + "Warns for deadstores in nested assignments." + "E.g.: if ((P = f())) where P is unused.", + "true", + Released>, + CmdLineOption<Boolean, + "ShowFixIts", + "Enable fix-it hints for this checker", + "false", + InAlpha> + ]>, Documentation<HasDocumentation>; } // end DeadCode @@ -799,6 +829,13 @@ let ParentPackage = Taint in { def GenericTaintChecker : Checker<"TaintPropagation">, HelpText<"Generate taint information used by other checkers">, + CheckerOptions<[ + CmdLineOption<String, + "Config", + "Specifies the name of the configuration file.", + "", + InAlpha>, + ]>, Documentation<HasAlphaDocumentation>; } // end "alpha.security.taint" diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def index 70bd476b6c43..d853fb74f9c7 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -294,12 +294,20 @@ ANALYZER_OPTION(bool, DisplayCTUProgress, "display-ctu-progress", ANALYZER_OPTION(bool, ShouldTrackConditions, "track-conditions", "Whether to track conditions that are a control dependency of " "an already tracked variable.", - false) + true) ANALYZER_OPTION(bool, ShouldTrackConditionsDebug, "track-conditions-debug", "Whether to place an event at each tracked condition.", false) +ANALYZER_OPTION(bool, ShouldEmitFixItHintsAsRemarks, "fixits-as-remarks", + "Emit fix-it hints as remarks for testing purposes", + false) + +//===----------------------------------------------------------------------===// +// Unsigned analyzer options. +//===----------------------------------------------------------------------===// + ANALYZER_OPTION(unsigned, CTUImportThreshold, "ctu-import-threshold", "The maximal amount of translation units that is considered " "for import when inlining functions during CTU analysis. " @@ -308,10 +316,6 @@ ANALYZER_OPTION(unsigned, CTUImportThreshold, "ctu-import-threshold", "various translation units.", 100u) -//===----------------------------------------------------------------------===// -// Unsinged analyzer options. -//===----------------------------------------------------------------------===// - ANALYZER_OPTION( unsigned, AlwaysInlineSize, "ipa-always-inline-size", "The size of the functions (in basic blocks), which should be considered " @@ -380,12 +384,6 @@ ANALYZER_OPTION( "Value: \"constructors\", \"destructors\", \"methods\".", "destructors") -ANALYZER_OPTION_DEPENDS_ON_USER_MODE( - StringRef, IPAMode, "ipa", - "Controls the mode of inter-procedural analysis. Value: \"none\", " - "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".", - /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate") - ANALYZER_OPTION( StringRef, ExplorationStrategy, "exploration_strategy", "Value: \"dfs\", \"bfs\", \"unexplored_first\", " @@ -393,5 +391,17 @@ ANALYZER_OPTION( "\"bfs_block_dfs_contents\".", "unexplored_first_queue") +ANALYZER_OPTION( + StringRef, RawSilencedCheckersAndPackages, "silence-checkers", + "A semicolon separated list of checker and package names to silence. " + "Silenced checkers will not emit reports, but the modeling remain enabled.", + "") + +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( + StringRef, IPAMode, "ipa", + "Controls the mode of inter-procedural analysis. Value: \"none\", " + "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".", + /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate") + #undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE #undef ANALYZER_OPTION diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 9630a229bd3b..ce16095e10c0 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -163,9 +163,16 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> { public: using ConfigTable = llvm::StringMap<std::string>; + /// Retrieves the list of checkers generated from Checkers.td. This doesn't + /// contain statically linked but non-generated checkers and plugin checkers! static std::vector<StringRef> getRegisteredCheckers(bool IncludeExperimental = false); + /// Retrieves the list of packages generated from Checkers.td. This doesn't + /// contain statically linked but non-generated packages and plugin packages! + static std::vector<StringRef> + getRegisteredPackages(bool IncludeExperimental = false); + /// Convenience function for printing options or checkers and their /// description in a formatted manner. If \p MinLineWidth is set to 0, no line /// breaks are introduced for the description. @@ -188,9 +195,11 @@ public: std::pair<StringRef, StringRef> EntryDescPair, size_t EntryWidth, size_t InitialPad, size_t MinLineWidth = 0); + /// Pairs of checker/package name and enable/disable. + std::vector<std::pair<std::string, bool>> CheckersAndPackages; - /// Pair of checker name and enable/disable. - std::vector<std::pair<std::string, bool>> CheckersControlList; + /// Vector of checker/package names which will not emit warnings. + std::vector<std::string> SilencedCheckersAndPackages; /// A key-value table of use-specified configuration values. // TODO: This shouldn't be public. @@ -212,12 +221,12 @@ public: /// The maximum number of times the analyzer visits a block. unsigned maxBlockVisitOnPath; - /// Disable all analyzer checks. + /// Disable all analyzer checkers. /// - /// This flag allows one to disable analyzer checks on the code processed by + /// This flag allows one to disable analyzer checkers on the code processed by /// the given analysis consumer. Note, the code will get parsed and the /// command-line options will get checked. - unsigned DisableAllChecks : 1; + unsigned DisableAllCheckers : 1; unsigned ShowCheckerHelp : 1; unsigned ShowCheckerHelpAlpha : 1; @@ -269,13 +278,13 @@ public: // Create an array of all -analyzer-config command line options. Sort it in // the constructor. - std::vector<StringRef> AnalyzerConfigCmdFlags = { + std::vector<llvm::StringLiteral> AnalyzerConfigCmdFlags = { #define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ SHALLOW_VAL, DEEP_VAL) \ ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) #define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ - CMDFLAG, + llvm::StringLiteral(CMDFLAG), #include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" #undef ANALYZER_OPTION @@ -292,7 +301,7 @@ public: } AnalyzerOptions() - : DisableAllChecks(false), ShowCheckerHelp(false), + : DisableAllCheckers(false), ShowCheckerHelp(false), ShowCheckerHelpAlpha(false), ShowCheckerHelpDeveloper(false), ShowCheckerOptionList(false), ShowCheckerOptionAlphaList(false), ShowCheckerOptionDeveloperList(false), ShowEnabledCheckerList(false), @@ -310,7 +319,7 @@ public: /// If an option value is not provided, returns the given \p DefaultVal. /// @param [in] CheckerName The *full name* of the checker. One may retrieve /// this from the checker object's field \c Name, or through \c - /// CheckerManager::getCurrentCheckName within the checker's registry + /// CheckerManager::getCurrentCheckerName within the checker's registry /// function. /// Checker options are retrieved in the following format: /// `-analyzer-config CheckerName:OptionName=Value. @@ -330,7 +339,7 @@ public: /// If an option value is not provided, returns the given \p DefaultVal. /// @param [in] CheckerName The *full name* of the checker. One may retrieve /// this from the checker object's field \c Name, or through \c - /// CheckerManager::getCurrentCheckName within the checker's registry + /// CheckerManager::getCurrentCheckerName within the checker's registry /// function. /// Checker options are retrieved in the following format: /// `-analyzer-config CheckerName:OptionName=Value. @@ -350,7 +359,7 @@ public: /// If an option value is not provided, returns the given \p DefaultVal. /// @param [in] CheckerName The *full name* of the checker. One may retrieve /// this from the checker object's field \c Name, or through \c - /// CheckerManager::getCurrentCheckName within the checker's registry + /// CheckerManager::getCurrentCheckerName within the checker's registry /// function. /// Checker options are retrieved in the following format: /// `-analyzer-config CheckerName:OptionName=Value. @@ -404,6 +413,43 @@ inline UserModeKind AnalyzerOptions::getUserMode() const { return K.getValue(); } +inline std::vector<StringRef> +AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental) { + static constexpr llvm::StringLiteral StaticAnalyzerCheckerNames[] = { +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ + llvm::StringLiteral(FULLNAME), +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + }; + std::vector<StringRef> Checkers; + for (StringRef CheckerName : StaticAnalyzerCheckerNames) { + if (!CheckerName.startswith("debug.") && + (IncludeExperimental || !CheckerName.startswith("alpha."))) + Checkers.push_back(CheckerName); + } + return Checkers; +} + +inline std::vector<StringRef> +AnalyzerOptions::getRegisteredPackages(bool IncludeExperimental) { + static constexpr llvm::StringLiteral StaticAnalyzerPackageNames[] = { +#define GET_PACKAGES +#define PACKAGE(FULLNAME) llvm::StringLiteral(FULLNAME), +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef PACKAGE +#undef GET_PACKAGES + }; + std::vector<StringRef> Packages; + for (StringRef PackageName : StaticAnalyzerPackageNames) { + if (PackageName != "debug" && + (IncludeExperimental || PackageName != "alpha")) + Packages.push_back(PackageName); + } + return Packages; +} + } // namespace clang #endif // LLVM_CLANG_STATICANALYZER_CORE_ANALYZEROPTIONS_H diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index d30ad19b20f8..e94b544172a0 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -14,10 +14,10 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" @@ -70,59 +70,253 @@ class SValBuilder; using DiagnosticForConsumerMapTy = llvm::DenseMap<PathDiagnosticConsumer *, std::unique_ptr<PathDiagnostic>>; -/// This class provides an interface through which checkers can create -/// individual bug reports. -class BugReport : public llvm::ilist_node<BugReport> { +/// Interface for classes constructing Stack hints. +/// +/// If a PathDiagnosticEvent occurs in a different frame than the final +/// diagnostic the hints can be used to summarize the effect of the call. +class StackHintGenerator { public: - class NodeResolver { - virtual void anchor(); + virtual ~StackHintGenerator() = 0; - public: - virtual ~NodeResolver() = default; + /// Construct the Diagnostic message for the given ExplodedNode. + virtual std::string getMessage(const ExplodedNode *N) = 0; +}; - virtual const ExplodedNode* - getOriginalNode(const ExplodedNode *N) = 0; - }; +/// Constructs a Stack hint for the given symbol. +/// +/// The class knows how to construct the stack hint message based on +/// traversing the CallExpr associated with the call and checking if the given +/// symbol is returned or is one of the arguments. +/// The hint can be customized by redefining 'getMessageForX()' methods. +class StackHintGeneratorForSymbol : public StackHintGenerator { +private: + SymbolRef Sym; + std::string Msg; - using ranges_iterator = const SourceRange *; - using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>; - using visitor_iterator = VisitorList::iterator; - using ExtraTextList = SmallVector<StringRef, 2>; - using NoteList = SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4>; +public: + StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} + ~StackHintGeneratorForSymbol() override = default; + + /// Search the call expression for the symbol Sym and dispatch the + /// 'getMessageForX()' methods to construct a specific message. + std::string getMessage(const ExplodedNode *N) override; + + /// Produces the message of the following form: + /// 'Msg via Nth parameter' + virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); + + virtual std::string getMessageForReturn(const CallExpr *CallExpr) { + return Msg; + } + + virtual std::string getMessageForSymbolNotFound() { + return Msg; + } +}; + +/// This class provides an interface through which checkers can create +/// individual bug reports. +class BugReport { +public: + enum class Kind { Basic, PathSensitive }; protected: friend class BugReportEquivClass; friend class BugReporter; + Kind K; const BugType& BT; - const Decl *DeclWithIssue = nullptr; std::string ShortDescription; std::string Description; + + SmallVector<SourceRange, 4> Ranges; + SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4> Notes; + SmallVector<FixItHint, 4> Fixits; + + BugReport(Kind kind, const BugType &bt, StringRef desc) + : K(kind), BT(bt), Description(desc) {} + + BugReport(Kind K, const BugType &BT, StringRef ShortDescription, + StringRef Description) + : K(K), BT(BT), ShortDescription(ShortDescription), + Description(Description) {} + +public: + virtual ~BugReport() = default; + + Kind getKind() const { return K; } + + const BugType& getBugType() const { return BT; } + + /// A verbose warning message that is appropriate for displaying next to + /// the source code that introduces the problem. The description should be + /// at least a full sentence starting with a capital letter. The period at + /// the end of the warning is traditionally omitted. If the description + /// consists of multiple sentences, periods between the sentences are + /// encouraged, but the period at the end of the description is still omitted. + StringRef getDescription() const { return Description; } + + /// A short general warning message that is appropriate for displaying in + /// the list of all reported bugs. It should describe what kind of bug is found + /// but does not need to try to go into details of that specific bug. + /// Grammatical conventions of getDescription() apply here as well. + StringRef getShortDescription(bool UseFallback = true) const { + if (ShortDescription.empty() && UseFallback) + return Description; + return ShortDescription; + } + + /// The primary location of the bug report that points at the undesirable + /// behavior in the code. UIs should attach the warning description to this + /// location. The warning description should describe the bad behavior + /// at this location. + virtual PathDiagnosticLocation getLocation() const = 0; + + /// The smallest declaration that contains the bug location. + /// This is purely cosmetic; the declaration can be displayed to the user + /// but it does not affect whether the report is emitted. + virtual const Decl *getDeclWithIssue() const = 0; + + /// Get the location on which the report should be uniqued. Two warnings are + /// considered to be equivalent whenever they have the same bug types, + /// descriptions, and uniqueing locations. Out of a class of equivalent + /// warnings only one gets displayed to the user. For most warnings the + /// uniqueing location coincides with their location, but sometimes + /// it makes sense to use different locations. For example, a leak + /// checker can place the warning at the location where the last reference + /// to the leaking resource is dropped but at the same time unique the warning + /// by where that resource is acquired (allocated). + virtual PathDiagnosticLocation getUniqueingLocation() const = 0; + + /// Get the declaration that corresponds to (usually contains) the uniqueing + /// location. This is not actively used for uniqueing, i.e. otherwise + /// identical reports that have different uniqueing decls will be considered + /// equivalent. + virtual const Decl *getUniqueingDecl() const = 0; + + /// Add new item to the list of additional notes that need to be attached to + /// this report. If the report is path-sensitive, these notes will not be + /// displayed as part of the execution path explanation, but will be displayed + /// separately. Use bug visitors if you need to add an extra path note. + void addNote(StringRef Msg, const PathDiagnosticLocation &Pos, + ArrayRef<SourceRange> Ranges = {}) { + auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg); + + for (const auto &R : Ranges) + P->addRange(R); + + Notes.push_back(std::move(P)); + } + + ArrayRef<std::shared_ptr<PathDiagnosticNotePiece>> getNotes() { + return Notes; + } + + /// Add a range to a bug report. + /// + /// Ranges are used to highlight regions of interest in the source code. + /// They should be at the same source code line as the BugReport location. + /// By default, the source range of the statement corresponding to the error + /// node will be used; add a single invalid range to specify absence of + /// ranges. + void addRange(SourceRange R) { + assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used " + "to specify that the report does not have a range."); + Ranges.push_back(R); + } + + /// Get the SourceRanges associated with the report. + virtual ArrayRef<SourceRange> getRanges() const { + return Ranges; + } + + /// Add a fix-it hint to the bug report. + /// + /// Fix-it hints are the suggested edits to the code that would resolve + /// the problem explained by the bug report. Fix-it hints should be + /// as conservative as possible because it is not uncommon for the user + /// to blindly apply all fixits to their project. Note that it is very hard + /// to produce a good fix-it hint for most path-sensitive warnings. + void addFixItHint(const FixItHint &F) { + Fixits.push_back(F); + } + + llvm::ArrayRef<FixItHint> getFixits() const { return Fixits; } + + /// Reports are uniqued to ensure that we do not emit multiple diagnostics + /// for each bug. + virtual void Profile(llvm::FoldingSetNodeID& hash) const = 0; +}; + +class BasicBugReport : public BugReport { PathDiagnosticLocation Location; - PathDiagnosticLocation UniqueingLocation; - const Decl *UniqueingDecl; + const Decl *DeclWithIssue = nullptr; + +public: + BasicBugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) + : BugReport(Kind::Basic, bt, desc), Location(l) {} + + static bool classof(const BugReport *R) { + return R->getKind() == Kind::Basic; + } + + PathDiagnosticLocation getLocation() const override { + assert(Location.isValid()); + return Location; + } + + const Decl *getDeclWithIssue() const override { + return DeclWithIssue; + } + + PathDiagnosticLocation getUniqueingLocation() const override { + return getLocation(); + } + + const Decl *getUniqueingDecl() const override { + return getDeclWithIssue(); + } + + /// Specifically set the Decl where an issue occurred. This isn't necessary + /// for BugReports that cover a path as it will be automatically inferred. + void setDeclWithIssue(const Decl *declWithIssue) { + DeclWithIssue = declWithIssue; + } + void Profile(llvm::FoldingSetNodeID& hash) const override; +}; + +class PathSensitiveBugReport : public BugReport { +public: + using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>; + using visitor_iterator = VisitorList::iterator; + using visitor_range = llvm::iterator_range<visitor_iterator>; + +protected: + /// The ExplodedGraph node against which the report was thrown. It corresponds + /// to the end of the execution path that demonstrates the bug. const ExplodedNode *ErrorNode = nullptr; - SmallVector<SourceRange, 4> Ranges; - ExtraTextList ExtraText; - NoteList Notes; - using Symbols = llvm::DenseSet<SymbolRef>; - using Regions = llvm::DenseSet<const MemRegion *>; + /// The range that corresponds to ErrorNode's program point. It is usually + /// highlighted in the report. + const SourceRange ErrorNodeRange; + + /// Profile to identify equivalent bug reports for error report coalescing. /// A (stack of) a set of symbols that are registered with this /// report as being "interesting", and thus used to help decide which /// diagnostics to include when constructing the final path diagnostic. /// The stack is largely used by BugReporter when generating PathDiagnostics /// for multiple PathDiagnosticConsumers. - SmallVector<Symbols *, 2> interestingSymbols; + llvm::DenseMap<SymbolRef, bugreporter::TrackingKind> InterestingSymbols; /// A (stack of) set of regions that are registered with this report as being /// "interesting", and thus used to help decide which diagnostics /// to include when constructing the final path diagnostic. /// The stack is largely used by BugReporter when generating PathDiagnostics /// for multiple PathDiagnosticConsumers. - SmallVector<Regions *, 2> interestingRegions; + llvm::DenseMap<const MemRegion *, bugreporter::TrackingKind> + InterestingRegions; /// A set of location contexts that correspoind to call sites which should be /// considered "interesting". @@ -156,66 +350,58 @@ protected: /// Conditions we're already tracking. llvm::SmallSet<const ExplodedNode *, 4> TrackedConditions; -private: - // Used internally by BugReporter. - Symbols &getInterestingSymbols(); - Regions &getInterestingRegions(); - - void lazyInitializeInterestingSets(); - void pushInterestingSymbolsAndRegions(); - void popInterestingSymbolsAndRegions(); - -public: - BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode) - : BT(bt), Description(desc), ErrorNode(errornode) {} + /// Reports with different uniqueing locations are considered to be different + /// for the purposes of deduplication. + PathDiagnosticLocation UniqueingLocation; + const Decl *UniqueingDecl; - BugReport(const BugType& bt, StringRef shortDesc, StringRef desc, - const ExplodedNode *errornode) - : BT(bt), ShortDescription(shortDesc), Description(desc), - ErrorNode(errornode) {} + const Stmt *getStmt() const; - BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) - : BT(bt), Description(desc), Location(l) {} + /// If an event occurs in a different frame than the final diagnostic, + /// supply a message that will be used to construct an extra hint on the + /// returns from all the calls on the stack from this event to the final + /// diagnostic. + // FIXME: Allow shared_ptr keys in DenseMap? + std::map<PathDiagnosticPieceRef, std::unique_ptr<StackHintGenerator>> + StackHints; - /// Create a BugReport with a custom uniqueing location. +public: + PathSensitiveBugReport(const BugType &bt, StringRef desc, + const ExplodedNode *errorNode) + : BugReport(Kind::PathSensitive, bt, desc), ErrorNode(errorNode), + ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() + : SourceRange()) {} + + PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc, + const ExplodedNode *errorNode) + : BugReport(Kind::PathSensitive, bt, shortDesc, desc), + ErrorNode(errorNode), + ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() + : SourceRange()) {} + + /// Create a PathSensitiveBugReport with a custom uniqueing location. /// /// The reports that have the same report location, description, bug type, and /// ranges are uniqued - only one of the equivalent reports will be presented /// to the user. This method allows to rest the location which should be used /// for uniquing reports. For example, memory leaks checker, could set this to /// the allocation site, rather then the location where the bug is reported. - BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode, - PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique) - : BT(bt), Description(desc), UniqueingLocation(LocationToUnique), - UniqueingDecl(DeclToUnique), ErrorNode(errornode) {} - - virtual ~BugReport(); - - const BugType& getBugType() const { return BT; } - //BugType& getBugType() { return BT; } + PathSensitiveBugReport(BugType &bt, StringRef desc, + const ExplodedNode *errorNode, + PathDiagnosticLocation LocationToUnique, + const Decl *DeclToUnique) + : BugReport(Kind::PathSensitive, bt, desc), ErrorNode(errorNode), + ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() : SourceRange()), + UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) { + assert(errorNode); + } - /// True when the report has an execution path associated with it. - /// - /// A report is said to be path-sensitive if it was thrown against a - /// particular exploded node in the path-sensitive analysis graph. - /// Path-sensitive reports have their intermediate path diagnostics - /// auto-generated, perhaps with the help of checker-defined visitors, - /// and may contain extra notes. - /// Path-insensitive reports consist only of a single warning message - /// in a specific location, and perhaps extra notes. - /// Path-sensitive checkers are allowed to throw path-insensitive reports. - bool isPathSensitive() const { return ErrorNode != nullptr; } + static bool classof(const BugReport *R) { + return R->getKind() == Kind::PathSensitive; + } const ExplodedNode *getErrorNode() const { return ErrorNode; } - StringRef getDescription() const { return Description; } - - StringRef getShortDescription(bool UseFallback = true) const { - if (ShortDescription.empty() && UseFallback) - return Description; - return ShortDescription; - } - /// Indicates whether or not any path pruning should take place /// when generating a PathDiagnostic from this BugReport. bool shouldPrunePath() const { return !DoNotPrunePath; } @@ -223,15 +409,54 @@ public: /// Disable all path pruning when generating a PathDiagnostic. void disablePathPruning() { DoNotPrunePath = true; } - void markInteresting(SymbolRef sym); - void markInteresting(const MemRegion *R); - void markInteresting(SVal V); + /// Get the location on which the report should be uniqued. + PathDiagnosticLocation getUniqueingLocation() const override { + return UniqueingLocation; + } + + /// Get the declaration containing the uniqueing location. + const Decl *getUniqueingDecl() const override { + return UniqueingDecl; + } + + const Decl *getDeclWithIssue() const override; + + ArrayRef<SourceRange> getRanges() const override; + + PathDiagnosticLocation getLocation() const override; + + /// Marks a symbol as interesting. Different kinds of interestingness will + /// be processed differently by visitors (e.g. if the tracking kind is + /// condition, will append "will be used as a condition" to the message). + void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind = + bugreporter::TrackingKind::Thorough); + + /// Marks a region as interesting. Different kinds of interestingness will + /// be processed differently by visitors (e.g. if the tracking kind is + /// condition, will append "will be used as a condition" to the message). + void markInteresting( + const MemRegion *R, + bugreporter::TrackingKind TKind = bugreporter::TrackingKind::Thorough); + + /// Marks a symbolic value as interesting. Different kinds of interestingness + /// will be processed differently by visitors (e.g. if the tracking kind is + /// condition, will append "will be used as a condition" to the message). + void markInteresting(SVal V, bugreporter::TrackingKind TKind = + bugreporter::TrackingKind::Thorough); void markInteresting(const LocationContext *LC); - bool isInteresting(SymbolRef sym); - bool isInteresting(const MemRegion *R); - bool isInteresting(SVal V); - bool isInteresting(const LocationContext *LC); + bool isInteresting(SymbolRef sym) const; + bool isInteresting(const MemRegion *R) const; + bool isInteresting(SVal V) const; + bool isInteresting(const LocationContext *LC) const; + + Optional<bugreporter::TrackingKind> + getInterestingnessKind(SymbolRef sym) const; + + Optional<bugreporter::TrackingKind> + getInterestingnessKind(const MemRegion *R) const; + + Optional<bugreporter::TrackingKind> getInterestingnessKind(SVal V) const; /// Returns whether or not this report should be considered valid. /// @@ -254,87 +479,10 @@ public: Invalidations.insert(std::make_pair(Tag, Data)); } - /// Return the canonical declaration, be it a method or class, where - /// this issue semantically occurred. - const Decl *getDeclWithIssue() const; - - /// Specifically set the Decl where an issue occurred. This isn't necessary - /// for BugReports that cover a path as it will be automatically inferred. - void setDeclWithIssue(const Decl *declWithIssue) { - DeclWithIssue = declWithIssue; - } - - /// Add new item to the list of additional notes that need to be attached to - /// this path-insensitive report. If you want to add extra notes to a - /// path-sensitive report, you need to use a BugReporterVisitor because it - /// allows you to specify where exactly in the auto-generated path diagnostic - /// the extra note should appear. - void addNote(StringRef Msg, const PathDiagnosticLocation &Pos, - ArrayRef<SourceRange> Ranges) { - auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg); - - for (const auto &R : Ranges) - P->addRange(R); - - Notes.push_back(std::move(P)); - } - - // FIXME: Instead of making an override, we could have default-initialized - // Ranges with {}, however it crashes the MSVC 2013 compiler. - void addNote(StringRef Msg, const PathDiagnosticLocation &Pos) { - std::vector<SourceRange> Ranges; - addNote(Msg, Pos, Ranges); - } - - virtual const NoteList &getNotes() { - return Notes; - } - - /// This allows for addition of meta data to the diagnostic. - /// - /// Currently, only the HTMLDiagnosticClient knows how to display it. - void addExtraText(StringRef S) { - ExtraText.push_back(S); - } - - virtual const ExtraTextList &getExtraText() { - return ExtraText; - } - - /// Return the "definitive" location of the reported bug. - /// - /// While a bug can span an entire path, usually there is a specific - /// location that can be used to identify where the key issue occurred. - /// This location is used by clients rendering diagnostics. - virtual PathDiagnosticLocation getLocation(const SourceManager &SM) const; - - /// Get the location on which the report should be uniqued. - PathDiagnosticLocation getUniqueingLocation() const { - return UniqueingLocation; - } - - /// Get the declaration containing the uniqueing location. - const Decl *getUniqueingDecl() const { - return UniqueingDecl; - } - - const Stmt *getStmt() const; - - /// Add a range to a bug report. - /// - /// Ranges are used to highlight regions of interest in the source code. - /// They should be at the same source code line as the BugReport location. - /// By default, the source range of the statement corresponding to the error - /// node will be used; add a single invalid range to specify absence of - /// ranges. - void addRange(SourceRange R) { - assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used " - "to specify that the report does not have a range."); - Ranges.push_back(R); - } - - /// Get the SourceRanges associated with the report. - virtual llvm::iterator_range<ranges_iterator> getRanges(); + /// Profile to identify equivalent bug reports for error report coalescing. + /// Reports are uniqued to ensure that we do not emit multiple diagnostics + /// for each bug. + void Profile(llvm::FoldingSetNodeID &hash) const override; /// Add custom or predefined bug report visitors to this report. /// @@ -351,6 +499,7 @@ public: /// Iterators through the custom diagnostic visitors. visitor_iterator visitor_begin() { return Callbacks.begin(); } visitor_iterator visitor_end() { return Callbacks.end(); } + visitor_range visitors() { return {visitor_begin(), visitor_end()}; } /// Notes that the condition of the CFGBlock associated with \p Cond is /// being tracked. @@ -359,10 +508,25 @@ public: return TrackedConditions.insert(Cond).second; } - /// Profile to identify equivalent bug reports for error report coalescing. - /// Reports are uniqued to ensure that we do not emit multiple diagnostics - /// for each bug. - virtual void Profile(llvm::FoldingSetNodeID& hash) const; + void addCallStackHint(PathDiagnosticPieceRef Piece, + std::unique_ptr<StackHintGenerator> StackHint) { + StackHints[Piece] = std::move(StackHint); + } + + bool hasCallStackHint(PathDiagnosticPieceRef Piece) const { + return StackHints.count(Piece) > 0; + } + + /// Produce the hint for the given node. The node contains + /// information about the call for which the diagnostic can be generated. + std::string + getCallStackMessage(PathDiagnosticPieceRef Piece, + const ExplodedNode *N) const { + auto I = StackHints.find(Piece); + if (I != StackHints.end()) + return I->second->getMessage(N); + return ""; + } }; //===----------------------------------------------------------------------===// @@ -373,29 +537,21 @@ class BugReportEquivClass : public llvm::FoldingSetNode { friend class BugReporter; /// List of *owned* BugReport objects. - llvm::ilist<BugReport> Reports; + llvm::SmallVector<std::unique_ptr<BugReport>, 4> Reports; - void AddReport(std::unique_ptr<BugReport> R) { - Reports.push_back(R.release()); + void AddReport(std::unique_ptr<BugReport> &&R) { + Reports.push_back(std::move(R)); } public: BugReportEquivClass(std::unique_ptr<BugReport> R) { AddReport(std::move(R)); } - ~BugReportEquivClass(); + + ArrayRef<std::unique_ptr<BugReport>> getReports() const { return Reports; } void Profile(llvm::FoldingSetNodeID& ID) const { assert(!Reports.empty()); - Reports.front().Profile(ID); + Reports.front()->Profile(ID); } - - using iterator = llvm::ilist<BugReport>::iterator; - using const_iterator = llvm::ilist<BugReport>::const_iterator; - - iterator begin() { return Reports.begin(); } - iterator end() { return Reports.end(); } - - const_iterator begin() const { return Reports.begin(); } - const_iterator end() const { return Reports.end(); } }; //===----------------------------------------------------------------------===// @@ -404,9 +560,8 @@ public: class BugReporterData { public: - virtual ~BugReporterData(); + virtual ~BugReporterData() = default; - virtual DiagnosticsEngine& getDiagnostic() = 0; virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0; virtual ASTContext &getASTContext() = 0; virtual SourceManager &getSourceManager() = 0; @@ -419,60 +574,29 @@ public: /// /// The base class is used for generating path-insensitive class BugReporter { -public: - enum Kind { BaseBRKind, GRBugReporterKind }; - private: - using BugTypesTy = llvm::ImmutableSet<BugType *>; - - BugTypesTy::Factory F; - BugTypesTy BugTypes; - - const Kind kind; BugReporterData& D; /// Generate and flush the diagnostics for the given bug report. void FlushReport(BugReportEquivClass& EQ); - /// Generate the diagnostics for the given bug report. - std::unique_ptr<DiagnosticForConsumerMapTy> - generateDiagnosticForConsumerMap(BugReport *exampleReport, - ArrayRef<PathDiagnosticConsumer *> consumers, - ArrayRef<BugReport *> bugReports); - /// The set of bug reports tracked by the BugReporter. llvm::FoldingSet<BugReportEquivClass> EQClasses; /// A vector of BugReports for tracking the allocated pointers and cleanup. std::vector<BugReportEquivClass *> EQClassesVector; -protected: - BugReporter(BugReporterData& d, Kind k) - : BugTypes(F.getEmptySet()), kind(k), D(d) {} - public: - BugReporter(BugReporterData& d) - : BugTypes(F.getEmptySet()), kind(BaseBRKind), D(d) {} + BugReporter(BugReporterData &d) : D(d) {} virtual ~BugReporter(); /// Generate and flush diagnostics for all bug reports. void FlushReports(); - Kind getKind() const { return kind; } - - DiagnosticsEngine& getDiagnostic() { - return D.getDiagnostic(); - } - ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() { return D.getPathDiagnosticConsumers(); } - /// Iterator over the set of BugTypes tracked by the BugReporter. - using iterator = BugTypesTy::iterator; - iterator begin() { return BugTypes.begin(); } - iterator end() { return BugTypes.end(); } - /// Iterator over the set of BugReports tracked by the BugReporter. using EQClasses_iterator = llvm::FoldingSet<BugReportEquivClass>::iterator; EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); } @@ -480,126 +604,116 @@ public: ASTContext &getContext() { return D.getASTContext(); } - SourceManager &getSourceManager() { return D.getSourceManager(); } - - AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } - - virtual std::unique_ptr<DiagnosticForConsumerMapTy> - generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers, - ArrayRef<BugReport *> &bugReports) { - return {}; - } + const SourceManager &getSourceManager() { return D.getSourceManager(); } - void Register(const BugType *BT); + const AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } /// Add the given report to the set of reports tracked by BugReporter. /// /// The reports are usually generated by the checkers. Further, they are /// folded based on the profile value, which is done to coalesce similar /// reports. - void emitReport(std::unique_ptr<BugReport> R); + virtual void emitReport(std::unique_ptr<BugReport> R); void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, - ArrayRef<SourceRange> Ranges = None); + ArrayRef<SourceRange> Ranges = None, + ArrayRef<FixItHint> Fixits = None); - void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName, + void EmitBasicReport(const Decl *DeclWithIssue, CheckerNameRef CheckerName, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, - ArrayRef<SourceRange> Ranges = None); + ArrayRef<SourceRange> Ranges = None, + ArrayRef<FixItHint> Fixits = None); private: llvm::StringMap<BugType *> StrBugTypes; /// Returns a BugType that is associated with the given name and /// category. - BugType *getBugTypeForName(CheckName CheckName, StringRef name, + BugType *getBugTypeForName(CheckerNameRef CheckerName, StringRef name, StringRef category); + + virtual BugReport * + findReportInEquivalenceClass(BugReportEquivClass &eqClass, + SmallVectorImpl<BugReport *> &bugReports) { + return eqClass.getReports()[0].get(); + } + +protected: + /// Generate the diagnostics for the given bug report. + virtual std::unique_ptr<DiagnosticForConsumerMapTy> + generateDiagnosticForConsumerMap(BugReport *exampleReport, + ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> bugReports); }; /// GRBugReporter is used for generating path-sensitive reports. -class GRBugReporter : public BugReporter { +class PathSensitiveBugReporter final : public BugReporter { ExprEngine& Eng; -public: - GRBugReporter(BugReporterData& d, ExprEngine& eng) - : BugReporter(d, GRBugReporterKind), Eng(eng) {} + BugReport *findReportInEquivalenceClass( + BugReportEquivClass &eqClass, + SmallVectorImpl<BugReport *> &bugReports) override; - ~GRBugReporter() override; + /// Generate the diagnostics for the given bug report. + std::unique_ptr<DiagnosticForConsumerMapTy> + generateDiagnosticForConsumerMap(BugReport *exampleReport, + ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> bugReports) override; +public: + PathSensitiveBugReporter(BugReporterData& d, ExprEngine& eng) + : BugReporter(d), Eng(eng) {} /// getGraph - Get the exploded graph created by the analysis engine /// for the analyzed method or function. - ExplodedGraph &getGraph(); + const ExplodedGraph &getGraph() const; /// getStateManager - Return the state manager used by the analysis /// engine. - ProgramStateManager &getStateManager(); + ProgramStateManager &getStateManager() const; /// \p bugReports A set of bug reports within a *single* equivalence class /// /// \return A mapping from consumers to the corresponding diagnostics. /// Iterates through the bug reports within a single equivalence class, /// stops at a first non-invalidated report. - std::unique_ptr<DiagnosticForConsumerMapTy> - generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers, - ArrayRef<BugReport *> &bugReports) override; + std::unique_ptr<DiagnosticForConsumerMapTy> generatePathDiagnostics( + ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<PathSensitiveBugReport *> &bugReports); - /// classof - Used by isa<>, cast<>, and dyn_cast<>. - static bool classof(const BugReporter* R) { - return R->getKind() == GRBugReporterKind; - } + void emitReport(std::unique_ptr<BugReport> R) override; }; -class NodeMapClosure : public BugReport::NodeResolver { - InterExplodedGraphMap &M; - -public: - NodeMapClosure(InterExplodedGraphMap &m) : M(m) {} - - const ExplodedNode *getOriginalNode(const ExplodedNode *N) override { - return M.lookup(N); - } -}; - class BugReporterContext { - GRBugReporter &BR; - NodeMapClosure NMC; + PathSensitiveBugReporter &BR; virtual void anchor(); public: - BugReporterContext(GRBugReporter &br, InterExplodedGraphMap &Backmap) - : BR(br), NMC(Backmap) {} + BugReporterContext(PathSensitiveBugReporter &br) : BR(br) {} virtual ~BugReporterContext() = default; - GRBugReporter& getBugReporter() { return BR; } + PathSensitiveBugReporter& getBugReporter() { return BR; } - ExplodedGraph &getGraph() { return BR.getGraph(); } - - ProgramStateManager& getStateManager() { + ProgramStateManager& getStateManager() const { return BR.getStateManager(); } - SValBuilder &getSValBuilder() { - return getStateManager().getSValBuilder(); - } - - ASTContext &getASTContext() { + ASTContext &getASTContext() const { return BR.getContext(); } - SourceManager& getSourceManager() { + const SourceManager& getSourceManager() const { return BR.getSourceManager(); } - AnalyzerOptions &getAnalyzerOptions() { + const AnalyzerOptions &getAnalyzerOptions() const { return BR.getAnalyzerOptions(); } - - NodeMapClosure& getNodeResolver() { return NMC; } }; @@ -648,7 +762,7 @@ public: public: const NoteTag *makeNoteTag(Callback &&Cb, bool IsPrunable = false) { - // We cannot use make_unique because we cannot access the private + // We cannot use std::make_unique because we cannot access the private // constructor from inside it. std::unique_ptr<NoteTag> T(new NoteTag(std::move(Cb), IsPrunable)); Tags.push_back(std::move(T)); diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index ef5d327d39da..de0ee5de81b5 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -33,11 +33,12 @@ class Stmt; namespace ento { -class BugReport; +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 { @@ -57,32 +58,68 @@ public: /// /// The last parameter can be used to register a new visitor with the given /// BugReport while processing a node. - virtual std::shared_ptr<PathDiagnosticPiece> - VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &BR) = 0; + 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, - BugReport &BR); + 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 std::shared_ptr<PathDiagnosticPiece> - getEndPath(BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR); + 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 std::shared_ptr<PathDiagnosticPiece> - getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *N, - BugReport &BR); + 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 the it + /// aren't pruned, but otherwise relies on the user reading the code or + /// following the arrows. + Condition +}; + +/// 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 EnableNullFPSuppression Whether we should employ false positive +/// suppression (inlined defensive checks, returned null). +/// +/// \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, + TrackingKind TKind = TrackingKind::Thorough, + bool EnableNullFPSuppression = true); + +const Expr *getDerefExpr(const Stmt *S); + +} // namespace bugreporter + /// Finds last store into the given region, /// which is different from a given symbolic value. class FindLastStoreBRVisitor final : public BugReporterVisitor { @@ -94,21 +131,34 @@ class FindLastStoreBRVisitor final : public BugReporterVisitor { /// bug, we are going to employ false positive suppression. bool EnableNullFPSuppression; -public: - /// Creates a visitor for every VarDecl inside a Stmt and registers it with - /// the BugReport. - static void registerStatementVarDecls(BugReport &BR, const Stmt *S, - bool EnableNullFPSuppression); + using TrackingKind = bugreporter::TrackingKind; + TrackingKind TKind; + const StackFrameContext *OriginSFC; +public: + /// \param V We're searching for the store where \c R received this value. + /// \param R The region we're tracking. + /// \param TKind May limit the amount of notes added to the bug report. + /// \param OriginSFC 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. FindLastStoreBRVisitor(KnownSVal V, const MemRegion *R, - bool InEnableNullFPSuppression) - : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression) {} + bool InEnableNullFPSuppression, TrackingKind TKind, + const StackFrameContext *OriginSFC = nullptr) + : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression), + TKind(TKind), OriginSFC(OriginSFC) { + assert(R); + } void Profile(llvm::FoldingSetNodeID &ID) const override; - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; class TrackConstraintBRVisitor final : public BugReporterVisitor { @@ -132,9 +182,9 @@ public: /// to make all PathDiagnosticPieces created by this visitor. static const char *getTag(); - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: /// Checks if the constraint is valid in the current state. @@ -150,9 +200,9 @@ public: ID.AddPointer(&x); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + 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. @@ -162,8 +212,10 @@ public: /// Visitor that tries to report interesting diagnostics from conditions. class ConditionBRVisitor final : public BugReporterVisitor { // FIXME: constexpr initialization isn't supported by MSVC2013. - static const char *const GenericTrueMessage; - static const char *const GenericFalseMessage; + 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 { @@ -175,41 +227,44 @@ public: /// to make all PathDiagnosticPieces created by this visitor. static const char *getTag(); - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; - std::shared_ptr<PathDiagnosticPiece> VisitNodeImpl(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR); + PathDiagnosticPieceRef VisitNodeImpl(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR); - std::shared_ptr<PathDiagnosticPiece> + PathDiagnosticPieceRef VisitTerminator(const Stmt *Term, const ExplodedNode *N, - const CFGBlock *srcBlk, const CFGBlock *dstBlk, BugReport &R, - BugReporterContext &BRC); + const CFGBlock *SrcBlk, const CFGBlock *DstBlk, + PathSensitiveBugReport &R, BugReporterContext &BRC); - std::shared_ptr<PathDiagnosticPiece> - VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, BugReport &R, - const ExplodedNode *N, bool TookTrue); + PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, + BugReporterContext &BRC, + PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue); - std::shared_ptr<PathDiagnosticPiece> - VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, - BugReporterContext &BRC, BugReport &R, const ExplodedNode *N, - bool TookTrue, bool IsAssuming); + PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, + BugReporterContext &BRC, + PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue, + bool IsAssuming); - std::shared_ptr<PathDiagnosticPiece> + PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, - BugReporterContext &BRC, BugReport &R, const ExplodedNode *N, - bool TookTrue, bool IsAssuming); + BugReporterContext &BRC, PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue, bool IsAssuming); - std::shared_ptr<PathDiagnosticPiece> - VisitTrueTest(const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC, - BugReport &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); - std::shared_ptr<PathDiagnosticPiece> + PathDiagnosticPieceRef VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr, - BugReporterContext &BRC, BugReport &R, + BugReporterContext &BRC, PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue); /// Tries to print the value of the given expression. @@ -228,7 +283,7 @@ public: const Expr *ParentEx, raw_ostream &Out, BugReporterContext &BRC, - BugReport &R, + PathSensitiveBugReport &R, const ExplodedNode *N, Optional<bool> &prunable, bool IsSameFieldName); @@ -251,14 +306,13 @@ public: ID.AddPointer(getTag()); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *, - BugReporterContext &, - BugReport &) override { + PathDiagnosticPieceRef VisitNode(const ExplodedNode *, BugReporterContext &, + PathSensitiveBugReport &) override { return nullptr; } void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, - BugReport &BR) override; + PathSensitiveBugReport &BR) override; }; /// When a region containing undefined value or '0' value is passed @@ -279,9 +333,9 @@ public: ID.AddPointer(R); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { @@ -308,9 +362,9 @@ public: /// to make all PathDiagnosticPieces created by this visitor. static const char *getTag(); - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, - BugReport &BR) override; + 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 @@ -326,12 +380,12 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const override; - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, - BugReport &BR) override; + PathSensitiveBugReport &BR) override; }; @@ -340,32 +394,11 @@ class TagVisitor : public BugReporterVisitor { public: void Profile(llvm::FoldingSetNodeID &ID) const override; - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &R) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &R) override; }; -namespace bugreporter { - -/// 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 EnableNullFPSuppression Whether we should employ false positive -/// suppression (inlined defensive checks, returned null). -/// -/// \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, BugReport &R, - bool EnableNullFPSuppression = true); - -const Expr *getDerefExpr(const Stmt *S); - -} // namespace bugreporter - } // namespace ento } // namespace clang diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h index 324b5312e790..237053df7e44 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -28,8 +28,8 @@ class ExprEngine; class BugType { private: - const CheckName Check; - const std::string Name; + const CheckerNameRef CheckerName; + const std::string Description; const std::string Category; const CheckerBase *Checker; bool SuppressOnSink; @@ -37,28 +37,27 @@ private: virtual void anchor(); public: - BugType(CheckName Check, StringRef Name, StringRef Cat, - bool SuppressOnSink=false) - : Check(Check), Name(Name), Category(Cat), Checker(nullptr), - SuppressOnSink(SuppressOnSink) {} + BugType(CheckerNameRef CheckerName, StringRef Name, StringRef Cat, + bool SuppressOnSink = false) + : CheckerName(CheckerName), Description(Name), Category(Cat), + Checker(nullptr), SuppressOnSink(SuppressOnSink) {} BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat, - bool SuppressOnSink=false) - : Check(Checker->getCheckName()), Name(Name), Category(Cat), - Checker(Checker), SuppressOnSink(SuppressOnSink) {} + bool SuppressOnSink = false) + : CheckerName(Checker->getCheckerName()), Description(Name), + Category(Cat), Checker(Checker), SuppressOnSink(SuppressOnSink) {} virtual ~BugType() = default; - StringRef getName() const { return Name; } + StringRef getDescription() const { return Description; } StringRef getCategory() const { return Category; } - StringRef getCheckName() const { - // FIXME: This is a workaround to ensure that the correct check name is used - // The check names are set after the constructors are run. + StringRef getCheckerName() const { + // FIXME: This is a workaround to ensure that the correct checerk name is + // used. The checker names are set after the constructors are run. // In case the BugType object is initialized in the checker's ctor - // the Check field will be empty. To circumvent this problem we use + // the CheckerName field will be empty. To circumvent this problem we use // CheckerBase whenever it is possible. - StringRef CheckName = - Checker ? Checker->getCheckName().getName() : Check.getName(); - assert(!CheckName.empty() && "Check name is not set properly."); - return CheckName; + StringRef Ret = Checker ? Checker->getCheckerName() : CheckerName; + assert(!Ret.empty() && "Checker name is not set properly."); + return Ret; } /// isSuppressOnSink - Returns true if bug reports associated with this bug @@ -71,8 +70,9 @@ class BuiltinBug : public BugType { const std::string desc; void anchor() override; public: - BuiltinBug(class CheckName check, const char *name, const char *description) - : BugType(check, name, categories::LogicError), desc(description) {} + BuiltinBug(class CheckerNameRef checker, const char *name, + const char *description) + : BugType(checker, name, categories::LogicError), desc(description) {} BuiltinBug(const CheckerBase *checker, const char *name, const char *description) diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h b/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h index 85526eb49f0c..22c1a7dd98cc 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h @@ -18,6 +18,7 @@ namespace clang { extern const char * const MemoryRefCount; extern const char * const MemoryError; extern const char * const UnixAPI; + extern const char * const CXXObjectLifecycle; } } } diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h deleted file mode 100644 index 5230742a4aa4..000000000000 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ /dev/null @@ -1,923 +0,0 @@ -//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- 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 defines the PathDiagnostic-related interfaces. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H -#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H - -#include "clang/AST/Stmt.h" -#include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Basic/LLVM.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/PointerUnion.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Allocator.h" -#include <cassert> -#include <deque> -#include <iterator> -#include <list> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <utility> -#include <vector> - -namespace clang { - -class AnalysisDeclContext; -class BinaryOperator; -class CallEnter; -class CallExitEnd; -class CallExpr; -class ConditionalOperator; -class Decl; -class Expr; -class LocationContext; -class MemberExpr; -class ProgramPoint; -class SourceManager; - -namespace ento { - -class ExplodedNode; -class SymExpr; - -using SymbolRef = const SymExpr *; - -//===----------------------------------------------------------------------===// -// High-level interface for handlers of path-sensitive diagnostics. -//===----------------------------------------------------------------------===// - -class PathDiagnostic; - -class PathDiagnosticConsumer { -public: - class PDFileEntry : public llvm::FoldingSetNode { - public: - PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} - - using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>; - - /// A vector of <consumer,file> pairs. - ConsumerFiles files; - - /// A precomputed hash tag used for uniquing PDFileEntry objects. - const llvm::FoldingSetNodeID NodeID; - - /// Used for profiling in the FoldingSet. - void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } - }; - - class FilesMade { - llvm::BumpPtrAllocator Alloc; - llvm::FoldingSet<PDFileEntry> Set; - - public: - ~FilesMade(); - - bool empty() const { return Set.empty(); } - - void addDiagnostic(const PathDiagnostic &PD, - StringRef ConsumerName, - StringRef fileName); - - PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); - }; - -private: - virtual void anchor(); - -public: - PathDiagnosticConsumer() = default; - virtual ~PathDiagnosticConsumer(); - - void FlushDiagnostics(FilesMade *FilesMade); - - virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - FilesMade *filesMade) = 0; - - virtual StringRef getName() const = 0; - - void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D); - - enum PathGenerationScheme { - /// Only runs visitors, no output generated. - None, - - /// Used for HTML, SARIF, and text output. - Minimal, - - /// Used for plist output, used for "arrows" generation. - Extensive, - }; - - virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } - virtual bool supportsLogicalOpControlFlow() const { return false; } - - /// Return true if the PathDiagnosticConsumer supports individual - /// PathDiagnostics that span multiple files. - virtual bool supportsCrossFileDiagnostics() const { return false; } - -protected: - bool flushed = false; - llvm::FoldingSet<PathDiagnostic> Diags; -}; - -//===----------------------------------------------------------------------===// -// Path-sensitive diagnostics. -//===----------------------------------------------------------------------===// - -class PathDiagnosticRange : public SourceRange { -public: - bool isPoint = false; - - PathDiagnosticRange(SourceRange R, bool isP = false) - : SourceRange(R), isPoint(isP) {} - PathDiagnosticRange() = default; -}; - -using LocationOrAnalysisDeclContext = - llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>; - -class PathDiagnosticLocation { -private: - enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK; - - const Stmt *S = nullptr; - const Decl *D = nullptr; - const SourceManager *SM = nullptr; - FullSourceLoc Loc; - PathDiagnosticRange Range; - - PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) - : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} - - FullSourceLoc genLocation( - SourceLocation L = SourceLocation(), - LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; - - PathDiagnosticRange genRange( - LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; - -public: - /// Create an invalid location. - PathDiagnosticLocation() = default; - - /// Create a location corresponding to the given statement. - PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, - LocationOrAnalysisDeclContext lac) - : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK), - S(K == StmtK ? s : nullptr), SM(&sm), - Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { - assert(K == SingleLocK || S); - assert(K == SingleLocK || Loc.isValid()); - assert(K == SingleLocK || Range.isValid()); - } - - /// Create a location corresponding to the given declaration. - PathDiagnosticLocation(const Decl *d, const SourceManager &sm) - : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { - assert(D); - assert(Loc.isValid()); - assert(Range.isValid()); - } - - /// Create a location at an explicit offset in the source. - /// - /// This should only be used if there are no more appropriate constructors. - PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) - : SM(&sm), Loc(loc, sm), Range(genRange()) { - assert(Loc.isValid()); - assert(Range.isValid()); - } - - /// Create a location corresponding to the given declaration. - static PathDiagnosticLocation create(const Decl *D, - const SourceManager &SM) { - return PathDiagnosticLocation(D, SM); - } - - /// Create a location for the beginning of the declaration. - static PathDiagnosticLocation createBegin(const Decl *D, - const SourceManager &SM); - - /// Create a location for the beginning of the declaration. - /// The third argument is ignored, useful for generic treatment - /// of statements and declarations. - static PathDiagnosticLocation - createBegin(const Decl *D, const SourceManager &SM, - const LocationOrAnalysisDeclContext LAC) { - return createBegin(D, SM); - } - - /// Create a location for the beginning of the statement. - static PathDiagnosticLocation createBegin(const Stmt *S, - const SourceManager &SM, - const LocationOrAnalysisDeclContext LAC); - - /// Create a location for the end of the statement. - /// - /// If the statement is a CompoundStatement, the location will point to the - /// closing brace instead of following it. - static PathDiagnosticLocation createEnd(const Stmt *S, - const SourceManager &SM, - const LocationOrAnalysisDeclContext LAC); - - /// Create the location for the operator of the binary expression. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, - const SourceManager &SM); - static PathDiagnosticLocation createConditionalColonLoc( - const ConditionalOperator *CO, - const SourceManager &SM); - - /// For member expressions, return the location of the '.' or '->'. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, - const SourceManager &SM); - - /// Create a location for the beginning of the compound statement. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, - const SourceManager &SM); - - /// Create a location for the end of the compound statement. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, - const SourceManager &SM); - - /// Create a location for the beginning of the enclosing declaration body. - /// Defaults to the beginning of the first statement in the declaration body. - static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, - const SourceManager &SM); - - /// Constructs a location for the end of the enclosing declaration body. - /// Defaults to the end of brace. - static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, - const SourceManager &SM); - - /// Create a location corresponding to the given valid ExplodedNode. - static PathDiagnosticLocation create(const ProgramPoint &P, - const SourceManager &SMng); - - /// Create a location corresponding to the next valid ExplodedNode as end - /// of path location. - static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N, - const SourceManager &SM); - - /// Convert the given location into a single kind location. - static PathDiagnosticLocation createSingleLocation( - const PathDiagnosticLocation &PDL); - - bool operator==(const PathDiagnosticLocation &X) const { - return K == X.K && Loc == X.Loc && Range == X.Range; - } - - bool operator!=(const PathDiagnosticLocation &X) const { - return !(*this == X); - } - - bool isValid() const { - return SM != nullptr; - } - - FullSourceLoc asLocation() const { - return Loc; - } - - PathDiagnosticRange asRange() const { - return Range; - } - - const Stmt *asStmt() const { assert(isValid()); return S; } - const Stmt *getStmtOrNull() const { - if (!isValid()) - return nullptr; - return asStmt(); - } - - const Decl *asDecl() const { assert(isValid()); return D; } - - bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } - - bool hasValidLocation() const { return asLocation().isValid(); } - - void invalidate() { - *this = PathDiagnosticLocation(); - } - - void flatten(); - - const SourceManager& getManager() const { assert(isValid()); return *SM; } - - void Profile(llvm::FoldingSetNodeID &ID) const; - - void dump() const; - - /// Given an exploded node, retrieve the statement that should be used - /// for the diagnostic location. - static const Stmt *getStmt(const ExplodedNode *N); - - /// Retrieve the statement corresponding to the successor node. - static const Stmt *getNextStmt(const ExplodedNode *N); -}; - -class PathDiagnosticLocationPair { -private: - PathDiagnosticLocation Start, End; - -public: - PathDiagnosticLocationPair(const PathDiagnosticLocation &start, - const PathDiagnosticLocation &end) - : Start(start), End(end) {} - - const PathDiagnosticLocation &getStart() const { return Start; } - const PathDiagnosticLocation &getEnd() const { return End; } - - void setStart(const PathDiagnosticLocation &L) { Start = L; } - void setEnd(const PathDiagnosticLocation &L) { End = L; } - - void flatten() { - Start.flatten(); - End.flatten(); - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - Start.Profile(ID); - End.Profile(ID); - } -}; - -//===----------------------------------------------------------------------===// -// Path "pieces" for path-sensitive diagnostics. -//===----------------------------------------------------------------------===// - -class PathDiagnosticPiece: public llvm::FoldingSetNode { -public: - enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp }; - enum DisplayHint { Above, Below }; - -private: - const std::string str; - const Kind kind; - const DisplayHint Hint; - - /// In the containing bug report, this piece is the last piece from - /// the main source file. - bool LastInMainSourceFile = false; - - /// A constant string that can be used to tag the PathDiagnosticPiece, - /// typically with the identification of the creator. The actual pointer - /// value is meant to be an identifier; the string itself is useful for - /// debugging. - StringRef Tag; - - std::vector<SourceRange> ranges; - -protected: - PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); - PathDiagnosticPiece(Kind k, DisplayHint hint = Below); - -public: - PathDiagnosticPiece() = delete; - PathDiagnosticPiece(const PathDiagnosticPiece &) = delete; - PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete; - virtual ~PathDiagnosticPiece(); - - StringRef getString() const { return str; } - - /// Tag this PathDiagnosticPiece with the given C-string. - void setTag(const char *tag) { Tag = tag; } - - /// Return the opaque tag (if any) on the PathDiagnosticPiece. - const void *getTag() const { return Tag.data(); } - - /// Return the string representation of the tag. This is useful - /// for debugging. - StringRef getTagStr() const { return Tag; } - - /// getDisplayHint - Return a hint indicating where the diagnostic should - /// be displayed by the PathDiagnosticConsumer. - DisplayHint getDisplayHint() const { return Hint; } - - virtual PathDiagnosticLocation getLocation() const = 0; - virtual void flattenLocations() = 0; - - Kind getKind() const { return kind; } - - void addRange(SourceRange R) { - if (!R.isValid()) - return; - ranges.push_back(R); - } - - void addRange(SourceLocation B, SourceLocation E) { - if (!B.isValid() || !E.isValid()) - return; - ranges.push_back(SourceRange(B,E)); - } - - /// Return the SourceRanges associated with this PathDiagnosticPiece. - ArrayRef<SourceRange> getRanges() const { return ranges; } - - virtual void Profile(llvm::FoldingSetNodeID &ID) const; - - void setAsLastInMainSourceFile() { - LastInMainSourceFile = true; - } - - bool isLastInMainSourceFile() const { - return LastInMainSourceFile; - } - - virtual void dump() const = 0; -}; - -class PathPieces : public std::list<std::shared_ptr<PathDiagnosticPiece>> { - void flattenTo(PathPieces &Primary, PathPieces &Current, - bool ShouldFlattenMacros) const; - -public: - PathPieces flatten(bool ShouldFlattenMacros) const { - PathPieces Result; - flattenTo(Result, Result, ShouldFlattenMacros); - return Result; - } - - void dump() const; -}; - -class PathDiagnosticSpotPiece : public PathDiagnosticPiece { -private: - PathDiagnosticLocation Pos; - -public: - PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, - StringRef s, - PathDiagnosticPiece::Kind k, - bool addPosRange = true) - : PathDiagnosticPiece(s, k), Pos(pos) { - assert(Pos.isValid() && Pos.hasValidLocation() && - "PathDiagnosticSpotPiece's must have a valid location."); - if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); - } - - PathDiagnosticLocation getLocation() const override { return Pos; } - void flattenLocations() override { Pos.flatten(); } - - void Profile(llvm::FoldingSetNodeID &ID) const override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Event || P->getKind() == Macro || - P->getKind() == Note || P->getKind() == PopUp; - } -}; - -/// Interface for classes constructing Stack hints. -/// -/// If a PathDiagnosticEvent occurs in a different frame than the final -/// diagnostic the hints can be used to summarize the effect of the call. -class StackHintGenerator { -public: - virtual ~StackHintGenerator() = 0; - - /// Construct the Diagnostic message for the given ExplodedNode. - virtual std::string getMessage(const ExplodedNode *N) = 0; -}; - -/// Constructs a Stack hint for the given symbol. -/// -/// The class knows how to construct the stack hint message based on -/// traversing the CallExpr associated with the call and checking if the given -/// symbol is returned or is one of the arguments. -/// The hint can be customized by redefining 'getMessageForX()' methods. -class StackHintGeneratorForSymbol : public StackHintGenerator { -private: - SymbolRef Sym; - std::string Msg; - -public: - StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} - ~StackHintGeneratorForSymbol() override = default; - - /// Search the call expression for the symbol Sym and dispatch the - /// 'getMessageForX()' methods to construct a specific message. - std::string getMessage(const ExplodedNode *N) override; - - /// Produces the message of the following form: - /// 'Msg via Nth parameter' - virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); - - virtual std::string getMessageForReturn(const CallExpr *CallExpr) { - return Msg; - } - - virtual std::string getMessageForSymbolNotFound() { - return Msg; - } -}; - -class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { - Optional<bool> IsPrunable; - - /// If the event occurs in a different frame than the final diagnostic, - /// supply a message that will be used to construct an extra hint on the - /// returns from all the calls on the stack from this event to the final - /// diagnostic. - std::unique_ptr<StackHintGenerator> CallStackHint; - -public: - PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, - StringRef s, bool addPosRange = true, - StackHintGenerator *stackHint = nullptr) - : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), - CallStackHint(stackHint) {} - ~PathDiagnosticEventPiece() override; - - /// Mark the diagnostic piece as being potentially prunable. This - /// flag may have been previously set, at which point it will not - /// be reset unless one specifies to do so. - void setPrunable(bool isPrunable, bool override = false) { - if (IsPrunable.hasValue() && !override) - return; - IsPrunable = isPrunable; - } - - /// Return true if the diagnostic piece is prunable. - bool isPrunable() const { - return IsPrunable.hasValue() ? IsPrunable.getValue() : false; - } - - bool hasCallStackHint() { return (bool)CallStackHint; } - - /// Produce the hint for the given node. The node contains - /// information about the call for which the diagnostic can be generated. - std::string getCallStackMessage(const ExplodedNode *N) { - if (CallStackHint) - return CallStackHint->getMessage(N); - return {}; - } - - void dump() const override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Event; - } -}; - -class PathDiagnosticCallPiece : public PathDiagnosticPiece { - const Decl *Caller; - const Decl *Callee = nullptr; - - // Flag signifying that this diagnostic has only call enter and no matching - // call exit. - bool NoExit; - - // Flag signifying that the callee function is an Objective-C autosynthesized - // property getter or setter. - bool IsCalleeAnAutosynthesizedPropertyAccessor = false; - - // The custom string, which should appear after the call Return Diagnostic. - // TODO: Should we allow multiple diagnostics? - std::string CallStackMessage; - - PathDiagnosticCallPiece(const Decl *callerD, - const PathDiagnosticLocation &callReturnPos) - : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), - callReturn(callReturnPos) {} - PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) - : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), - path(oldPath) {} - -public: - PathDiagnosticLocation callEnter; - PathDiagnosticLocation callEnterWithin; - PathDiagnosticLocation callReturn; - PathPieces path; - - ~PathDiagnosticCallPiece() override; - - const Decl *getCaller() const { return Caller; } - - const Decl *getCallee() const { return Callee; } - void setCallee(const CallEnter &CE, const SourceManager &SM); - - bool hasCallStackMessage() { return !CallStackMessage.empty(); } - void setCallStackMessage(StringRef st) { CallStackMessage = st; } - - PathDiagnosticLocation getLocation() const override { return callEnter; } - - std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const; - std::shared_ptr<PathDiagnosticEventPiece> - getCallEnterWithinCallerEvent() const; - std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const; - - void flattenLocations() override { - callEnter.flatten(); - callReturn.flatten(); - for (const auto &I : path) - I->flattenLocations(); - } - - static std::shared_ptr<PathDiagnosticCallPiece> - construct(const CallExitEnd &CE, - const SourceManager &SM); - - static PathDiagnosticCallPiece *construct(PathPieces &pieces, - const Decl *caller); - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Call; - } -}; - -class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { - std::vector<PathDiagnosticLocationPair> LPairs; - -public: - PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, - const PathDiagnosticLocation &endPos, - StringRef s) - : PathDiagnosticPiece(s, ControlFlow) { - LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); - } - - PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, - const PathDiagnosticLocation &endPos) - : PathDiagnosticPiece(ControlFlow) { - LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); - } - - ~PathDiagnosticControlFlowPiece() override; - - PathDiagnosticLocation getStartLocation() const { - assert(!LPairs.empty() && - "PathDiagnosticControlFlowPiece needs at least one location."); - return LPairs[0].getStart(); - } - - PathDiagnosticLocation getEndLocation() const { - assert(!LPairs.empty() && - "PathDiagnosticControlFlowPiece needs at least one location."); - return LPairs[0].getEnd(); - } - - void setStartLocation(const PathDiagnosticLocation &L) { - LPairs[0].setStart(L); - } - - void setEndLocation(const PathDiagnosticLocation &L) { - LPairs[0].setEnd(L); - } - - void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } - - PathDiagnosticLocation getLocation() const override { - return getStartLocation(); - } - - using iterator = std::vector<PathDiagnosticLocationPair>::iterator; - - iterator begin() { return LPairs.begin(); } - iterator end() { return LPairs.end(); } - - void flattenLocations() override { - for (auto &I : *this) - I.flatten(); - } - - using const_iterator = - std::vector<PathDiagnosticLocationPair>::const_iterator; - - const_iterator begin() const { return LPairs.begin(); } - const_iterator end() const { return LPairs.end(); } - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == ControlFlow; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { -public: - PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) - : PathDiagnosticSpotPiece(pos, "", Macro) {} - ~PathDiagnosticMacroPiece() override; - - PathPieces subPieces; - - bool containsEvent() const; - - void flattenLocations() override { - PathDiagnosticSpotPiece::flattenLocations(); - for (const auto &I : subPieces) - I->flattenLocations(); - } - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Macro; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { -public: - PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, - bool AddPosRange = true) - : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} - ~PathDiagnosticNotePiece() override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Note; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece { -public: - PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S, - bool AddPosRange = true) - : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} - ~PathDiagnosticPopUpPiece() override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == PopUp; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -/// File IDs mapped to sets of line numbers. -using FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>; - -/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive -/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, -/// each which represent the pieces of the path. -class PathDiagnostic : public llvm::FoldingSetNode { - std::string CheckName; - const Decl *DeclWithIssue; - std::string BugType; - std::string VerboseDesc; - std::string ShortDesc; - std::string Category; - std::deque<std::string> OtherDesc; - - /// Loc The location of the path diagnostic report. - PathDiagnosticLocation Loc; - - PathPieces pathImpl; - SmallVector<PathPieces *, 3> pathStack; - - /// Important bug uniqueing location. - /// The location info is useful to differentiate between bugs. - PathDiagnosticLocation UniqueingLoc; - const Decl *UniqueingDecl; - - /// Lines executed in the path. - std::unique_ptr<FilesToLineNumsMap> ExecutedLines; - -public: - PathDiagnostic() = delete; - PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue, - StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, - StringRef category, PathDiagnosticLocation LocationToUnique, - const Decl *DeclToUnique, - std::unique_ptr<FilesToLineNumsMap> ExecutedLines); - ~PathDiagnostic(); - - const PathPieces &path; - - /// Return the path currently used by builders for constructing the - /// PathDiagnostic. - PathPieces &getActivePath() { - if (pathStack.empty()) - return pathImpl; - return *pathStack.back(); - } - - /// Return a mutable version of 'path'. - PathPieces &getMutablePieces() { - return pathImpl; - } - - /// Return the unrolled size of the path. - unsigned full_size(); - - void pushActivePath(PathPieces *p) { pathStack.push_back(p); } - void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } - - bool isWithinCall() const { return !pathStack.empty(); } - - void setEndOfPath(std::shared_ptr<PathDiagnosticPiece> EndPiece) { - assert(!Loc.isValid() && "End location already set!"); - Loc = EndPiece->getLocation(); - assert(Loc.isValid() && "Invalid location for end-of-path piece"); - getActivePath().push_back(std::move(EndPiece)); - } - - void appendToDesc(StringRef S) { - if (!ShortDesc.empty()) - ShortDesc += S; - VerboseDesc += S; - } - - /// If the last piece of the report point to the header file, resets - /// the location of the report to be the last location in the main source - /// file. - void resetDiagnosticLocationToMainFile(); - - StringRef getVerboseDescription() const { return VerboseDesc; } - - StringRef getShortDescription() const { - return ShortDesc.empty() ? VerboseDesc : ShortDesc; - } - - StringRef getCheckName() const { return CheckName; } - StringRef getBugType() const { return BugType; } - StringRef getCategory() const { return Category; } - - /// Return the semantic context where an issue occurred. If the - /// issue occurs along a path, this represents the "central" area - /// where the bug manifests. - const Decl *getDeclWithIssue() const { return DeclWithIssue; } - - using meta_iterator = std::deque<std::string>::const_iterator; - - meta_iterator meta_begin() const { return OtherDesc.begin(); } - meta_iterator meta_end() const { return OtherDesc.end(); } - void addMeta(StringRef s) { OtherDesc.push_back(s); } - - const FilesToLineNumsMap &getExecutedLines() const { - return *ExecutedLines; - } - - FilesToLineNumsMap &getExecutedLines() { - return *ExecutedLines; - } - - PathDiagnosticLocation getLocation() const { - return Loc; - } - - /// Get the location on which the report should be uniqued. - PathDiagnosticLocation getUniqueingLoc() const { - return UniqueingLoc; - } - - /// Get the declaration containing the uniqueing location. - const Decl *getUniqueingDecl() const { - return UniqueingDecl; - } - - void flattenLocations() { - Loc.flatten(); - for (const auto &I : pathImpl) - I->flattenLocations(); - } - - /// Profiles the diagnostic, independent of the path it references. - /// - /// This can be used to merge diagnostics that refer to the same issue - /// along different paths. - void Profile(llvm::FoldingSetNodeID &ID) const; - - /// Profiles the diagnostic, including its path. - /// - /// Two diagnostics with the same issue along different paths will generate - /// different profiles. - void FullProfile(llvm::FoldingSetNodeID &ID) const; -}; - -} // namespace ento - -} // namespace clang - -#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index d0fe15f8b896..0c7acdbc3a97 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -490,12 +490,12 @@ public: } // end eval namespace class CheckerBase : public ProgramPointTag { - CheckName Name; + CheckerNameRef Name; friend class ::clang::ento::CheckerManager; public: StringRef getTagDescription() const override; - CheckName getCheckName() const; + CheckerNameRef getCheckerName() const; /// See CheckerManager::runCheckersForPrintState. virtual void printState(raw_ostream &Out, ProgramStateRef State, diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 6cc4baa1687f..38a9aaf72c27 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -90,21 +90,23 @@ enum PointerEscapeKind { PSK_EscapeOther }; -// This wrapper is used to ensure that only StringRefs originating from the -// CheckerRegistry are used as check names. We want to make sure all check -// name strings have a lifetime that keeps them alive at least until the path -// diagnostics have been processed. -class CheckName { +/// This wrapper is used to ensure that only StringRefs originating from the +/// CheckerRegistry are used as check names. We want to make sure all checker +/// name strings have a lifetime that keeps them alive at least until the path +/// diagnostics have been processed, since they are expected to be constexpr +/// string literals (most likely generated by TblGen). +class CheckerNameRef { friend class ::clang::ento::CheckerRegistry; StringRef Name; - explicit CheckName(StringRef Name) : Name(Name) {} + explicit CheckerNameRef(StringRef Name) : Name(Name) {} public: - CheckName() = default; + CheckerNameRef() = default; StringRef getName() const { return Name; } + operator StringRef() const { return Name; } }; enum class ObjCMessageVisitKind { @@ -117,7 +119,7 @@ class CheckerManager { ASTContext &Context; const LangOptions LangOpts; AnalyzerOptions &AOptions; - CheckName CurrentCheckName; + CheckerNameRef CurrentCheckerName; public: CheckerManager(ASTContext &Context, AnalyzerOptions &AOptions) @@ -125,8 +127,8 @@ public: ~CheckerManager(); - void setCurrentCheckName(CheckName name) { CurrentCheckName = name; } - CheckName getCurrentCheckName() const { return CurrentCheckName; } + void setCurrentCheckerName(CheckerNameRef name) { CurrentCheckerName = name; } + CheckerNameRef getCurrentCheckerName() const { return CurrentCheckerName; } bool hasPathSensitiveCheckers() const; @@ -162,7 +164,7 @@ public: assert(!ref && "Checker already registered, use getChecker!"); CHECKER *checker = new CHECKER(std::forward<AT>(Args)...); - checker->Name = CurrentCheckName; + checker->Name = CurrentCheckerName; CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>)); CHECKER::_register(checker, *this); ref = checker; diff --git a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h index ef6e7e0f45d5..8601966c91e5 100644 --- a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h +++ b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h @@ -20,17 +20,19 @@ namespace clang { class AnalyzerOptions; class Preprocessor; +namespace cross_tu { +class CrossTranslationUnitContext; +} namespace ento { class PathDiagnosticConsumer; typedef std::vector<PathDiagnosticConsumer*> PathDiagnosticConsumers; -#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN)\ -void CREATEFN(AnalyzerOptions &AnalyzerOpts,\ - PathDiagnosticConsumers &C,\ - const std::string &Prefix,\ - const Preprocessor &PP); +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \ + void CREATEFN(AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, \ + const std::string &Prefix, const Preprocessor &PP, \ + const cross_tu::CrossTranslationUnitContext &CTU); #include "clang/StaticAnalyzer/Core/Analyses.def" } // end 'ento' namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h index b0dda78a00a9..d605a6a667f6 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -15,9 +15,9 @@ #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ANALYSISMANAGER_H #include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" namespace clang { @@ -32,7 +32,6 @@ class AnalysisManager : public BugReporterData { AnalysisDeclContextManager AnaCtxMgr; ASTContext &Ctx; - DiagnosticsEngine &Diags; const LangOptions &LangOpts; PathDiagnosticConsumers PathConsumers; @@ -45,7 +44,7 @@ class AnalysisManager : public BugReporterData { public: AnalyzerOptions &options; - AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, + AnalysisManager(ASTContext &ctx, const PathDiagnosticConsumers &Consumers, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, @@ -84,10 +83,6 @@ public: return getASTContext().getSourceManager(); } - DiagnosticsEngine &getDiagnostic() override { - return Diags; - } - const LangOptions &getLangOpts() const { return LangOpts; } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index db84102983af..fc1cc9138826 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -347,7 +347,7 @@ public: ProgramStateRef invalidateRegions(unsigned BlockCount, ProgramStateRef Orig = nullptr) const; - using FrameBindingTy = std::pair<Loc, SVal>; + using FrameBindingTy = std::pair<SVal, SVal>; using BindingsTy = SmallVectorImpl<FrameBindingTy>; /// Populates the given SmallVector with the bindings in the callee's stack @@ -386,11 +386,12 @@ public: /// during analysis if the call is inlined, but it may still be useful /// in intermediate calculations even if the call isn't inlined. /// May fail; returns null on failure. - const StackFrameContext *getCalleeStackFrame() const; + const StackFrameContext *getCalleeStackFrame(unsigned BlockCount) const; /// Returns memory location for a parameter variable within the callee stack /// frame. May fail; returns null on failure. - const VarRegion *getParameterLocation(unsigned Index) const; + const VarRegion *getParameterLocation(unsigned Index, + unsigned BlockCount) const; /// Returns true if on the current path, the argument was constructed by /// calling a C++ constructor over it. This is an internal detail of the @@ -1063,8 +1064,19 @@ class CallDescription { // e.g. "{a, b}" represent the qualified names, like "a::b". std::vector<const char *> QualifiedName; Optional<unsigned> RequiredArgs; + Optional<size_t> RequiredParams; int Flags; + // A constructor helper. + static Optional<size_t> readRequiredParams(Optional<unsigned> RequiredArgs, + Optional<size_t> RequiredParams) { + if (RequiredParams) + return RequiredParams; + if (RequiredArgs) + return static_cast<size_t>(*RequiredArgs); + return None; + } + public: /// Constructs a CallDescription object. /// @@ -1077,14 +1089,17 @@ public: /// call. Omit this parameter to match every occurrence of call with a given /// name regardless the number of arguments. CallDescription(int Flags, ArrayRef<const char *> QualifiedName, - Optional<unsigned> RequiredArgs = None) + Optional<unsigned> RequiredArgs = None, + Optional<size_t> RequiredParams = None) : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs), + RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)), Flags(Flags) {} /// Construct a CallDescription with default flags. CallDescription(ArrayRef<const char *> QualifiedName, - Optional<unsigned> RequiredArgs = None) - : CallDescription(0, QualifiedName, RequiredArgs) {} + Optional<unsigned> RequiredArgs = None, + Optional<size_t> RequiredParams = None) + : CallDescription(0, QualifiedName, RequiredArgs, RequiredParams) {} /// Get the name of the function that this object matches. StringRef getFunctionName() const { return QualifiedName.back(); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 981133e66977..7f4df0d88def 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -103,7 +103,7 @@ public: return Eng.getBugReporter(); } - SourceManager &getSourceManager() { + const SourceManager &getSourceManager() { return getBugReporter().getSourceManager(); } @@ -234,7 +234,7 @@ public: } /// A shorthand version of getNoteTag that doesn't require you to accept - /// the BugReporterContext arguments when you don't need it. + /// the 'BugReporterContext' argument when you don't need it. /// /// @param Cb Callback only with 'BugReport &' parameter. /// @param IsPrunable Whether the note is prunable. It allows BugReporter @@ -247,6 +247,19 @@ public: IsPrunable); } + /// A shorthand version of getNoteTag that doesn't require you to accept + /// the arguments when you don't need it. + /// + /// @param Cb Callback without parameters. + /// @param IsPrunable Whether the note is prunable. It allows BugReporter + /// to omit the note from the report if it would make the displayed + /// bug path significantly shorter. + const NoteTag *getNoteTag(std::function<std::string()> &&Cb, + bool IsPrunable = false) { + return getNoteTag([Cb](BugReporterContext &, BugReport &) { return Cb(); }, + IsPrunable); + } + /// A shorthand version of getNoteTag that accepts a plain note. /// /// @param Note The note. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h new file mode 100644 index 000000000000..f5a710c77a6a --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h @@ -0,0 +1,55 @@ +//===- DynamicCastInfo.h - Runtime cast information -------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H + +#include "clang/AST/Type.h" + +namespace clang { +namespace ento { + +class DynamicCastInfo { +public: + enum CastResult { Success, Failure }; + + DynamicCastInfo(QualType from, QualType to, CastResult resultKind) + : From(from), To(to), ResultKind(resultKind) {} + + QualType from() const { return From; } + QualType to() const { return To; } + + bool equals(QualType from, QualType to) const { + return From == from && To == to; + } + + bool succeeds() const { return ResultKind == CastResult::Success; } + bool fails() const { return ResultKind == CastResult::Failure; } + + bool operator==(const DynamicCastInfo &RHS) const { + return From == RHS.From && To == RHS.To; + } + bool operator<(const DynamicCastInfo &RHS) const { + return From < RHS.From && To < RHS.To; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(From); + ID.Add(To); + ID.AddInteger(ResultKind); + } + +private: + QualType From, To; + CastResult ResultKind; +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h new file mode 100644 index 000000000000..356401d77561 --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h @@ -0,0 +1,73 @@ +//===- DynamicType.h - Dynamic type related APIs ----------------*- 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 defines APIs that track and query dynamic type information. This +// information can be used to devirtualize calls during the symbolic execution +// or do type checking. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H + +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/Optional.h" +#include <utility> + +namespace clang { +namespace ento { + +/// Get dynamic type information for the region \p MR. +DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR); + +/// Get raw dynamic type information for the region \p MR. +const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State, + const MemRegion *MR); + +/// Get dynamic cast information from \p CastFromTy to \p CastToTy of \p MR. +const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy); + +/// Set dynamic type information of the region; return the new state. +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + DynamicTypeInfo NewTy); + +/// Set dynamic type information of the region; return the new state. +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + QualType NewTy, bool CanBeSubClassed = true); + +/// Set dynamic type and cast information of the region; return the new state. +ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy, + bool IsCastSucceeds); + +/// Removes the dead type informations from \p State. +ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR); + +/// Removes the dead cast informations from \p State. +ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR); + +void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, + const char *NL = "\n", unsigned int Space = 0, + bool IsDot = false); + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h index 9bb1e2137566..6262c4a1ce37 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h @@ -1,10 +1,11 @@ -//== DynamicTypeInfo.h - Runtime type information ----------------*- C++ -*--=// +//===- DynamicTypeInfo.h - Runtime type information -------------*- 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 // //===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H @@ -16,36 +17,37 @@ namespace ento { /// Stores the currently inferred strictest bound on the runtime type /// of a region in a given state along the analysis path. class DynamicTypeInfo { -private: - QualType T; - bool CanBeASubClass; - public: + DynamicTypeInfo() : DynTy(QualType()) {} - DynamicTypeInfo() : T(QualType()) {} - DynamicTypeInfo(QualType WithType, bool CanBeSub = true) - : T(WithType), CanBeASubClass(CanBeSub) {} + DynamicTypeInfo(QualType Ty, bool CanBeSub = true) + : DynTy(Ty), CanBeASubClass(CanBeSub) {} + + /// Returns false if the type information is precise (the type 'DynTy' is + /// the only type in the lattice), true otherwise. + bool canBeASubClass() const { return CanBeASubClass; } - /// Return false if no dynamic type info is available. - bool isValid() const { return !T.isNull(); } + /// Returns true if the dynamic type info is available. + bool isValid() const { return !DynTy.isNull(); } /// Returns the currently inferred upper bound on the runtime type. - QualType getType() const { return T; } + QualType getType() const { return DynTy; } - /// Returns false if the type information is precise (the type T is - /// the only type in the lattice), true otherwise. - bool canBeASubClass() const { return CanBeASubClass; } + bool operator==(const DynamicTypeInfo &RHS) const { + return DynTy == RHS.DynTy && CanBeASubClass == RHS.CanBeASubClass; + } void Profile(llvm::FoldingSetNodeID &ID) const { - ID.Add(T); - ID.AddInteger((unsigned)CanBeASubClass); - } - bool operator==(const DynamicTypeInfo &X) const { - return T == X.T && CanBeASubClass == X.CanBeASubClass; + ID.Add(DynTy); + ID.AddBoolean(CanBeASubClass); } + +private: + QualType DynTy; + bool CanBeASubClass; }; -} // end ento -} // end clang +} // namespace ento +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h deleted file mode 100644 index a84b24872061..000000000000 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h +++ /dev/null @@ -1,63 +0,0 @@ -//===- DynamicTypeMap.h - Dynamic type map ----------------------*- 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 provides APIs for tracking dynamic type information. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H -#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H - -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "llvm/ADT/ImmutableMap.h" -#include "clang/AST/Type.h" - -namespace clang { -namespace ento { - -class MemRegion; - -/// The GDM component containing the dynamic type info. This is a map from a -/// symbol to its most likely type. -struct DynamicTypeMap {}; - -using DynamicTypeMapTy = llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo>; - -template <> -struct ProgramStateTrait<DynamicTypeMap> - : public ProgramStatePartialTrait<DynamicTypeMapTy> { - static void *GDMIndex(); -}; - -/// Get dynamic type information for a region. -DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, - const MemRegion *Reg); - -/// Set dynamic type information of the region; return the new state. -ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, - DynamicTypeInfo NewTy); - -/// Set dynamic type information of the region; return the new state. -inline ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, - const MemRegion *Reg, QualType NewTy, - bool CanBeSubClassed = true) { - return setDynamicTypeInfo(State, Reg, - DynamicTypeInfo(NewTy, CanBeSubClassed)); -} - -void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, - const char *NL = "\n", unsigned int Space = 0, - bool IsDot = false); - -} // namespace ento -} // namespace clang - -#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index 727d04cba278..e87772c04b9b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -131,10 +131,12 @@ class ExplodedNode : public llvm::FoldingSetNode { /// Succs - The successors of this node. NodeGroup Succs; + int64_t Id; + public: explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, - bool IsSink) - : Location(loc), State(std::move(state)), Succs(IsSink) { + int64_t Id, bool IsSink) + : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) { assert(isSink() == IsSink); } @@ -153,7 +155,11 @@ public: CFG &getCFG() const { return *getLocationContext()->getCFG(); } - ParentMap &getParentMap() const {return getLocationContext()->getParentMap();} + const CFGBlock *getCFGBlock() const; + + const ParentMap &getParentMap() const { + return getLocationContext()->getParentMap(); + } template <typename T> T &getAnalysis() const { @@ -219,12 +225,20 @@ public: // Iterators over successor and predecessor vertices. using succ_iterator = ExplodedNode * const *; + using succ_range = llvm::iterator_range<succ_iterator>; + using const_succ_iterator = const ExplodedNode * const *; + using const_succ_range = llvm::iterator_range<const_succ_iterator>; + using pred_iterator = ExplodedNode * const *; + using pred_range = llvm::iterator_range<pred_iterator>; + using const_pred_iterator = const ExplodedNode * const *; + using const_pred_range = llvm::iterator_range<const_pred_iterator>; pred_iterator pred_begin() { return Preds.begin(); } pred_iterator pred_end() { return Preds.end(); } + pred_range preds() { return {Preds.begin(), Preds.end()}; } const_pred_iterator pred_begin() const { return const_cast<ExplodedNode*>(this)->pred_begin(); @@ -232,9 +246,11 @@ public: const_pred_iterator pred_end() const { return const_cast<ExplodedNode*>(this)->pred_end(); } + const_pred_range preds() const { return {Preds.begin(), Preds.end()}; } succ_iterator succ_begin() { return Succs.begin(); } succ_iterator succ_end() { return Succs.end(); } + succ_range succs() { return {Succs.begin(), Succs.end()}; } const_succ_iterator succ_begin() const { return const_cast<ExplodedNode*>(this)->succ_begin(); @@ -242,8 +258,9 @@ public: const_succ_iterator succ_end() const { return const_cast<ExplodedNode*>(this)->succ_end(); } + const_succ_range succs() const { return {Succs.begin(), Succs.end()}; } - int64_t getID(ExplodedGraph *G) const; + int64_t getID() const { return Id; } /// The node is trivial if it has only one successor, only one predecessor, /// it's predecessor has only one successor, @@ -252,6 +269,30 @@ public: /// Trivial nodes may be skipped while printing exploded graph. bool isTrivial() const; + /// If the node's program point corresponds to a statement, retrieve that + /// statement. Useful for figuring out where to put a warning or a note. + /// If the statement belongs to a body-farmed definition, + /// retrieve the call site for that definition. + const Stmt *getStmtForDiagnostics() const; + + /// Find the next statement that was executed on this node's execution path. + /// Useful for explaining control flow that follows the current node. + /// If the statement belongs to a body-farmed definition, retrieve the + /// call site for that definition. + const Stmt *getNextStmtForDiagnostics() const; + + /// Find the statement that was executed immediately before this node. + /// Useful when the node corresponds to a CFG block entrance. + /// If the statement belongs to a body-farmed definition, retrieve the + /// call site for that definition. + const Stmt *getPreviousStmtForDiagnostics() const; + + /// Find the statement that was executed at or immediately before this node. + /// Useful when any nearby statement will do. + /// If the statement belongs to a body-farmed definition, retrieve the + /// call site for that definition. + const Stmt *getCurrentOrPreviousStmtForDiagnostics() const; + private: void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); } void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } @@ -285,7 +326,7 @@ protected: BumpVectorContext BVC; /// NumNodes - The number of nodes in the graph. - unsigned NumNodes = 0; + int64_t NumNodes = 0; /// A list of recently allocated nodes that can potentially be recycled. NodeVector ChangedNodes; @@ -319,10 +360,11 @@ public: /// ExplodedGraph for further processing. ExplodedNode *createUncachedNode(const ProgramPoint &L, ProgramStateRef State, + int64_t Id, bool IsSink = false); std::unique_ptr<ExplodedGraph> MakeEmptyGraph() const { - return llvm::make_unique<ExplodedGraph>(); + return std::make_unique<ExplodedGraph>(); } /// addRoot - Add an untyped node to the set of roots. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 2629d7121de4..2d0967616ff2 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -145,9 +145,9 @@ private: ObjCNoReturn ObjCNoRet; /// The BugReporter associated with this engine. It is important that - /// this object be placed at the very end of member variables so that its - /// destructor is called before the rest of the ExprEngine is destroyed. - GRBugReporter BR; + /// this object be placed at the very end of member variables so that its + /// destructor is called before the rest of the ExprEngine is destroyed. + PathSensitiveBugReporter BR; /// The functions which have been analyzed through inlining. This is owned by /// AnalysisConsumer. It can be null. @@ -530,7 +530,7 @@ public: void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst, - const EvalCallOptions &Options); + EvalCallOptions &Options); void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, ExplodedNode *Pred, @@ -666,7 +666,7 @@ public: const LocationContext *LCtx, ProgramStateRef State); - /// Evaluate a call, running pre- and post-call checks and allowing checkers + /// Evaluate a call, running pre- and post-call checkers and allowing checkers /// to be responsible for handling the evaluation of the call itself. void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 071e35085a5f..71cbbe28fc25 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -169,6 +169,7 @@ public: Kind getKind() const { return kind; } template<typename RegionTy> const RegionTy* getAs() const; + template<typename RegionTy> const RegionTy* castAs() const; virtual bool isBoundable() const { return false; } @@ -1231,6 +1232,11 @@ const RegionTy* MemRegion::getAs() const { return nullptr; } +template<typename RegionTy> +const RegionTy* MemRegion::castAs() const { + return cast<RegionTy>(this); +} + //===----------------------------------------------------------------------===// // MemRegionManager - Factory object for creating regions. //===----------------------------------------------------------------------===// diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index d38058f9af56..07920790c80a 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -507,6 +507,10 @@ public: return *svalBuilder; } + const SValBuilder &getSValBuilder() const { + return *svalBuilder; + } + SymbolManager &getSymbolManager() { return svalBuilder->getSymbolManager(); } |