diff options
Diffstat (limited to 'include/clang/StaticAnalyzer/Core')
46 files changed, 3738 insertions, 1515 deletions
diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index ce50cc582d1e0..9d292cfddb0c8 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -1,4 +1,4 @@ -//===--- AnalyzerOptions.h - Analysis Engine Options ------------*- C++ -*-===// +//===- AnalyzerOptions.h - Analysis Engine Options --------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -19,18 +19,18 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include <string> +#include <utility> #include <vector> namespace clang { -class ASTConsumer; -class DiagnosticsEngine; -class Preprocessor; -class LangOptions; namespace ento { + class CheckerBase; -} + +} // namespace ento /// Analysis - Set of available source code analyses. enum Analyses { @@ -76,7 +76,7 @@ enum AnalysisInliningMode { NumInliningModes }; -/// \brief Describes the different kinds of C++ member functions which can be +/// Describes the different kinds of C++ member functions which can be /// considered for inlining by the analyzer. /// /// These options are cumulative; enabling one kind of member function will @@ -100,7 +100,7 @@ enum CXXInlineableMemberKind { CIMK_Destructors }; -/// \brief Describes the different modes of inter-procedural analysis. +/// Describes the different modes of inter-procedural analysis. enum IPAKind { IPAK_NotSet = 0, @@ -123,28 +123,31 @@ enum IPAKind { class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> { public: - typedef llvm::StringMap<std::string> ConfigTable; + using ConfigTable = llvm::StringMap<std::string>; static std::vector<StringRef> getRegisteredCheckers(bool IncludeExperimental = false); - /// \brief Pair of checker name and enable/disable. - std::vector<std::pair<std::string, bool> > CheckersControlList; + /// Pair of checker name and enable/disable. + std::vector<std::pair<std::string, bool>> CheckersControlList; - /// \brief A key-value table of use-specified configuration values. + /// A key-value table of use-specified configuration values. ConfigTable Config; - AnalysisStores AnalysisStoreOpt; - AnalysisConstraints AnalysisConstraintsOpt; - AnalysisDiagClients AnalysisDiagOpt; - AnalysisPurgeMode AnalysisPurgeOpt; + AnalysisStores AnalysisStoreOpt = RegionStoreModel; + AnalysisConstraints AnalysisConstraintsOpt = RangeConstraintsModel; + AnalysisDiagClients AnalysisDiagOpt = PD_HTML; + AnalysisPurgeMode AnalysisPurgeOpt = PurgeStmt; std::string AnalyzeSpecificFunction; + + /// Store full compiler invocation for reproducible instructions in the + /// generated report. + std::string FullCompilerInvocation; - /// \brief The maximum number of times the analyzer visits a block. + /// The maximum number of times the analyzer visits a block. unsigned maxBlockVisitOnPath; - - /// \brief Disable all analyzer checks. + /// Disable all analyzer checks. /// /// This flag allows one to disable analyzer checks on the code processed by /// the given analysis consumer. Note, the code will get parsed and the @@ -157,7 +160,7 @@ public: unsigned AnalyzerDisplayProgress : 1; unsigned AnalyzeNestedBlocks : 1; - /// \brief The flag regulates if we should eagerly assume evaluations of + /// The flag regulates if we should eagerly assume evaluations of /// conditionals, thus, bifurcating the path. /// /// This flag indicates how the engine should handle expressions such as: 'x = @@ -174,22 +177,36 @@ public: unsigned UnoptimizedCFG : 1; unsigned PrintStats : 1; - /// \brief Do not re-analyze paths leading to exhausted nodes with a different + /// Do not re-analyze paths leading to exhausted nodes with a different /// strategy. We get better code coverage when retry is enabled. unsigned NoRetryExhausted : 1; - /// \brief The inlining stack depth limit. - unsigned InlineMaxStackDepth; + /// The inlining stack depth limit. + // Cap the stack depth at 4 calls (5 stack frames, base + 4 calls). + unsigned InlineMaxStackDepth = 5; - /// \brief The mode of function selection used during inlining. - AnalysisInliningMode InliningMode; + /// The mode of function selection used during inlining. + AnalysisInliningMode InliningMode = NoRedundancy; + + enum class ExplorationStrategyKind { + DFS, + BFS, + UnexploredFirst, + UnexploredFirstQueue, + BFSBlockDFSContents, + NotSet + }; private: - /// \brief Describes the kinds for high-level analyzer mode. + ExplorationStrategyKind ExplorationStrategy = ExplorationStrategyKind::NotSet; + + /// Describes the kinds for high-level analyzer mode. enum UserModeKind { UMK_NotSet = 0, + /// Perform shallow but fast analyzes. UMK_Shallow = 1, + /// Perform deep analyzes. UMK_Deep = 2 }; @@ -197,10 +214,10 @@ private: /// Controls the high-level analyzer mode, which influences the default /// settings for some of the lower-level config options (such as IPAMode). /// \sa getUserMode - UserModeKind UserMode; + UserModeKind UserMode = UMK_NotSet; /// Controls the mode of inter-procedural analysis. - IPAKind IPAMode; + IPAKind IPAMode = IPAK_NotSet; /// Controls which C++ member functions will be considered for inlining. CXXInlineableMemberKind CXXMemberInliningMode; @@ -217,9 +234,15 @@ private: /// \sa IncludeLoopExitInCFG Optional<bool> IncludeLoopExitInCFG; + /// \sa IncludeRichConstructorsInCFG + Optional<bool> IncludeRichConstructorsInCFG; + /// \sa mayInlineCXXStandardLibrary Optional<bool> InlineCXXStandardLibrary; + /// \sa includeScopesInCFG + Optional<bool> IncludeScopesInCFG; + /// \sa mayInlineTemplateFunctions Optional<bool> InlineTemplateFunctions; @@ -232,6 +255,9 @@ private: /// \sa mayInlineCXXSharedPtrDtor Optional<bool> InlineCXXSharedPtrDtor; + /// \sa mayInlineCXXTemporaryDtors + Optional<bool> InlineCXXTemporaryDtors; + /// \sa mayInlineObjCMethod Optional<bool> ObjCInliningMode; @@ -254,15 +280,23 @@ private: /// \sa shouldSuppressFromCXXStandardLibrary Optional<bool> SuppressFromCXXStandardLibrary; + /// \sa shouldCrosscheckWithZ3 + Optional<bool> CrosscheckWithZ3; + /// \sa reportIssuesInMainSourceFile Optional<bool> ReportIssuesInMainSourceFile; /// \sa StableReportFilename Optional<bool> StableReportFilename; + Optional<bool> SerializeStats; + /// \sa getGraphTrimInterval Optional<unsigned> GraphTrimInterval; + /// \sa getMaxSymbolComplexity + Optional<unsigned> MaxSymbolComplexity; + /// \sa getMaxTimesInlineLarge Optional<unsigned> MaxTimesInlineLarge; @@ -284,6 +318,22 @@ private: /// \sa shouldDisplayNotesAsEvents Optional<bool> DisplayNotesAsEvents; + /// \sa shouldAggressivelySimplifyBinaryOperation + Optional<bool> AggressiveBinaryOperationSimplification; + + /// \sa getCTUDir + Optional<StringRef> CTUDir; + + /// \sa getCTUIndexName + Optional<StringRef> CTUIndexName; + + /// \sa naiveCTUEnabled + Optional<bool> NaiveCTU; + + /// \sa shouldElideConstructors + Optional<bool> ElideConstructors; + + /// A helper function that retrieves option for a given full-qualified /// checker name. /// Options for checkers can be specified via 'analyzer-config' command-line @@ -311,6 +361,15 @@ private: bool SearchInParents = false); public: + AnalyzerOptions() + : DisableAllChecks(false), ShowCheckerHelp(false), + ShowEnabledCheckerList(false), AnalyzeAll(false), + AnalyzerDisplayProgress(false), AnalyzeNestedBlocks(false), + eagerlyAssumeBinOpBifurcation(false), TrimGraph(false), + visualizeExplodedGraphWithGraphViz(false), + visualizeExplodedGraphWithUbiGraph(false), UnoptimizedCFG(false), + PrintStats(false), NoRetryExhausted(false), CXXMemberInliningMode() {} + /// Interprets an option's string value as a boolean. The "true" string is /// interpreted as true and the "false" string is interpreted as false. /// @@ -320,7 +379,7 @@ public: /// specified. /// @param [in] C The optional checker parameter that can be used to restrict /// the search to the options of this particular checker (and its parents - /// dependening on search mode). + /// depending on search mode). /// @param [in] SearchInParents If set to true and the searched option was not /// specified for the given checker the options for the parent packages will /// be searched as well. The inner packages take precedence over the outer @@ -338,7 +397,7 @@ public: /// specified. /// @param [in] C The optional checker parameter that can be used to restrict /// the search to the options of this particular checker (and its parents - /// dependening on search mode). + /// depending on search mode). /// @param [in] SearchInParents If set to true and the searched option was not /// specified for the given checker the options for the parent packages will /// be searched as well. The inner packages take precedence over the outer @@ -355,7 +414,7 @@ public: /// specified. /// @param [in] C The optional checker parameter that can be used to restrict /// the search to the options of this particular checker (and its parents - /// dependening on search mode). + /// depending on search mode). /// @param [in] SearchInParents If set to true and the searched option was not /// specified for the given checker the options for the parent packages will /// be searched as well. The inner packages take precedence over the outer @@ -372,7 +431,7 @@ public: /// specified. /// @param [in] C The optional checker parameter that can be used to restrict /// the search to the options of this particular checker (and its parents - /// dependening on search mode). + /// depending on search mode). /// @param [in] SearchInParents If set to true and the searched option was not /// specified for the given checker the options for the parent packages will /// be searched as well. The inner packages take precedence over the outer @@ -381,12 +440,14 @@ public: const ento::CheckerBase *C = nullptr, bool SearchInParents = false); - /// \brief Retrieves and sets the UserMode. This is a high-level option, + /// Retrieves and sets the UserMode. This is a high-level option, /// which is used to set other low-level options. It is not accessible /// outside of AnalyzerOptions. UserModeKind getUserMode(); - /// \brief Returns the inter-procedural analysis mode. + ExplorationStrategyKind getExplorationStrategy(); + + /// Returns the inter-procedural analysis mode. IPAKind getIPAMode(); /// Returns the option controlling which C++ member functions will be @@ -428,6 +489,19 @@ public: /// the values "true" and "false". bool includeLoopExitInCFG(); + /// Returns whether or not construction site information should be included + /// in the CFG C++ constructor elements. + /// + /// This is controlled by the 'cfg-rich-constructors' config options, + /// which accepts the values "true" and "false". + bool includeRichConstructorsInCFG(); + + /// Returns whether or not scope information should be included in the CFG. + /// + /// This is controlled by the 'cfg-scope-info' config option, which accepts + /// the values "true" and "false". + bool includeScopesInCFG(); + /// Returns whether or not C++ standard library functions may be considered /// for inlining. /// @@ -464,6 +538,17 @@ public: /// accepts the values "true" and "false". bool mayInlineCXXSharedPtrDtor(); + /// Returns true if C++ temporary destructors should be inlined during + /// analysis. + /// + /// If temporary destructors are disabled in the CFG via the + /// 'cfg-temporary-dtors' option, temporary destructors would not be + /// inlined anyway. + /// + /// This is controlled by the 'c++-temp-dtor-inlining' config option, which + /// accepts the values "true" and "false". + bool mayInlineCXXTemporaryDtors(); + /// Returns whether or not paths that go through null returns should be /// suppressed. /// @@ -499,6 +584,13 @@ public: /// which accepts the values "true" and "false". bool shouldSuppressFromCXXStandardLibrary(); + /// Returns whether bug reports should be crosschecked with the Z3 + /// constraint manager backend. + /// + /// This is controlled by the 'crosscheck-with-z3' config option, + /// which accepts the values "true" and "false". + bool shouldCrosscheckWithZ3(); + /// Returns whether or not the diagnostic report should be always reported /// in the main source file and not the headers. /// @@ -512,6 +604,14 @@ public: /// which accepts the values "true" and "false". Default = false bool shouldWriteStableReportFilename(); + /// \return Whether the analyzer should + /// serialize statistics to plist output. + /// Statistics would be serialized in JSON format inside the main dictionary + /// under the \c statistics key. + /// Available only if compiled in assert mode or with LLVM statistics + /// explicitly enabled. + bool shouldSerializeStats(); + /// Returns whether irrelevant parts of a bug report path should be pruned /// out of the final output. /// @@ -546,6 +646,11 @@ public: /// node reclamation, set the option to "0". unsigned getGraphTrimInterval(); + /// Returns the maximum complexity of symbolic constraint (50 by default). + /// + /// This is controlled by "-analyzer-config max-symbol-complexity" option. + unsigned getMaxSymbolComplexity(); + /// Returns the maximum times a large function could be inlined. /// /// This is controlled by the 'max-times-inline-large' config option. @@ -585,36 +690,41 @@ public: /// to false when unset. bool shouldDisplayNotesAsEvents(); -public: - AnalyzerOptions() : - AnalysisStoreOpt(RegionStoreModel), - AnalysisConstraintsOpt(RangeConstraintsModel), - AnalysisDiagOpt(PD_HTML), - AnalysisPurgeOpt(PurgeStmt), - DisableAllChecks(0), - ShowCheckerHelp(0), - ShowEnabledCheckerList(0), - AnalyzeAll(0), - AnalyzerDisplayProgress(0), - AnalyzeNestedBlocks(0), - eagerlyAssumeBinOpBifurcation(0), - TrimGraph(0), - visualizeExplodedGraphWithGraphViz(0), - visualizeExplodedGraphWithUbiGraph(0), - UnoptimizedCFG(0), - PrintStats(0), - NoRetryExhausted(0), - // Cap the stack depth at 4 calls (5 stack frames, base + 4 calls). - InlineMaxStackDepth(5), - InliningMode(NoRedundancy), - UserMode(UMK_NotSet), - IPAMode(IPAK_NotSet), - CXXMemberInliningMode() {} - + /// Returns true if SValBuilder should rearrange comparisons and additive + /// operations of symbolic expressions which consist of a sum of a symbol and + /// a concrete integer into the format where symbols are on the left-hand + /// side and the integer is on the right. This is only done if both symbols + /// and both concrete integers are signed, greater than or equal to the + /// quarter of the minimum value of the type and less than or equal to the + /// quarter of the maximum value of that type. + /// + /// A + n <OP> B + m becomes A - B <OP> m - n, where A and B symbolic, + /// n and m are integers. <OP> is any of '==', '!=', '<', '<=', '>', '>=', + /// '+' or '-'. The rearrangement also happens with '-' instead of '+' on + // either or both side and also if any or both integers are missing. + bool shouldAggressivelySimplifyBinaryOperation(); + + /// Returns the directory containing the CTU related files. + StringRef getCTUDir(); + + /// Returns the name of the file containing the CTU index of functions. + StringRef getCTUIndexName(); + + /// Returns true when naive cross translation unit analysis is enabled. + /// This is an experimental feature to inline functions from another + /// translation units. + bool naiveCTUEnabled(); + + /// Returns true if elidable C++ copy-constructors and move-constructors + /// should be actually elided during analysis. Both behaviors are allowed + /// by the C++ standard, and the analyzer, like CodeGen, defaults to eliding. + /// Starting with C++17 some elisions become mandatory, and in these cases + /// the option will be ignored. + bool shouldElideConstructors(); }; -typedef IntrusiveRefCntPtr<AnalyzerOptions> AnalyzerOptionsRef; +using AnalyzerOptionsRef = IntrusiveRefCntPtr<AnalyzerOptions>; -} +} // namespace clang -#endif +#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 cd1355d03b639..111e1d1e8e20a 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -1,4 +1,4 @@ -//===--- BugReporter.h - Generate PathDiagnostics --------------*- C++ -*-===// +//===- BugReporter.h - Generate PathDiagnostics -----------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -15,78 +15,101 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.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" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/None.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" +#include "llvm/ADT/iterator_range.h" +#include <cassert> +#include <memory> +#include <string> +#include <utility> +#include <vector> namespace clang { +class AnalyzerOptions; class ASTContext; +class Decl; class DiagnosticsEngine; +class LocationContext; +class SourceManager; class Stmt; -class ParentMap; namespace ento { -class PathDiagnostic; -class ExplodedNode; +class BugType; +class CheckerBase; class ExplodedGraph; -class BugReport; -class BugReporter; -class BugReporterContext; +class ExplodedNode; class ExprEngine; -class BugType; +class MemRegion; +class SValBuilder; //===----------------------------------------------------------------------===// // Interface for individual bug reports. //===----------------------------------------------------------------------===// +/// A mapping from diagnostic consumers to the diagnostics they should +/// consume. +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> { public: class NodeResolver { virtual void anchor(); + public: - virtual ~NodeResolver() {} + virtual ~NodeResolver() = default; + virtual const ExplodedNode* getOriginalNode(const ExplodedNode *N) = 0; }; - typedef const SourceRange *ranges_iterator; - typedef SmallVector<std::unique_ptr<BugReporterVisitor>, 8> VisitorList; - typedef VisitorList::iterator visitor_iterator; - typedef SmallVector<StringRef, 2> ExtraTextList; - typedef SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4> NoteList; + 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>; protected: - friend class BugReporter; friend class BugReportEquivClass; + friend class BugReporter; BugType& BT; - const Decl *DeclWithIssue; + const Decl *DeclWithIssue = nullptr; std::string ShortDescription; std::string Description; PathDiagnosticLocation Location; PathDiagnosticLocation UniqueingLocation; const Decl *UniqueingDecl; - const ExplodedNode *ErrorNode; + const ExplodedNode *ErrorNode = nullptr; SmallVector<SourceRange, 4> Ranges; ExtraTextList ExtraText; NoteList Notes; - typedef llvm::DenseSet<SymbolRef> Symbols; - typedef llvm::DenseSet<const MemRegion *> Regions; + using Symbols = llvm::DenseSet<SymbolRef>; + using Regions = llvm::DenseSet<const MemRegion *>; /// A (stack of) a set of symbols that are registered with this /// report as being "interesting", and thus used to help decide which @@ -113,20 +136,16 @@ protected: /// Used for ensuring the visitors are only added once. llvm::FoldingSet<BugReporterVisitor> CallbacksSet; - /// Used for clients to tell if the report's configuration has changed - /// since the last time they checked. - unsigned ConfigurationChangeToken; - /// When set, this flag disables all callstack pruning from a diagnostic /// path. This is useful for some reports that want maximum fidelty /// when reporting an issue. - bool DoNotPrunePath; + bool DoNotPrunePath = false; /// Used to track unique reasons why a bug report might be invalid. /// /// \sa markInvalid /// \sa removeInvalidation - typedef std::pair<const void *, const void *> InvalidationRecord; + using InvalidationRecord = std::pair<const void *, const void *>; /// If non-empty, this bug report is likely a false positive and should not be /// shown to the user. @@ -146,20 +165,17 @@ private: public: BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode) - : BT(bt), DeclWithIssue(nullptr), Description(desc), ErrorNode(errornode), - ConfigurationChangeToken(0), DoNotPrunePath(false) {} + : BT(bt), Description(desc), ErrorNode(errornode) {} BugReport(BugType& bt, StringRef shortDesc, StringRef desc, const ExplodedNode *errornode) - : BT(bt), DeclWithIssue(nullptr), ShortDescription(shortDesc), - Description(desc), ErrorNode(errornode), ConfigurationChangeToken(0), - DoNotPrunePath(false) {} + : BT(bt), ShortDescription(shortDesc), Description(desc), + ErrorNode(errornode) {} BugReport(BugType &bt, StringRef desc, PathDiagnosticLocation l) - : BT(bt), DeclWithIssue(nullptr), Description(desc), Location(l), - ErrorNode(nullptr), ConfigurationChangeToken(0), DoNotPrunePath(false) {} + : BT(bt), Description(desc), Location(l) {} - /// \brief Create a BugReport with a custom uniqueing location. + /// Create a BugReport 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 @@ -168,18 +184,15 @@ public: /// 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), DeclWithIssue(nullptr), Description(desc), - UniqueingLocation(LocationToUnique), - UniqueingDecl(DeclToUnique), - ErrorNode(errornode), ConfigurationChangeToken(0), - DoNotPrunePath(false) {} + : BT(bt), Description(desc), UniqueingLocation(LocationToUnique), + UniqueingDecl(DeclToUnique), ErrorNode(errornode) {} virtual ~BugReport(); const BugType& getBugType() const { return BT; } BugType& getBugType() { return BT; } - /// \brief True when the report has an execution path associated with it. + /// 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. @@ -218,10 +231,6 @@ public: bool isInteresting(SVal V); bool isInteresting(const LocationContext *LC); - unsigned getConfigurationChangeToken() const { - return ConfigurationChangeToken; - } - /// Returns whether or not this report should be considered valid. /// /// Invalid reports are those that have been classified as likely false @@ -242,13 +251,6 @@ public: void markInvalid(const void *Tag, const void *Data) { Invalidations.insert(std::make_pair(Tag, Data)); } - - /// Reverses the effects of a previous invalidation. - /// - /// \sa markInvalid - void removeInvalidation(const void *Tag, const void *Data) { - Invalidations.erase(std::make_pair(Tag, Data)); - } /// Return the canonical declaration, be it a method or class, where /// this issue semantically occurred. @@ -286,7 +288,7 @@ public: return Notes; } - /// \brief This allows for addition of meta data to the diagnostic. + /// This allows for addition of meta data to the diagnostic. /// /// Currently, only the HTMLDiagnosticClient knows how to display it. void addExtraText(StringRef S) { @@ -297,26 +299,26 @@ public: return ExtraText; } - /// \brief Return the "definitive" location of the reported bug. + /// 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; - /// \brief Get the location on which the report should be uniqued. + /// Get the location on which the report should be uniqued. PathDiagnosticLocation getUniqueingLocation() const { return UniqueingLocation; } - /// \brief Get the declaration containing the uniqueing location. + /// Get the declaration containing the uniqueing location. const Decl *getUniqueingDecl() const { return UniqueingDecl; } const Stmt *getStmt() const; - /// \brief Add a range to a bug report. + /// 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. @@ -329,10 +331,10 @@ public: Ranges.push_back(R); } - /// \brief Get the SourceRanges associated with the report. + /// Get the SourceRanges associated with the report. virtual llvm::iterator_range<ranges_iterator> getRanges(); - /// \brief Add custom or predefined bug report visitors to this report. + /// Add custom or predefined bug report visitors to this report. /// /// The visitors should be used when the default trace is not sufficient. /// For example, they allow constructing a more elaborate trace. @@ -341,6 +343,9 @@ public: /// registerVarDeclsLastStore(). void addVisitor(std::unique_ptr<BugReporterVisitor> visitor); + /// Remove all visitors attached to this bug report. + void clearVisitors(); + /// Iterators through the custom diagnostic visitors. visitor_iterator visitor_begin() { return Callbacks.begin(); } visitor_iterator visitor_end() { return Callbacks.end(); } @@ -356,10 +361,11 @@ public: //===----------------------------------------------------------------------===// class BugReportEquivClass : public llvm::FoldingSetNode { + friend class BugReporter; + /// List of *owned* BugReport objects. llvm::ilist<BugReport> Reports; - friend class BugReporter; void AddReport(std::unique_ptr<BugReport> R) { Reports.push_back(R.release()); } @@ -373,8 +379,8 @@ public: Reports.front().Profile(ID); } - typedef llvm::ilist<BugReport>::iterator iterator; - typedef llvm::ilist<BugReport>::const_iterator const_iterator; + using iterator = llvm::ilist<BugReport>::iterator; + using const_iterator = llvm::ilist<BugReport>::const_iterator; iterator begin() { return Reports.begin(); } iterator end() { return Reports.end(); } @@ -390,22 +396,26 @@ public: class BugReporterData { public: virtual ~BugReporterData(); + virtual DiagnosticsEngine& getDiagnostic() = 0; virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0; virtual ASTContext &getASTContext() = 0; - virtual SourceManager& getSourceManager() = 0; - virtual AnalyzerOptions& getAnalyzerOptions() = 0; + virtual SourceManager &getSourceManager() = 0; + virtual AnalyzerOptions &getAnalyzerOptions() = 0; }; /// BugReporter is a utility class for generating PathDiagnostics for analysis. /// It collects the BugReports and BugTypes and knows how to generate /// and flush the corresponding diagnostics. +/// +/// The base class is used for generating path-insensitive class BugReporter { public: enum Kind { BaseBRKind, GRBugReporterKind }; private: - typedef llvm::ImmutableSet<BugType*> BugTypesTy; + using BugTypesTy = llvm::ImmutableSet<BugType *>; + BugTypesTy::Factory F; BugTypesTy BugTypes; @@ -415,27 +425,28 @@ private: /// Generate and flush the diagnostics for the given bug report. void FlushReport(BugReportEquivClass& EQ); - /// Generate and flush the diagnostics for the given bug report - /// and PathDiagnosticConsumer. - void FlushReport(BugReport *exampleReport, - PathDiagnosticConsumer &PD, - ArrayRef<BugReport*> BugReports); + /// 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) {} + 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) + : BugTypes(F.getEmptySet()), kind(BaseBRKind), D(d) {} virtual ~BugReporter(); - /// \brief Generate and flush diagnostics for all bug reports. + /// Generate and flush diagnostics for all bug reports. void FlushReports(); Kind getKind() const { return kind; } @@ -448,33 +459,31 @@ public: return D.getPathDiagnosticConsumers(); } - /// \brief Iterator over the set of BugTypes tracked by the BugReporter. - typedef BugTypesTy::iterator iterator; + /// Iterator over the set of BugTypes tracked by the BugReporter. + using iterator = BugTypesTy::iterator; iterator begin() { return BugTypes.begin(); } iterator end() { return BugTypes.end(); } - /// \brief Iterator over the set of BugReports tracked by the BugReporter. - typedef llvm::FoldingSet<BugReportEquivClass>::iterator EQClasses_iterator; + /// Iterator over the set of BugReports tracked by the BugReporter. + using EQClasses_iterator = llvm::FoldingSet<BugReportEquivClass>::iterator; EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); } EQClasses_iterator EQClasses_end() { return EQClasses.end(); } ASTContext &getContext() { return D.getASTContext(); } - SourceManager& getSourceManager() { return D.getSourceManager(); } + SourceManager &getSourceManager() { return D.getSourceManager(); } - AnalyzerOptions& getAnalyzerOptions() { return D.getAnalyzerOptions(); } + AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } - virtual bool generatePathDiagnostic(PathDiagnostic& pathDiagnostic, - PathDiagnosticConsumer &PC, - ArrayRef<BugReport *> &bugReports) { - return true; + virtual std::unique_ptr<DiagnosticForConsumerMapTy> + generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> &bugReports) { + return {}; } - bool RemoveUnneededCalls(PathPieces &pieces, BugReport *R); - void Register(BugType *BT); - /// \brief Add the given report to the set of reports tracked by BugReporter. + /// 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 @@ -494,25 +503,22 @@ public: private: llvm::StringMap<BugType *> StrBugTypes; - /// \brief Returns a BugType that is associated with the given name and + /// Returns a BugType that is associated with the given name and /// category. BugType *getBugTypeForName(CheckName CheckName, StringRef name, StringRef category); }; -// FIXME: Get rid of GRBugReporter. It's the wrong abstraction. +/// GRBugReporter is used for generating path-sensitive reports. class GRBugReporter : public BugReporter { ExprEngine& Eng; + public: GRBugReporter(BugReporterData& d, ExprEngine& eng) - : BugReporter(d, GRBugReporterKind), Eng(eng) {} + : BugReporter(d, GRBugReporterKind), Eng(eng) {} ~GRBugReporter() override; - /// getEngine - Return the analysis engine used to analyze a given - /// function or method. - ExprEngine &getEngine() { return Eng; } - /// getGraph - Get the exploded graph created by the analysis engine /// for the analyzed method or function. ExplodedGraph &getGraph(); @@ -521,16 +527,14 @@ public: /// engine. ProgramStateManager &getStateManager(); - /// Generates a path corresponding to one of the given bug reports. - /// - /// Which report is used for path generation is not specified. The - /// bug reporter will try to pick the shortest path, but this is not - /// guaranteed. + /// \p bugReports A set of bug reports within a *single* equivalence class /// - /// \return True if the report was valid and a path was generated, - /// false if the reports should be considered invalid. - bool generatePathDiagnostic(PathDiagnostic &PD, PathDiagnosticConsumer &PC, - ArrayRef<BugReport*> &bugReports) override; + /// \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; /// classof - Used by isa<>, cast<>, and dyn_cast<>. static bool classof(const BugReporter* R) { @@ -538,13 +542,29 @@ public: } }; + +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 { - virtual void anchor(); GRBugReporter &BR; + NodeMapClosure NMC; + + virtual void anchor(); + public: - BugReporterContext(GRBugReporter& br) : BR(br) {} + BugReporterContext(GRBugReporter &br, InterExplodedGraphMap &Backmap) + : BR(br), NMC(Backmap) {} - virtual ~BugReporterContext() {} + virtual ~BugReporterContext() = default; GRBugReporter& getBugReporter() { return BR; } @@ -554,7 +574,7 @@ public: return BR.getStateManager(); } - SValBuilder& getSValBuilder() { + SValBuilder &getSValBuilder() { return getStateManager().getSValBuilder(); } @@ -566,11 +586,15 @@ public: return BR.getSourceManager(); } - virtual BugReport::NodeResolver& getNodeResolver() = 0; + AnalyzerOptions &getAnalyzerOptions() { + return BR.getAnalyzerOptions(); + } + + NodeMapClosure& getNodeResolver() { return NMC; } }; -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 2043896fd26fe..92118b0fbee25 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -1,4 +1,4 @@ -//===--- BugReporterVisitors.h - Generate PathDiagnostics -------*- C++ -*-===// +//===- BugReporterVisitors.h - Generate PathDiagnostics ---------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -15,11 +15,21 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include <memory> namespace clang { + +class BinaryOperator; class CFGBlock; +class DeclRefExpr; +class Expr; +class Stmt; namespace ento { @@ -29,13 +39,7 @@ class ExplodedNode; class MemRegion; class PathDiagnosticPiece; -/// \brief BugReporterVisitors are used to add custom diagnostics along a path. -/// -/// Custom visitors should subclass the BugReporterVisitorImpl class for a -/// default implementation of the clone() method. -/// (Warning: if you have a deep subclass of BugReporterVisitorImpl, the -/// default implementation of clone() will NOT do the right thing, and you -/// will have to provide your own implementation.) +/// BugReporterVisitors are used to add custom diagnostics along a path. class BugReporterVisitor : public llvm::FoldingSetNode { public: BugReporterVisitor() = default; @@ -43,19 +47,13 @@ public: BugReporterVisitor(BugReporterVisitor &&) {} virtual ~BugReporterVisitor(); - /// \brief Returns a copy of this BugReporter. - /// - /// Custom BugReporterVisitors should not override this method directly. - /// Instead, they should inherit from BugReporterVisitorImpl and provide - /// a protected or public copy constructor. - /// - /// (Warning: if you have a deep subclass of BugReporterVisitorImpl, the - /// default implementation of clone() will NOT do the right thing, and you - /// will have to provide your own implementation.) - virtual std::unique_ptr<BugReporterVisitor> clone() const = 0; - - /// \brief Return a diagnostic piece which should be associated with the + /// Return a diagnostic piece which should be associated with the /// given node. + /// Note that this function does *not* get run on the very last node + /// of the report, as the PathDiagnosticPiece associated with the + /// last node should be unique. + /// Use {@code getEndPath} to customize the note associated with the report + /// end instead. /// /// The last parameter can be used to register a new visitor with the given /// BugReport while processing a node. @@ -63,43 +61,32 @@ public: VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) = 0; - /// \brief Provide custom definition for the final diagnostic piece on the + /// Last function called on the visitor, no further calls to VisitNode + /// would follow. + virtual void finalizeVisitor(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR); + + /// Provide custom definition for the final diagnostic piece on the /// path - the piece, which is displayed before the path is expanded. /// - /// If returns NULL the default implementation will be used. - /// Also note that at most one visitor of a BugReport should generate a - /// non-NULL end of path diagnostic piece. - virtual std::unique_ptr<PathDiagnosticPiece> + /// 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 void Profile(llvm::FoldingSetNodeID &ID) const = 0; - /// \brief Generates the default final diagnostic piece. - static std::unique_ptr<PathDiagnosticPiece> + /// Generates the default final diagnostic piece. + static std::shared_ptr<PathDiagnosticPiece> getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR); }; -/// This class provides a convenience implementation for clone() using the -/// Curiously-Recurring Template Pattern. If you are implementing a custom -/// BugReporterVisitor, subclass BugReporterVisitorImpl and provide a public -/// or protected copy constructor. -/// -/// (Warning: if you have a deep subclass of BugReporterVisitorImpl, the -/// default implementation of clone() will NOT do the right thing, and you -/// will have to provide your own implementation.) -template <class DERIVED> -class BugReporterVisitorImpl : public BugReporterVisitor { - std::unique_ptr<BugReporterVisitor> clone() const override { - return llvm::make_unique<DERIVED>(*static_cast<const DERIVED *>(this)); - } -}; - -class FindLastStoreBRVisitor final - : public BugReporterVisitorImpl<FindLastStoreBRVisitor> { +class FindLastStoreBRVisitor final : public BugReporterVisitor { const MemRegion *R; SVal V; - bool Satisfied; + bool Satisfied = false; /// If the visitor is tracking the value directly responsible for the /// bug, we are going to employ false positive suppression. @@ -113,10 +100,7 @@ public: FindLastStoreBRVisitor(KnownSVal V, const MemRegion *R, bool InEnableNullFPSuppression) - : R(R), - V(V), - Satisfied(false), - EnableNullFPSuppression(InEnableNullFPSuppression) {} + : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression) {} void Profile(llvm::FoldingSetNodeID &ID) const override; @@ -126,22 +110,20 @@ public: BugReport &BR) override; }; -class TrackConstraintBRVisitor final - : public BugReporterVisitorImpl<TrackConstraintBRVisitor> { +class TrackConstraintBRVisitor final : public BugReporterVisitor { DefinedSVal Constraint; bool Assumption; - bool IsSatisfied; + bool IsSatisfied = false; bool IsZeroCheck; /// We should start tracking from the last node along the path in which the /// value is constrained. - bool IsTrackingTurnedOn; + bool IsTrackingTurnedOn = false; public: TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) - : Constraint(constraint), Assumption(assumption), IsSatisfied(false), - IsZeroCheck(!Assumption && Constraint.getAs<Loc>()), - IsTrackingTurnedOn(false) {} + : Constraint(constraint), Assumption(assumption), + IsZeroCheck(!Assumption && Constraint.getAs<Loc>()) {} void Profile(llvm::FoldingSetNodeID &ID) const override; @@ -157,15 +139,12 @@ public: private: /// Checks if the constraint is valid in the current state. bool isUnderconstrained(const ExplodedNode *N) const; - }; /// \class NilReceiverBRVisitor -/// \brief Prints path notes when a message is sent to a nil receiver. -class NilReceiverBRVisitor final - : public BugReporterVisitorImpl<NilReceiverBRVisitor> { +/// Prints path notes when a message is sent to a nil receiver. +class NilReceiverBRVisitor final : public BugReporterVisitor { public: - void Profile(llvm::FoldingSetNodeID &ID) const override { static int x = 0; ID.AddPointer(&x); @@ -182,9 +161,7 @@ public: }; /// Visitor that tries to report interesting diagnostics from conditions. -class ConditionBRVisitor final - : public BugReporterVisitorImpl<ConditionBRVisitor> { - +class ConditionBRVisitor final : public BugReporterVisitor { // FIXME: constexpr initialization isn't supported by MSVC2013. static const char *const GenericTrueMessage; static const char *const GenericFalseMessage; @@ -243,11 +220,11 @@ public: static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece); }; -/// \brief Suppress reports that might lead to known false positives. +/// Suppress reports that might lead to known false positives. /// /// Currently this suppresses reports based on locations of bugs. class LikelyFalsePositiveSuppressionBRVisitor final - : public BugReporterVisitorImpl<LikelyFalsePositiveSuppressionBRVisitor> { + : public BugReporterVisitor { public: static void *getTag() { static int Tag = 0; @@ -265,19 +242,16 @@ public: return nullptr; } - std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) override; + void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, + BugReport &BR) override; }; -/// \brief When a region containing undefined value or '0' value is passed +/// When a region containing undefined value or '0' value is passed /// as an argument in a call, marks the call as interesting. /// /// As a result, BugReporter will not prune the path through the function even /// if the region's contents are not modified/accessed by the call. -class UndefOrNullArgVisitor final - : public BugReporterVisitorImpl<UndefOrNullArgVisitor> { - +class UndefOrNullArgVisitor final : public BugReporterVisitor { /// The interesting memory region this visitor is tracking. const MemRegion *R; @@ -296,21 +270,20 @@ public: BugReport &BR) override; }; -class SuppressInlineDefensiveChecksVisitor final - : public BugReporterVisitorImpl<SuppressInlineDefensiveChecksVisitor> { +class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { /// The symbolic value for which we are tracking constraints. /// This value is constrained to null in the end of path. DefinedSVal V; /// Track if we found the node where the constraint was first added. - bool IsSatisfied; + bool IsSatisfied = false; /// Since the visitors can be registered on nodes previous to the last /// node in the BugReport, but the path traversal always starts with the last /// node, the visitor invariant (that we start with a node in which V is null) /// might not hold when node visitation starts. We are going to start tracking /// from the last node in which the value is null. - bool IsTrackingTurnedOn; + bool IsTrackingTurnedOn = false; public: SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N); @@ -327,13 +300,11 @@ public: BugReport &BR) override; }; -class CXXSelfAssignmentBRVisitor final - : public BugReporterVisitorImpl<CXXSelfAssignmentBRVisitor> { - - bool Satisfied; +class CXXSelfAssignmentBRVisitor final : public BugReporterVisitor { + bool Satisfied = false; public: - CXXSelfAssignmentBRVisitor() : Satisfied(false) {} + CXXSelfAssignmentBRVisitor() = default; void Profile(llvm::FoldingSetNodeID &ID) const override {} @@ -343,6 +314,44 @@ public: BugReport &BR) override; }; +/// The bug visitor prints a diagnostic message at the location where a given +/// variable was tainted. +class TaintBugVisitor final : public BugReporterVisitor { +private: + const SVal V; + +public: + TaintBugVisitor(const SVal V) : V(V) {} + void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +/// The bug visitor will walk all the nodes in a path and collect all the +/// constraints. When it reaches the root node, will create a refutation +/// manager and check if the constraints are satisfiable +class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor { +private: + /// Holds the constraints in a given path + ConstraintRangeTy Constraints; + +public: + FalsePositiveRefutationBRVisitor(); + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + + void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, + BugReport &BR) override; +}; + namespace bugreporter { /// Attempts to add visitors to trace a null or undefined value back to its @@ -370,10 +379,10 @@ const Stmt *GetDenomExpr(const ExplodedNode *N); const Stmt *GetRetValExpr(const ExplodedNode *N); bool isDeclRefExprToReference(const Expr *E); +} // namespace bugreporter -} // end namespace clang -} // end namespace ento -} // end namespace bugreporter +} // namespace ento +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h index 18fa85c9657f6..c723f31aec266 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -1,4 +1,4 @@ -//===--- BugType.h - Bug Information Desciption ----------------*- C++ -*-===// +//===--- BugType.h - Bug Information Description ---------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -32,27 +32,39 @@ private: const CheckName Check; const std::string Name; const std::string Category; - bool SuppressonSink; + const CheckerBase *Checker; + bool SuppressOnSink; virtual void anchor(); + public: - BugType(class CheckName check, StringRef name, StringRef cat) - : Check(check), Name(name), Category(cat), SuppressonSink(false) {} - BugType(const CheckerBase *checker, StringRef name, StringRef cat) - : Check(checker->getCheckName()), Name(name), Category(cat), - SuppressonSink(false) {} - virtual ~BugType() {} - - // FIXME: Should these be made strings as well? + BugType(CheckName Check, StringRef Name, StringRef Cat) + : Check(Check), Name(Name), Category(Cat), Checker(nullptr), + SuppressOnSink(false) {} + BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat) + : Check(Checker->getCheckName()), Name(Name), Category(Cat), + Checker(Checker), SuppressOnSink(false) {} + virtual ~BugType() = default; + StringRef getName() const { return Name; } StringRef getCategory() const { return Category; } - StringRef getCheckName() const { return Check.getName(); } + 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. + // In case the BugType object is initialized in the checker's ctor + // the Check 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; + } /// isSuppressOnSink - Returns true if bug reports associated with this bug /// type should be suppressed if the end node of the report is post-dominated /// by a sink node. - bool isSuppressOnSink() const { return SuppressonSink; } - void setSuppressOnSink(bool x) { SuppressonSink = x; } + bool isSuppressOnSink() const { return SuppressOnSink; } + void setSuppressOnSink(bool x) { SuppressOnSink = x; } virtual void FlushReports(BugReporter& BR); }; @@ -74,7 +86,7 @@ public: StringRef getDescription() const { return desc; } }; -} // end GR namespace +} // end ento namespace } // end clang namespace #endif diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h index f31ab2cd81cd2..b18d3c9b3031c 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -1,4 +1,4 @@ -//===--- PathDiagnostic.h - Path-Specific Diagnostic Handling ---*- C++ -*-===// +//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -14,37 +14,49 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H -#include "clang/Analysis/ProgramPoint.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/IntrusiveRefCntPtr.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 ConditionalOperator; + class AnalysisDeclContext; class BinaryOperator; -class CompoundStmt; +class CallEnter; +class CallExitEnd; +class CallExpr; +class ConditionalOperator; class Decl; +class Expr; class LocationContext; class MemberExpr; -class ParentMap; class ProgramPoint; class SourceManager; -class Stmt; -class CallExpr; namespace ento { class ExplodedNode; class SymExpr; -typedef const SymExpr* SymbolRef; + +using SymbolRef = const SymExpr *; //===----------------------------------------------------------------------===// // High-level interface for handlers of path-sensitive diagnostics. @@ -58,15 +70,15 @@ public: public: PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} - typedef std::vector<std::pair<StringRef, StringRef> > ConsumerFiles; + using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>; - /// \brief A vector of <consumer,file> pairs. + /// A vector of <consumer,file> pairs. ConsumerFiles files; - /// \brief A precomputed hash tag used for uniquing PDFileEntry objects. + /// A precomputed hash tag used for uniquing PDFileEntry objects. const llvm::FoldingSetNodeID NodeID; - /// \brief Used for profiling in the FoldingSet. + /// Used for profiling in the FoldingSet. void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } }; @@ -88,8 +100,9 @@ public: private: virtual void anchor(); + public: - PathDiagnosticConsumer() : flushed(false) {} + PathDiagnosticConsumer() = default; virtual ~PathDiagnosticConsumer(); void FlushDiagnostics(FilesMade *FilesMade); @@ -101,7 +114,17 @@ public: void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D); - enum PathGenerationScheme { None, Minimal, Extensive, AlternateExtensive }; + enum PathGenerationScheme { + /// Only runs visitors, no output generated. + None, + + /// Used for HTML 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; } @@ -110,7 +133,7 @@ public: virtual bool supportsCrossFileDiagnostics() const { return false; } protected: - bool flushed; + bool flushed = false; llvm::FoldingSet<PathDiagnostic> Diags; }; @@ -120,31 +143,28 @@ protected: class PathDiagnosticRange : public SourceRange { public: - bool isPoint; + bool isPoint = false; PathDiagnosticRange(SourceRange R, bool isP = false) - : SourceRange(R), isPoint(isP) {} - - PathDiagnosticRange() : isPoint(false) {} + : SourceRange(R), isPoint(isP) {} + PathDiagnosticRange() = default; }; -typedef llvm::PointerUnion<const LocationContext*, AnalysisDeclContext*> - LocationOrAnalysisDeclContext; +using LocationOrAnalysisDeclContext = + llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>; class PathDiagnosticLocation { private: - enum Kind { RangeK, SingleLocK, StmtK, DeclK } K; - const Stmt *S; - const Decl *D; - const SourceManager *SM; + 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), S(nullptr), D(nullptr), SM(&sm), - Loc(genLocation(L)), Range(genRange()) { - } + PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) + : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} FullSourceLoc genLocation( SourceLocation L = SourceLocation(), @@ -155,18 +175,15 @@ private: public: /// Create an invalid location. - PathDiagnosticLocation() - : K(SingleLocK), S(nullptr), D(nullptr), SM(nullptr) {} + PathDiagnosticLocation() = default; /// Create a location corresponding to the given statement. PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, LocationOrAnalysisDeclContext lac) - : K(s->getLocStart().isValid() ? StmtK : SingleLocK), - S(K == StmtK ? s : nullptr), - D(nullptr), SM(&sm), - Loc(genLocation(SourceLocation(), lac)), - Range(genRange(lac)) { + : K(s->getLocStart().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()); @@ -174,8 +191,7 @@ public: /// Create a location corresponding to the given declaration. PathDiagnosticLocation(const Decl *d, const SourceManager &sm) - : K(DeclK), S(nullptr), D(d), SM(&sm), - Loc(genLocation()), Range(genRange()) { + : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { assert(D); assert(Loc.isValid()); assert(Range.isValid()); @@ -185,8 +201,7 @@ public: /// /// This should only be used if there are no more appropriate constructors. PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) - : K(SingleLocK), S(nullptr), D(nullptr), SM(&sm), Loc(loc, sm), - Range(genRange()) { + : SM(&sm), Loc(loc, sm), Range(genRange()) { assert(Loc.isValid()); assert(Range.isValid()); } @@ -201,6 +216,15 @@ public: 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, @@ -248,7 +272,7 @@ public: const SourceManager &SM); /// Create a location corresponding to the given valid ExplodedNode. - static PathDiagnosticLocation create(const ProgramPoint& P, + static PathDiagnosticLocation create(const ProgramPoint &P, const SourceManager &SMng); /// Create a location corresponding to the next valid ExplodedNode as end @@ -281,6 +305,12 @@ public: } 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; } @@ -297,21 +327,22 @@ public: void dump() const; - /// \brief Given an exploded node, retrieve the statement that should be used + /// Given an exploded node, retrieve the statement that should be used /// for the diagnostic location. static const Stmt *getStmt(const ExplodedNode *N); - /// \brief Retrieve the statement corresponding to the successor node. + /// 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) {} + : Start(start), End(end) {} const PathDiagnosticLocation &getStart() const { return Start; } const PathDiagnosticLocation &getEnd() const { return End; } @@ -344,9 +375,9 @@ private: const Kind kind; const DisplayHint Hint; - /// \brief In the containing bug report, this piece is the last piece from + /// In the containing bug report, this piece is the last piece from /// the main source file. - bool LastInMainSourceFile; + bool LastInMainSourceFile = false; /// A constant string that can be used to tag the PathDiagnosticPiece, /// typically with the identification of the creator. The actual pointer @@ -356,16 +387,14 @@ private: std::vector<SourceRange> ranges; - PathDiagnosticPiece() = delete; - PathDiagnosticPiece(const PathDiagnosticPiece &P) = delete; - void operator=(const PathDiagnosticPiece &P) = delete; - 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; } @@ -420,8 +449,8 @@ public: class PathPieces : public std::list<std::shared_ptr<PathDiagnosticPiece>> { void flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const; -public: +public: PathPieces flatten(bool ShouldFlattenMacros) const { PathPieces Result; flattenTo(Result, Result, ShouldFlattenMacros); @@ -434,12 +463,13 @@ public: 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) { + : PathDiagnosticPiece(s, k), Pos(pos) { assert(Pos.isValid() && Pos.asLocation().isValid() && "PathDiagnosticSpotPiece's must have a valid location."); if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); @@ -456,7 +486,7 @@ public: } }; -/// \brief Interface for classes constructing Stack hints. +/// 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. @@ -464,11 +494,11 @@ class StackHintGenerator { public: virtual ~StackHintGenerator() = 0; - /// \brief Construct the Diagnostic message for the given ExplodedNode. + /// Construct the Diagnostic message for the given ExplodedNode. virtual std::string getMessage(const ExplodedNode *N) = 0; }; -/// \brief Constructs a Stack hint for the given symbol. +/// 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 @@ -481,18 +511,20 @@ private: public: StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} - ~StackHintGeneratorForSymbol() override {} + ~StackHintGeneratorForSymbol() override = default; - /// \brief Search the call expression for the symbol Sym and dispatch the + /// 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; } @@ -511,9 +543,8 @@ public: PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, StringRef s, bool addPosRange = true, StackHintGenerator *stackHint = nullptr) - : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), - CallStackHint(stackHint) {} - + : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), + CallStackHint(stackHint) {} ~PathDiagnosticEventPiece() override; /// Mark the diagnostic piece as being potentially prunable. This @@ -537,30 +568,19 @@ public: std::string getCallStackMessage(const ExplodedNode *N) { if (CallStackHint) return CallStackHint->getMessage(N); - return ""; + return {}; } void dump() const override; - static inline bool classof(const PathDiagnosticPiece *P) { + static bool classof(const PathDiagnosticPiece *P) { return P->getKind() == Event; } }; class PathDiagnosticCallPiece : public PathDiagnosticPiece { - PathDiagnosticCallPiece(const Decl *callerD, - const PathDiagnosticLocation &callReturnPos) - : PathDiagnosticPiece(Call), Caller(callerD), Callee(nullptr), - NoExit(false), IsCalleeAnAutosynthesizedPropertyAccessor(false), - callReturn(callReturnPos) {} - - PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) - : PathDiagnosticPiece(Call), Caller(caller), Callee(nullptr), - NoExit(true), IsCalleeAnAutosynthesizedPropertyAccessor(false), - path(oldPath) {} - const Decl *Caller; - const Decl *Callee; + const Decl *Callee = nullptr; // Flag signifying that this diagnostic has only call enter and no matching // call exit. @@ -568,12 +588,20 @@ class PathDiagnosticCallPiece : public PathDiagnosticPiece { // Flag signifying that the callee function is an Objective-C autosynthesized // property getter or setter. - bool IsCalleeAnAutosynthesizedPropertyAccessor; + 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; @@ -588,13 +616,9 @@ public: void setCallee(const CallEnter &CE, const SourceManager &SM); bool hasCallStackMessage() { return !CallStackMessage.empty(); } - void setCallStackMessage(StringRef st) { - CallStackMessage = st; - } + void setCallStackMessage(StringRef st) { CallStackMessage = st; } - PathDiagnosticLocation getLocation() const override { - return callEnter; - } + PathDiagnosticLocation getLocation() const override { return callEnter; } std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const; std::shared_ptr<PathDiagnosticEventPiece> @@ -604,8 +628,8 @@ public: void flattenLocations() override { callEnter.flatten(); callReturn.flatten(); - for (PathPieces::iterator I = path.begin(), - E = path.end(); I != E; ++I) (*I)->flattenLocations(); + for (const auto &I : path) + I->flattenLocations(); } static std::shared_ptr<PathDiagnosticCallPiece> @@ -619,28 +643,29 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const override; - static inline bool classof(const PathDiagnosticPiece *P) { + 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)); - } + : PathDiagnosticPiece(s, ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, const PathDiagnosticLocation &endPos) - : PathDiagnosticPiece(ControlFlow) { - LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); - } + : PathDiagnosticPiece(ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } - ~PathDiagnosticControlFlowPiece() override; + ~PathDiagnosticControlFlowPiece() override; PathDiagnosticLocation getStartLocation() const { assert(!LPairs.empty() && @@ -668,20 +693,23 @@ public: return getStartLocation(); } - typedef std::vector<PathDiagnosticLocationPair>::iterator iterator; + using iterator = std::vector<PathDiagnosticLocationPair>::iterator; + iterator begin() { return LPairs.begin(); } - iterator end() { return LPairs.end(); } + iterator end() { return LPairs.end(); } void flattenLocations() override { - for (iterator I=begin(), E=end(); I!=E; ++I) I->flatten(); + for (auto &I : *this) + I.flatten(); } - typedef std::vector<PathDiagnosticLocationPair>::const_iterator - const_iterator; + using const_iterator = + std::vector<PathDiagnosticLocationPair>::const_iterator; + const_iterator begin() const { return LPairs.begin(); } - const_iterator end() const { return LPairs.end(); } + const_iterator end() const { return LPairs.end(); } - static inline bool classof(const PathDiagnosticPiece *P) { + static bool classof(const PathDiagnosticPiece *P) { return P->getKind() == ControlFlow; } @@ -693,8 +721,7 @@ public: class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { public: PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) - : PathDiagnosticSpotPiece(pos, "", Macro) {} - + : PathDiagnosticSpotPiece(pos, "", Macro) {} ~PathDiagnosticMacroPiece() override; PathPieces subPieces; @@ -703,11 +730,11 @@ public: void flattenLocations() override { PathDiagnosticSpotPiece::flattenLocations(); - for (PathPieces::iterator I = subPieces.begin(), - E = subPieces.end(); I != E; ++I) (*I)->flattenLocations(); + for (const auto &I : subPieces) + I->flattenLocations(); } - static inline bool classof(const PathDiagnosticPiece *P) { + static bool classof(const PathDiagnosticPiece *P) { return P->getKind() == Macro; } @@ -721,10 +748,9 @@ public: PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, bool AddPosRange = true) : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} - ~PathDiagnosticNotePiece() override; - static inline bool classof(const PathDiagnosticPiece *P) { + static bool classof(const PathDiagnosticPiece *P) { return P->getKind() == Note; } @@ -733,6 +759,9 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const override; }; +/// File IDs mapped to sets of line numbers. +using FilesToLineNumsMap = std::map<unsigned, 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. @@ -745,24 +774,27 @@ class PathDiagnostic : public llvm::FoldingSetNode { std::string Category; std::deque<std::string> OtherDesc; - /// \brief Loc The location of the path diagnostic report. + /// Loc The location of the path diagnostic report. PathDiagnosticLocation Loc; PathPieces pathImpl; SmallVector<PathPieces *, 3> pathStack; - /// \brief Important bug uniqueing location. + /// Important bug uniqueing location. /// The location info is useful to differentiate between bugs. PathDiagnosticLocation UniqueingLoc; const Decl *UniqueingDecl; - PathDiagnostic() = delete; + /// 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); - + const Decl *DeclToUnique, + std::unique_ptr<FilesToLineNumsMap> ExecutedLines); ~PathDiagnostic(); const PathPieces &path; @@ -788,7 +820,7 @@ public: bool isWithinCall() const { return !pathStack.empty(); } - void setEndOfPath(std::unique_ptr<PathDiagnosticPiece> EndPiece) { + 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"); @@ -801,21 +833,17 @@ public: VerboseDesc += S; } - void resetPath() { - pathStack.clear(); - pathImpl.clear(); - Loc = PathDiagnosticLocation(); - } - - /// \brief If the last piece of the report point to the header file, resets + /// 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; } @@ -825,30 +853,38 @@ public: /// where the bug manifests. const Decl *getDeclWithIssue() const { return DeclWithIssue; } - typedef std::deque<std::string>::const_iterator meta_iterator; + 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); } + using filesmap_iterator = FilesToLineNumsMap::const_iterator; + + filesmap_iterator executedLines_begin() const { + return ExecutedLines->begin(); + } + + filesmap_iterator executedLines_end() const { return ExecutedLines->end(); } + PathDiagnosticLocation getLocation() const { - assert(Loc.isValid() && "No report location set yet!"); return Loc; } - /// \brief Get the location on which the report should be uniqued. + /// Get the location on which the report should be uniqued. PathDiagnosticLocation getUniqueingLoc() const { return UniqueingLoc; } - /// \brief Get the declaration containing the uniqueing location. + /// Get the declaration containing the uniqueing location. const Decl *getUniqueingDecl() const { return UniqueingDecl; } void flattenLocations() { Loc.flatten(); - for (PathPieces::iterator I = pathImpl.begin(), E = pathImpl.end(); - I != E; ++I) (*I)->flattenLocations(); + for (const auto &I : pathImpl) + I->flattenLocations(); } /// Profiles the diagnostic, independent of the path it references. @@ -864,8 +900,8 @@ public: void FullProfile(llvm::FoldingSetNodeID &ID) const; }; -} // end GR namespace +} // namespace ento -} //end clang namespace +} // namespace clang -#endif +#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 f9477762c7585..45b7a61139ea7 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -254,9 +254,9 @@ public: class EndFunction { template <typename CHECKER> - static void _checkEndFunction(void *checker, + static void _checkEndFunction(void *checker, const ReturnStmt *RS, CheckerContext &C) { - ((const CHECKER *)checker)->checkEndFunction(C); + ((const CHECKER *)checker)->checkEndFunction(RS, C); } public: @@ -283,6 +283,22 @@ public: } }; +class NewAllocator { + template <typename CHECKER> + static void _checkNewAllocator(void *checker, const CXXNewExpr *NE, + SVal Target, CheckerContext &C) { + ((const CHECKER *)checker)->checkNewAllocator(NE, Target, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForNewAllocator( + CheckerManager::CheckNewAllocatorFunc(checker, + _checkNewAllocator<CHECKER>)); + } +}; + class LiveSymbols { template <typename CHECKER> static void _checkLiveSymbols(void *checker, ProgramStateRef state, @@ -532,7 +548,7 @@ public: } }; -/// \brief We dereferenced a location that may be null. +/// We dereferenced a location that may be null. struct ImplicitNullDerefEvent { SVal Location; bool IsLoad; @@ -544,7 +560,7 @@ struct ImplicitNullDerefEvent { bool IsDirectDereference; }; -/// \brief A helper class which wraps a boolean value set to false by default. +/// A helper class which wraps a boolean value set to false by default. /// /// This class should behave exactly like 'bool' except that it doesn't need to /// be explicitly initialized. diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 88cb08a4b647c..33a794061a384 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -1,4 +1,4 @@ -//===--- CheckerManager.h - Static Analyzer Checker Manager -----*- C++ -*-===// +//===- CheckerManager.h - Static Analyzer Checker Manager -------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -16,51 +16,62 @@ #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LangOptions.h" -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" -#include <utility> +#include "llvm/ADT/StringRef.h" #include <vector> namespace clang { - class Decl; - class Stmt; - class CallExpr; + +class AnalyzerOptions; +class CallExpr; +class CXXNewExpr; +class Decl; +class LocationContext; +class Stmt; +class TranslationUnitDecl; namespace ento { - class CheckerBase; - class CheckerRegistry; - class ExprEngine; - class AnalysisManager; - class BugReporter; - class CheckerContext; - class ObjCMethodCall; - class SVal; - class ExplodedNode; - class ExplodedNodeSet; - class ExplodedGraph; - class ProgramState; - class NodeBuilder; - struct NodeBuilderContext; - class MemRegion; - class SymbolReaper; + +class AnalysisManager; +class BugReporter; +class CallEvent; +class CheckerBase; +class CheckerContext; +class CheckerRegistry; +class ExplodedGraph; +class ExplodedNode; +class ExplodedNodeSet; +class ExprEngine; +class MemRegion; +struct NodeBuilderContext; +class ObjCMethodCall; +class RegionAndSymbolInvalidationTraits; +class SVal; +class SymbolReaper; template <typename T> class CheckerFn; template <typename RET, typename... Ps> class CheckerFn<RET(Ps...)> { - typedef RET (*Func)(void *, Ps...); + using Func = RET (*)(void *, Ps...); + Func Fn; + public: CheckerBase *Checker; - CheckerFn(CheckerBase *checker, Func fn) : Fn(fn), Checker(checker) { } + + CheckerFn(CheckerBase *checker, Func fn) : Fn(fn), Checker(checker) {} + RET operator()(Ps... ps) const { return Fn(Checker, ps...); } }; -/// \brief Describes the different reasons a pointer escapes +/// Describes the different reasons a pointer escapes /// during analysis. enum PointerEscapeKind { /// A pointer escapes due to binding its value to a location @@ -85,12 +96,15 @@ enum PointerEscapeKind { // name strings have a lifetime that keeps them alive at least until the path // diagnostics have been processed. class CheckName { - StringRef Name; friend class ::clang::ento::CheckerRegistry; + + StringRef Name; + explicit CheckName(StringRef Name) : Name(Name) {} public: CheckName() = default; + StringRef getName() const { return Name; } }; @@ -121,40 +135,27 @@ public: const LangOptions &getLangOpts() const { return LangOpts; } AnalyzerOptions &getAnalyzerOptions() { return AOptions; } - typedef CheckerBase *CheckerRef; - typedef const void *CheckerTag; - typedef CheckerFn<void ()> CheckerDtor; + using CheckerRef = CheckerBase *; + using CheckerTag = const void *; + using CheckerDtor = CheckerFn<void ()>; //===----------------------------------------------------------------------===// // registerChecker //===----------------------------------------------------------------------===// - /// \brief Used to register checkers. + /// Used to register checkers. + /// All arguments are automatically passed through to the checker + /// constructor. /// /// \returns a pointer to the checker object. - template <typename CHECKER> - CHECKER *registerChecker() { + template <typename CHECKER, typename... AT> + CHECKER *registerChecker(AT... Args) { CheckerTag tag = getTag<CHECKER>(); CheckerRef &ref = CheckerTags[tag]; if (ref) return static_cast<CHECKER *>(ref); // already registered. - CHECKER *checker = new CHECKER(); - checker->Name = CurrentCheckName; - CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>)); - CHECKER::_register(checker, *this); - ref = checker; - return checker; - } - - template <typename CHECKER> - CHECKER *registerChecker(AnalyzerOptions &AOpts) { - CheckerTag tag = getTag<CHECKER>(); - CheckerRef &ref = CheckerTags[tag]; - if (ref) - return static_cast<CHECKER *>(ref); // already registered. - - CHECKER *checker = new CHECKER(AOpts); + CHECKER *checker = new CHECKER(Args...); checker->Name = CurrentCheckName; CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>)); CHECKER::_register(checker, *this); @@ -166,11 +167,11 @@ public: // Functions for running checkers for AST traversing.. //===----------------------------------------------------------------------===// - /// \brief Run checkers handling Decls. + /// Run checkers handling Decls. void runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, BugReporter &BR); - /// \brief Run checkers handling Decls containing a Stmt body. + /// Run checkers handling Decls containing a Stmt body. void runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR); @@ -178,7 +179,7 @@ public: // Functions for running checkers for path-sensitive checking. //===----------------------------------------------------------------------===// - /// \brief Run checkers for pre-visiting Stmts. + /// Run checkers for pre-visiting Stmts. /// /// The notification is performed for every explored CFGElement, which does /// not include the control flow statements such as IfStmt. @@ -191,7 +192,7 @@ public: runCheckersForStmt(/*isPreVisit=*/true, Dst, Src, S, Eng); } - /// \brief Run checkers for post-visiting Stmts. + /// Run checkers for post-visiting Stmts. /// /// The notification is performed for every explored CFGElement, which does /// not include the control flow statements such as IfStmt. @@ -205,13 +206,13 @@ public: runCheckersForStmt(/*isPreVisit=*/false, Dst, Src, S, Eng, wasInlined); } - /// \brief Run checkers for visiting Stmts. + /// Run checkers for visiting Stmts. void runCheckersForStmt(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const Stmt *S, ExprEngine &Eng, bool wasInlined = false); - /// \brief Run checkers for pre-visiting obj-c messages. + /// Run checkers for pre-visiting obj-c messages. void runCheckersForPreObjCMessage(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, @@ -219,7 +220,7 @@ public: runCheckersForObjCMessage(ObjCMessageVisitKind::Pre, Dst, Src, msg, Eng); } - /// \brief Run checkers for post-visiting obj-c messages. + /// Run checkers for post-visiting obj-c messages. void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, @@ -229,7 +230,7 @@ public: wasInlined); } - /// \brief Run checkers for visiting an obj-c message to nil. + /// Run checkers for visiting an obj-c message to nil. void runCheckersForObjCMessageNil(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, @@ -238,21 +239,20 @@ public: Eng); } - - /// \brief Run checkers for visiting obj-c messages. + /// Run checkers for visiting obj-c messages. void runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, ExprEngine &Eng, bool wasInlined = false); - /// \brief Run checkers for pre-visiting obj-c messages. + /// Run checkers for pre-visiting obj-c messages. void runCheckersForPreCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng) { runCheckersForCallEvent(/*isPreVisit=*/true, Dst, Src, Call, Eng); } - /// \brief Run checkers for post-visiting obj-c messages. + /// Run checkers for post-visiting obj-c messages. void runCheckersForPostCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng, bool wasInlined = false) { @@ -260,13 +260,13 @@ public: wasInlined); } - /// \brief Run checkers for visiting obj-c messages. + /// Run checkers for visiting obj-c messages. void runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng, bool wasInlined = false); - /// \brief Run checkers for load/store of a location. + /// Run checkers for load/store of a location. void runCheckersForLocation(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, @@ -275,35 +275,43 @@ public: const Stmt *BoundEx, ExprEngine &Eng); - /// \brief Run checkers for binding of a value to a location. + /// Run checkers for binding of a value to a location. void runCheckersForBind(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, SVal val, const Stmt *S, ExprEngine &Eng, const ProgramPoint &PP); - /// \brief Run checkers for end of analysis. + /// Run checkers for end of analysis. void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng); - /// \brief Run checkers on beginning of function. + /// Run checkers on beginning of function. void runCheckersForBeginFunction(ExplodedNodeSet &Dst, const BlockEdge &L, ExplodedNode *Pred, ExprEngine &Eng); - /// \brief Run checkers on end of function. + /// Run checkers on end of function. void runCheckersForEndFunction(NodeBuilderContext &BC, ExplodedNodeSet &Dst, ExplodedNode *Pred, - ExprEngine &Eng); + ExprEngine &Eng, + const ReturnStmt *RS); - /// \brief Run checkers for branch condition. + /// Run checkers for branch condition. void runCheckersForBranchCondition(const Stmt *condition, ExplodedNodeSet &Dst, ExplodedNode *Pred, ExprEngine &Eng); - /// \brief Run checkers for live symbols. + /// Run checkers between C++ operator new and constructor calls. + void runCheckersForNewAllocator(const CXXNewExpr *NE, SVal Target, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, + ExprEngine &Eng, + bool wasInlined = false); + + /// Run checkers for live symbols. /// /// Allows modifying SymbolReaper object. For example, checkers can explicitly /// register symbols of interest as live. These symbols will not be marked @@ -311,7 +319,7 @@ public: void runCheckersForLiveSymbols(ProgramStateRef state, SymbolReaper &SymReaper); - /// \brief Run checkers for dead symbols. + /// Run checkers for dead symbols. /// /// Notifies checkers when symbols become dead. For example, this allows /// checkers to aggressively clean up/reduce the checker state and produce @@ -322,7 +330,7 @@ public: ExprEngine &Eng, ProgramPoint::Kind K); - /// \brief Run checkers for region changes. + /// Run checkers for region changes. /// /// This corresponds to the check::RegionChanges callback. /// \param state The current program state. @@ -341,7 +349,7 @@ public: const LocationContext *LCtx, const CallEvent *Call); - /// \brief Run checkers when pointers escape. + /// Run checkers when pointers escape. /// /// This notifies the checkers about pointer escape, which occurs whenever /// the analyzer cannot track the symbol any more. For example, as a @@ -361,25 +369,25 @@ public: const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind, - RegionAndSymbolInvalidationTraits *ITraits); + RegionAndSymbolInvalidationTraits *ITraits); - /// \brief Run checkers for handling assumptions on symbolic values. + /// Run checkers for handling assumptions on symbolic values. ProgramStateRef runCheckersForEvalAssume(ProgramStateRef state, SVal Cond, bool Assumption); - /// \brief Run checkers for evaluating a call. + /// Run checkers for evaluating a call. /// /// Warning: Currently, the CallEvent MUST come from a CallExpr! void runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &CE, ExprEngine &Eng); - /// \brief Run checkers for the entire Translation Unit. + /// Run checkers for the entire Translation Unit. void runCheckersOnEndOfTranslationUnit(const TranslationUnitDecl *TU, AnalysisManager &mgr, BugReporter &BR); - /// \brief Run checkers for debug-printing a ProgramState. + /// Run checkers for debug-printing a ProgramState. /// /// Unlike most other callbacks, any checker can simply implement the virtual /// method CheckerBase::printState if it has custom data to print. @@ -397,10 +405,11 @@ public: // Functions used by the registration mechanism, checkers should not touch // these directly. - typedef CheckerFn<void (const Decl *, AnalysisManager&, BugReporter &)> - CheckDeclFunc; + using CheckDeclFunc = + CheckerFn<void (const Decl *, AnalysisManager&, BugReporter &)>; + + using HandlesDeclFunc = bool (*)(const Decl *D); - typedef bool (*HandlesDeclFunc)(const Decl *D); void _registerForDecl(CheckDeclFunc checkfn, HandlesDeclFunc isForDeclFn); void _registerForBody(CheckDeclFunc checkfn); @@ -409,67 +418,67 @@ public: // Internal registration functions for path-sensitive checking. //===----------------------------------------------------------------------===// - typedef CheckerFn<void (const Stmt *, CheckerContext &)> CheckStmtFunc; + using CheckStmtFunc = CheckerFn<void (const Stmt *, CheckerContext &)>; - typedef CheckerFn<void (const ObjCMethodCall &, CheckerContext &)> - CheckObjCMessageFunc; + using CheckObjCMessageFunc = + CheckerFn<void (const ObjCMethodCall &, CheckerContext &)>; - typedef CheckerFn<void (const CallEvent &, CheckerContext &)> - CheckCallFunc; + using CheckCallFunc = + CheckerFn<void (const CallEvent &, CheckerContext &)>; - typedef CheckerFn<void (const SVal &location, bool isLoad, - const Stmt *S, - CheckerContext &)> - CheckLocationFunc; + using CheckLocationFunc = + CheckerFn<void (const SVal &location, bool isLoad, const Stmt *S, + CheckerContext &)>; - typedef CheckerFn<void (const SVal &location, const SVal &val, - const Stmt *S, CheckerContext &)> - CheckBindFunc; + using CheckBindFunc = + CheckerFn<void (const SVal &location, const SVal &val, const Stmt *S, + CheckerContext &)>; - typedef CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)> - CheckEndAnalysisFunc; + using CheckEndAnalysisFunc = + CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)>; - typedef CheckerFn<void (CheckerContext &)> - CheckBeginFunctionFunc; + using CheckBeginFunctionFunc = CheckerFn<void (CheckerContext &)>; - typedef CheckerFn<void (CheckerContext &)> - CheckEndFunctionFunc; + using CheckEndFunctionFunc = + CheckerFn<void (const ReturnStmt *, CheckerContext &)>; - typedef CheckerFn<void (const Stmt *, CheckerContext &)> - CheckBranchConditionFunc; + using CheckBranchConditionFunc = + CheckerFn<void (const Stmt *, CheckerContext &)>; + + using CheckNewAllocatorFunc = + CheckerFn<void (const CXXNewExpr *, SVal, CheckerContext &)>; - typedef CheckerFn<void (SymbolReaper &, CheckerContext &)> - CheckDeadSymbolsFunc; + using CheckDeadSymbolsFunc = + CheckerFn<void (SymbolReaper &, CheckerContext &)>; - typedef CheckerFn<void (ProgramStateRef,SymbolReaper &)> CheckLiveSymbolsFunc; + using CheckLiveSymbolsFunc = CheckerFn<void (ProgramStateRef,SymbolReaper &)>; - typedef CheckerFn<ProgramStateRef (ProgramStateRef, - const InvalidatedSymbols *symbols, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const LocationContext *LCtx, - const CallEvent *Call)> - CheckRegionChangesFunc; + using CheckRegionChangesFunc = + CheckerFn<ProgramStateRef (ProgramStateRef, + const InvalidatedSymbols *symbols, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call)>; - typedef CheckerFn<ProgramStateRef (ProgramStateRef, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind, - RegionAndSymbolInvalidationTraits *ITraits)> - CheckPointerEscapeFunc; + using CheckPointerEscapeFunc = + CheckerFn<ProgramStateRef (ProgramStateRef, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind, + RegionAndSymbolInvalidationTraits *ITraits)>; - typedef CheckerFn<ProgramStateRef (ProgramStateRef, - const SVal &cond, bool assumption)> - EvalAssumeFunc; + using EvalAssumeFunc = + CheckerFn<ProgramStateRef (ProgramStateRef, const SVal &cond, + bool assumption)>; - typedef CheckerFn<bool (const CallExpr *, CheckerContext &)> - EvalCallFunc; + using EvalCallFunc = CheckerFn<bool (const CallExpr *, CheckerContext &)>; - typedef CheckerFn<void (const TranslationUnitDecl *, - AnalysisManager&, BugReporter &)> - CheckEndOfTranslationUnit; + using CheckEndOfTranslationUnit = + CheckerFn<void (const TranslationUnitDecl *, AnalysisManager &, + BugReporter &)>; + + using HandlesStmtFunc = bool (*)(const Stmt *D); - typedef bool (*HandlesStmtFunc)(const Stmt *D); void _registerForPreStmt(CheckStmtFunc checkfn, HandlesStmtFunc isForStmtFn); void _registerForPostStmt(CheckStmtFunc checkfn, @@ -489,11 +498,13 @@ public: void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn); - void _registerForBeginFunction(CheckEndFunctionFunc checkfn); + void _registerForBeginFunction(CheckBeginFunctionFunc checkfn); void _registerForEndFunction(CheckEndFunctionFunc checkfn); void _registerForBranchCondition(CheckBranchConditionFunc checkfn); + void _registerForNewAllocator(CheckNewAllocatorFunc checkfn); + void _registerForLiveSymbols(CheckLiveSymbolsFunc checkfn); void _registerForDeadSymbols(CheckDeadSymbolsFunc checkfn); @@ -514,8 +525,8 @@ public: // Internal registration functions for events. //===----------------------------------------------------------------------===// - typedef void *EventTag; - typedef CheckerFn<void (const void *event)> CheckEventFunc; + using EventTag = void *; + using CheckEventFunc = CheckerFn<void (const void *event)>; template <typename EVENT> void _registerListenerForEvent(CheckEventFunc checkfn) { @@ -535,8 +546,8 @@ public: if (I == Events.end()) return; const EventInfo &info = I->second; - for (unsigned i = 0, e = info.Checkers.size(); i != e; ++i) - info.Checkers[i](&event); + for (const auto Checker : info.Checkers) + Checker(&event); } //===----------------------------------------------------------------------===// @@ -562,8 +573,8 @@ private: std::vector<CheckDeclFunc> BodyCheckers; - typedef SmallVector<CheckDeclFunc, 4> CachedDeclCheckers; - typedef llvm::DenseMap<unsigned, CachedDeclCheckers> CachedDeclCheckersMapTy; + using CachedDeclCheckers = SmallVector<CheckDeclFunc, 4>; + using CachedDeclCheckersMapTy = llvm::DenseMap<unsigned, CachedDeclCheckers>; CachedDeclCheckersMapTy CachedDeclCheckersMap; struct StmtCheckerInfo { @@ -573,8 +584,8 @@ private: }; std::vector<StmtCheckerInfo> StmtCheckers; - typedef SmallVector<CheckStmtFunc, 4> CachedStmtCheckers; - typedef llvm::DenseMap<unsigned, CachedStmtCheckers> CachedStmtCheckersMapTy; + using CachedStmtCheckers = SmallVector<CheckStmtFunc, 4>; + using CachedStmtCheckersMapTy = llvm::DenseMap<unsigned, CachedStmtCheckers>; CachedStmtCheckersMapTy CachedStmtCheckersMap; const CachedStmtCheckers &getCachedStmtCheckersFor(const Stmt *S, @@ -603,6 +614,8 @@ private: std::vector<CheckBranchConditionFunc> BranchConditionCheckers; + std::vector<CheckNewAllocatorFunc> NewAllocatorCheckers; + std::vector<CheckLiveSymbolsFunc> LiveSymbolsCheckers; std::vector<CheckDeadSymbolsFunc> DeadSymbolsCheckers; @@ -619,16 +632,17 @@ private: struct EventInfo { SmallVector<CheckEventFunc, 4> Checkers; - bool HasDispatcher; - EventInfo() : HasDispatcher(false) { } + bool HasDispatcher = false; + + EventInfo() = default; }; - typedef llvm::DenseMap<EventTag, EventInfo> EventsTy; + using EventsTy = llvm::DenseMap<EventTag, EventInfo>; EventsTy Events; }; -} // end ento namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H diff --git a/include/clang/StaticAnalyzer/Core/CheckerRegistry.h b/include/clang/StaticAnalyzer/Core/CheckerRegistry.h index 3b26ed3e1a09a..912a601b61c49 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerRegistry.h +++ b/include/clang/StaticAnalyzer/Core/CheckerRegistry.h @@ -1,4 +1,4 @@ -//===--- CheckerRegistry.h - Maintains all available checkers ---*- C++ -*-===// +//===- CheckerRegistry.h - Maintains all available checkers -----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -12,6 +12,9 @@ #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include <cstddef> #include <vector> // FIXME: move this information to an HTML file in docs/. @@ -64,8 +67,9 @@ #endif namespace clang { -class DiagnosticsEngine; + class AnalyzerOptions; +class DiagnosticsEngine; namespace ento { @@ -81,17 +85,18 @@ class CheckerRegistry { public: /// Initialization functions perform any necessary setup for a checker. /// They should include a call to CheckerManager::registerChecker. - typedef void (*InitializationFunction)(CheckerManager &); + using InitializationFunction = void (*)(CheckerManager &); + struct CheckerInfo { InitializationFunction Initialize; StringRef FullName; StringRef Desc; CheckerInfo(InitializationFunction fn, StringRef name, StringRef desc) - : Initialize(fn), FullName(name), Desc(desc) {} + : Initialize(fn), FullName(name), Desc(desc) {} }; - typedef std::vector<CheckerInfo> CheckerInfoList; + using CheckerInfoList = std::vector<CheckerInfo>; private: template <typename T> @@ -136,7 +141,8 @@ private: mutable llvm::StringMap<size_t> Packages; }; -} // end namespace ento -} // end namespace clang +} // namespace ento -#endif +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H diff --git a/include/clang/StaticAnalyzer/Core/IssueHash.h b/include/clang/StaticAnalyzer/Core/IssueHash.h index b3c4f1465594e..8cb6631fae747 100644 --- a/include/clang/StaticAnalyzer/Core/IssueHash.h +++ b/include/clang/StaticAnalyzer/Core/IssueHash.h @@ -17,7 +17,7 @@ class SourceManager; class FullSourceLoc; class LangOptions; -/// \brief Get an MD5 hash to help identify bugs. +/// Get an MD5 hash to help identify bugs. /// /// This function returns a hash that helps identify bugs within a source file. /// This identification can be utilized to diff diagnostic results on different @@ -41,7 +41,7 @@ llvm::SmallString<32> GetIssueHash(const SourceManager &SM, llvm::StringRef BugType, const Decl *D, const LangOptions &LangOpts); -/// \brief Get the string representation of issue hash. See GetIssueHash() for +/// Get the string representation of issue hash. See GetIssueHash() for /// more information. std::string GetIssueString(const SourceManager &SM, FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef BugType, diff --git a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h index ce512fd301ee7..2e81aa38c8de3 100644 --- a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h +++ b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h @@ -1,4 +1,4 @@ -//===--- PathDiagnosticClients.h - Path Diagnostic Clients ------*- C++ -*-===// +//===--- PathDiagnosticConsumers.h - Path Diagnostic Clients ------*- C++ -*-===// // // The LLVM Compiler Infrastructure // diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h index cc8a9b8ef0719..93edfcef2d5aa 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h @@ -16,7 +16,7 @@ namespace clang { namespace ento { -/// \brief A record of the "type" of an APSInt, used for conversions. +/// A record of the "type" of an APSInt, used for conversions. class APSIntType { uint32_t BitWidth; bool IsUnsigned; @@ -31,7 +31,7 @@ public: uint32_t getBitWidth() const { return BitWidth; } bool isUnsigned() const { return IsUnsigned; } - /// \brief Convert a given APSInt, in place, to match this type. + /// Convert a given APSInt, in place, to match this type. /// /// This behaves like a C cast: converting 255u8 (0xFF) to s16 gives /// 255 (0x00FF), and converting -1s8 (0xFF) to u16 gives 65535 (0xFFFF). @@ -93,7 +93,7 @@ public: return BitWidth == Other.BitWidth && IsUnsigned == Other.IsUnsigned; } - /// \brief Provide an ordering for finding a common conversion type. + /// Provide an ordering for finding a common conversion type. /// /// Unsigned integers are considered to be better conversion types than /// signed integers of the same width. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h index 15b930bc3f1eb..02b335761e708 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -126,6 +126,36 @@ public: AnalysisDeclContext *getAnalysisDeclContext(const Decl *D) { return AnaCtxMgr.getContext(D); } + + static bool isInCodeFile(SourceLocation SL, const SourceManager &SM) { + if (SM.isInMainFile(SL)) + return true; + + // Support the "unified sources" compilation method (eg. WebKit) that + // involves producing non-header files that include other non-header files. + // We should be included directly from a UnifiedSource* file + // and we shouldn't be a header - which is a very safe defensive check. + SourceLocation IL = SM.getIncludeLoc(SM.getFileID(SL)); + if (!IL.isValid() || !SM.isInMainFile(IL)) + return false; + // Should rather be "file name starts with", but the current .getFilename + // includes the full path. + if (SM.getFilename(IL).contains("UnifiedSource")) { + // It might be great to reuse FrontendOptions::getInputKindForExtension() + // but for now it doesn't discriminate between code and header files. + return llvm::StringSwitch<bool>(SM.getFilename(SL).rsplit('.').second) + .Cases("c", "m", "mm", "C", "cc", "cp", true) + .Cases("cpp", "CPP", "c++", "cxx", "cppm", true) + .Default(false); + } + + return false; + } + + bool isInCodeFile(SourceLocation SL) { + const SourceManager &SM = getASTContext().getSourceManager(); + return isInCodeFile(SL, SM); + } }; } // enAnaCtxMgrspace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index 4aa87443e4c22..0bbc6500d6ec3 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -1,4 +1,4 @@ -//=== BasicValueFactory.h - Basic values for Path Sens analysis --*- C++ -*---// +//==- BasicValueFactory.h - Basic values for Path Sens analysis --*- C++ -*-==// // // The LLVM Compiler Infrastructure // @@ -17,12 +17,26 @@ #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_BASICVALUEFACTORY_H #include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Allocator.h" +#include <cassert> +#include <cstdint> +#include <utility> namespace clang { + +class CXXBaseSpecifier; +class DeclaratorDecl; + namespace ento { class CompoundValData : public llvm::FoldingSetNode { @@ -34,7 +48,8 @@ public: assert(NonLoc::isCompoundType(t)); } - typedef llvm::ImmutableList<SVal>::iterator iterator; + using iterator = llvm::ImmutableList<SVal>::iterator; + iterator begin() const { return L.begin(); } iterator end() const { return L.end(); } @@ -47,6 +62,7 @@ public: class LazyCompoundValData : public llvm::FoldingSetNode { StoreRef store; const TypedValueRegion *region; + public: LazyCompoundValData(const StoreRef &st, const TypedValueRegion *r) : store(st), region(r) { @@ -63,16 +79,17 @@ public: void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, store, region); } }; -class PointerToMemberData: public llvm::FoldingSetNode { +class PointerToMemberData : public llvm::FoldingSetNode { const DeclaratorDecl *D; llvm::ImmutableList<const CXXBaseSpecifier *> L; public: PointerToMemberData(const DeclaratorDecl *D, llvm::ImmutableList<const CXXBaseSpecifier *> L) - : D(D), L(L) {} + : D(D), L(L) {} + + using iterator = llvm::ImmutableList<const CXXBaseSpecifier *>::iterator; - typedef llvm::ImmutableList<const CXXBaseSpecifier *>::iterator iterator; iterator begin() const { return L.begin(); } iterator end() const { return L.end(); } @@ -81,24 +98,25 @@ public: void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, D, L); } const DeclaratorDecl *getDeclaratorDecl() const {return D;} + llvm::ImmutableList<const CXXBaseSpecifier *> getCXXBaseList() const { return L; } }; class BasicValueFactory { - typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<llvm::APSInt> > - APSIntSetTy; + using APSIntSetTy = + llvm::FoldingSet<llvm::FoldingSetNodeWrapper<llvm::APSInt>>; ASTContext &Ctx; llvm::BumpPtrAllocator& BPAlloc; - APSIntSetTy APSIntSet; - void * PersistentSVals; - void * PersistentSValPairs; + APSIntSetTy APSIntSet; + void *PersistentSVals = nullptr; + void *PersistentSValPairs = nullptr; llvm::ImmutableList<SVal>::Factory SValListFactory; - llvm::ImmutableList<const CXXBaseSpecifier*>::Factory CXXBaseListFactory; + llvm::ImmutableList<const CXXBaseSpecifier *>::Factory CXXBaseListFactory; llvm::FoldingSet<CompoundValData> CompoundValDataSet; llvm::FoldingSet<LazyCompoundValData> LazyCompoundValDataSet; llvm::FoldingSet<PointerToMemberData> PointerToMemberDataSet; @@ -109,9 +127,8 @@ class BasicValueFactory { public: BasicValueFactory(ASTContext &ctx, llvm::BumpPtrAllocator &Alloc) - : Ctx(ctx), BPAlloc(Alloc), PersistentSVals(nullptr), - PersistentSValPairs(nullptr), SValListFactory(Alloc), - CXXBaseListFactory(Alloc) {} + : Ctx(ctx), BPAlloc(Alloc), SValListFactory(Alloc), + CXXBaseListFactory(Alloc) {} ~BasicValueFactory(); @@ -147,57 +164,57 @@ public: return getValue(TargetType.convert(From)); } - const llvm::APSInt& getIntValue(uint64_t X, bool isUnsigned) { + const llvm::APSInt &getIntValue(uint64_t X, bool isUnsigned) { QualType T = isUnsigned ? Ctx.UnsignedIntTy : Ctx.IntTy; return getValue(X, T); } - inline const llvm::APSInt& getMaxValue(const llvm::APSInt &v) { + const llvm::APSInt &getMaxValue(const llvm::APSInt &v) { return getValue(APSIntType(v).getMaxValue()); } - inline const llvm::APSInt& getMinValue(const llvm::APSInt &v) { + const llvm::APSInt &getMinValue(const llvm::APSInt &v) { return getValue(APSIntType(v).getMinValue()); } - inline const llvm::APSInt& getMaxValue(QualType T) { + const llvm::APSInt &getMaxValue(QualType T) { return getValue(getAPSIntType(T).getMaxValue()); } - inline const llvm::APSInt& getMinValue(QualType T) { + const llvm::APSInt &getMinValue(QualType T) { return getValue(getAPSIntType(T).getMinValue()); } - inline const llvm::APSInt& Add1(const llvm::APSInt& V) { + const llvm::APSInt &Add1(const llvm::APSInt &V) { llvm::APSInt X = V; ++X; return getValue(X); } - inline const llvm::APSInt& Sub1(const llvm::APSInt& V) { + const llvm::APSInt &Sub1(const llvm::APSInt &V) { llvm::APSInt X = V; --X; return getValue(X); } - inline const llvm::APSInt& getZeroWithTypeSize(QualType T) { + const llvm::APSInt &getZeroWithTypeSize(QualType T) { assert(T->isScalarType()); return getValue(0, Ctx.getTypeSize(T), true); } - inline const llvm::APSInt& getZeroWithPtrWidth(bool isUnsigned = true) { + const llvm::APSInt &getZeroWithPtrWidth(bool isUnsigned = true) { return getValue(0, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); } - inline const llvm::APSInt &getIntWithPtrWidth(uint64_t X, bool isUnsigned) { + const llvm::APSInt &getIntWithPtrWidth(uint64_t X, bool isUnsigned) { return getValue(X, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); } - inline const llvm::APSInt& getTruthValue(bool b, QualType T) { - return getValue(b ? 1 : 0, Ctx.getTypeSize(T), false); + const llvm::APSInt &getTruthValue(bool b, QualType T) { + return getValue(b ? 1 : 0, Ctx.getIntWidth(T), true); } - inline const llvm::APSInt& getTruthValue(bool b) { + const llvm::APSInt &getTruthValue(bool b) { return getTruthValue(b, Ctx.getLogicalOperationType()); } @@ -229,7 +246,7 @@ public: return CXXBaseListFactory.add(CBS, L); } - const clang::ento::PointerToMemberData *accumCXXBase( + const PointerToMemberData *accumCXXBase( llvm::iterator_range<CastExpr::path_const_iterator> PathRange, const nonloc::PointerToMember &PTM); @@ -246,8 +263,8 @@ public: const SVal* getPersistentSVal(SVal X); }; -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_BASICVALUEFACTORY_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h b/include/clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h index 1d779e6cb6ec0..b94dadff23427 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h @@ -25,7 +25,7 @@ class StackFrameContext; namespace ento { /// \class BlockCounter -/// \brief An abstract data type used to count the number of times a given +/// An abstract data type used to count the number of times a given /// block has been visited along a path analyzed by CoreEngine. class BlockCounter { void *Data; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 9fec217aca725..9c667f912e9fd 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -1,4 +1,4 @@ -//===- CallEvent.h - Wrapper for all function and method calls ----*- C++ -*--// +//===- CallEvent.h - Wrapper for all function and method calls --*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -16,19 +16,42 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLEVENT_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLEVENT_H +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" -#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <cassert> +#include <limits> #include <utility> namespace clang { + +class LocationContext; class ProgramPoint; class ProgramPointTag; +class StackFrameContext; namespace ento { @@ -48,31 +71,31 @@ enum CallEventKind { }; class CallEvent; -class CallEventManager; /// This class represents a description of a function call using the number of /// arguments and the name of the function. class CallDescription { friend CallEvent; - mutable IdentifierInfo *II; - mutable bool IsLookupDone; + + mutable IdentifierInfo *II = nullptr; + mutable bool IsLookupDone = false; StringRef FuncName; unsigned RequiredArgs; public: - const static unsigned NoArgRequirement = ~0; - /// \brief Constructs a CallDescription object. + const static unsigned NoArgRequirement = std::numeric_limits<unsigned>::max(); + + /// Constructs a CallDescription object. /// /// @param FuncName The name of the function that will be matched. /// /// @param RequiredArgs The number of arguments that is expected to match a - /// call. Omit this parameter to match every occurance of call with a given + /// call. Omit this parameter to match every occurrence of call with a given /// name regardless the number of arguments. CallDescription(StringRef FuncName, unsigned RequiredArgs = NoArgRequirement) - : II(nullptr), IsLookupDone(false), FuncName(FuncName), - RequiredArgs(RequiredArgs) {} + : FuncName(FuncName), RequiredArgs(RequiredArgs) {} - /// \brief Get the name of the function that this object matches. + /// Get the name of the function that this object matches. StringRef getFunctionName() const { return FuncName; } }; @@ -95,7 +118,7 @@ public: }; /// \class RuntimeDefinition -/// \brief Defines the runtime definition of the called function. +/// Defines the runtime definition of the called function. /// /// Encapsulates the information we have about which Decl will be used /// when the call is executed on the given path. When dealing with dynamic @@ -104,21 +127,22 @@ public: class RuntimeDefinition { /// The Declaration of the function which could be called at runtime. /// NULL if not available. - const Decl *D; + const Decl *D = nullptr; /// The region representing an object (ObjC/C++) on which the method is /// called. With dynamic dispatch, the method definition depends on the /// runtime type of this object. NULL when the DynamicTypeInfo is /// precise. - const MemRegion *R; + const MemRegion *R = nullptr; public: - RuntimeDefinition(): D(nullptr), R(nullptr) {} - RuntimeDefinition(const Decl *InD): D(InD), R(nullptr) {} + RuntimeDefinition() = default; + RuntimeDefinition(const Decl *InD): D(InD) {} RuntimeDefinition(const Decl *InD, const MemRegion *InR): D(InD), R(InR) {} + const Decl *getDecl() { return D; } - /// \brief Check if the definition we have is precise. + /// Check if the definition we have is precise. /// If not, it is possible that the call dispatches to another definition at /// execution time. bool mayHaveOtherDefinitions() { return R != nullptr; } @@ -128,7 +152,7 @@ public: const MemRegion *getDispatchRegion() { return R; } }; -/// \brief Represents an abstract call to a function or method along a +/// Represents an abstract call to a function or method along a /// particular path. /// /// CallEvents are created through the factory methods of CallEventManager. @@ -139,15 +163,13 @@ public: /// Use the "Data" and "Location" fields instead. class CallEvent { public: - typedef CallEventKind Kind; + using Kind = CallEventKind; private: ProgramStateRef State; const LocationContext *LCtx; llvm::PointerUnion<const Expr *, const Decl *> Origin; - void operator=(const CallEvent &) = delete; - protected: // This is user data for subclasses. const void *Data; @@ -158,9 +180,10 @@ protected: SourceLocation Location; private: - mutable unsigned RefCount; - template <typename T> friend struct llvm::IntrusiveRefCntPtrInfo; + + mutable unsigned RefCount = 0; + void Retain() const { ++RefCount; } void Release() const; @@ -168,72 +191,72 @@ protected: friend class CallEventManager; CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx) - : State(std::move(state)), LCtx(lctx), Origin(E), RefCount(0) {} + : State(std::move(state)), LCtx(lctx), Origin(E) {} CallEvent(const Decl *D, ProgramStateRef state, const LocationContext *lctx) - : State(std::move(state)), LCtx(lctx), Origin(D), RefCount(0) {} + : State(std::move(state)), LCtx(lctx), Origin(D) {} // DO NOT MAKE PUBLIC CallEvent(const CallEvent &Original) - : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin), - Data(Original.Data), Location(Original.Location), RefCount(0) {} + : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin), + Data(Original.Data), Location(Original.Location) {} /// Copies this CallEvent, with vtable intact, into a new block of memory. virtual void cloneTo(void *Dest) const = 0; - /// \brief Get the value of arbitrary expressions at this point in the path. + /// Get the value of arbitrary expressions at this point in the path. SVal getSVal(const Stmt *S) const { return getState()->getSVal(S, getLocationContext()); } + using ValueList = SmallVectorImpl<SVal>; - typedef SmallVectorImpl<SVal> ValueList; - - /// \brief Used to specify non-argument regions that will be invalidated as a + /// Used to specify non-argument regions that will be invalidated as a /// result of this call. virtual void getExtraInvalidatedValues(ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const {} public: - virtual ~CallEvent() {} + CallEvent &operator=(const CallEvent &) = delete; + virtual ~CallEvent() = default; - /// \brief Returns the kind of call this is. + /// Returns the kind of call this is. virtual Kind getKind() const = 0; - /// \brief Returns the declaration of the function or method that will be + /// Returns the declaration of the function or method that will be /// called. May be null. virtual const Decl *getDecl() const { return Origin.dyn_cast<const Decl *>(); } - /// \brief The state in which the call is being evaluated. + /// The state in which the call is being evaluated. const ProgramStateRef &getState() const { return State; } - /// \brief The context in which the call is being evaluated. + /// The context in which the call is being evaluated. const LocationContext *getLocationContext() const { return LCtx; } - /// \brief Returns the definition of the function or method that will be + /// Returns the definition of the function or method that will be /// called. virtual RuntimeDefinition getRuntimeDefinition() const = 0; - /// \brief Returns the expression whose value will be the result of this call. + /// Returns the expression whose value will be the result of this call. /// May be null. const Expr *getOriginExpr() const { return Origin.dyn_cast<const Expr *>(); } - /// \brief Returns the number of arguments (explicit and implicit). + /// Returns the number of arguments (explicit and implicit). /// /// Note that this may be greater than the number of parameters in the /// callee's declaration, and that it may include arguments not written in /// the source. virtual unsigned getNumArgs() const = 0; - /// \brief Returns true if the callee is known to be from a system header. + /// Returns true if the callee is known to be from a system header. bool isInSystemHeader() const { const Decl *D = getDecl(); if (!D) @@ -248,57 +271,57 @@ public: // Special case for implicitly-declared global operator new/delete. // These should be considered system functions. - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (const auto *FD = dyn_cast<FunctionDecl>(D)) return FD->isOverloadedOperator() && FD->isImplicit() && FD->isGlobal(); return false; } - /// \brief Returns true if the CallEvent is a call to a function that matches + /// Returns true if the CallEvent is a call to a function that matches /// the CallDescription. /// /// Note that this function is not intended to be used to match Obj-C method /// calls. bool isCalled(const CallDescription &CD) const; - /// \brief Returns a source range for the entire call, suitable for + /// Returns a source range for the entire call, suitable for /// outputting in diagnostics. virtual SourceRange getSourceRange() const { return getOriginExpr()->getSourceRange(); } - /// \brief Returns the value of a given argument at the time of the call. + /// Returns the value of a given argument at the time of the call. virtual SVal getArgSVal(unsigned Index) const; - /// \brief Returns the expression associated with a given argument. + /// Returns the expression associated with a given argument. /// May be null if this expression does not appear in the source. virtual const Expr *getArgExpr(unsigned Index) const { return nullptr; } - /// \brief Returns the source range for errors associated with this argument. + /// Returns the source range for errors associated with this argument. /// /// May be invalid if the argument is not written in the source. virtual SourceRange getArgSourceRange(unsigned Index) const; - /// \brief Returns the result type, adjusted for references. + /// Returns the result type, adjusted for references. QualType getResultType() const; - /// \brief Returns the return value of the call. + /// Returns the return value of the call. /// /// This should only be called if the CallEvent was created using a state in /// which the return value has already been bound to the origin expression. SVal getReturnValue() const; - /// \brief Returns true if the type of any of the non-null arguments satisfies + /// Returns true if the type of any of the non-null arguments satisfies /// the condition. bool hasNonNullArgumentsWithType(bool (*Condition)(QualType)) const; - /// \brief Returns true if any of the arguments appear to represent callbacks. + /// Returns true if any of the arguments appear to represent callbacks. bool hasNonZeroCallbackArg() const; - /// \brief Returns true if any of the arguments is void*. + /// Returns true if any of the arguments is void*. bool hasVoidPointerToNonConstArg() const; - /// \brief Returns true if any of the arguments are known to escape to long- + /// Returns true if any of the arguments are known to escape to long- /// term storage, even if this method will not modify them. // NOTE: The exact semantics of this are still being defined! // We don't really want a list of hardcoded exceptions in the long run, @@ -307,7 +330,7 @@ public: return hasNonZeroCallbackArg(); } - /// \brief Returns true if the callee is an externally-visible function in the + /// Returns true if the callee is an externally-visible function in the /// top-level namespace, such as \c malloc. /// /// You can use this call to determine that a particular function really is @@ -325,7 +348,7 @@ public: // precise callbacks. bool isGlobalCFunction(StringRef SpecificName = StringRef()) const; - /// \brief Returns the name of the callee, if its name is a simple identifier. + /// Returns the name of the callee, if its name is a simple identifier. /// /// Note that this will fail for Objective-C methods, blocks, and C++ /// overloaded operators. The former is named by a Selector rather than a @@ -333,25 +356,25 @@ public: // FIXME: Move this down to AnyFunctionCall once checkers have more // precise callbacks. const IdentifierInfo *getCalleeIdentifier() const { - const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(getDecl()); + const auto *ND = dyn_cast_or_null<NamedDecl>(getDecl()); if (!ND) return nullptr; return ND->getIdentifier(); } - /// \brief Returns an appropriate ProgramPoint for this call. + /// Returns an appropriate ProgramPoint for this call. ProgramPoint getProgramPoint(bool IsPreVisit = false, const ProgramPointTag *Tag = nullptr) const; - /// \brief Returns a new state with all argument regions invalidated. + /// Returns a new state with all argument regions invalidated. /// /// This accepts an alternate state in case some processing has already /// occurred. ProgramStateRef invalidateRegions(unsigned BlockCount, ProgramStateRef Orig = nullptr) const; - typedef std::pair<Loc, SVal> FrameBindingTy; - typedef SmallVectorImpl<FrameBindingTy> BindingsTy; + using FrameBindingTy = std::pair<Loc, SVal>; + using BindingsTy = SmallVectorImpl<FrameBindingTy>; /// Populates the given SmallVector with the bindings in the callee's stack /// frame at the start of this call. @@ -367,16 +390,16 @@ public: return cloneWithState<CallEvent>(NewState); } - /// \brief Returns true if this is a statement is a function or method call + /// Returns true if this is a statement is a function or method call /// of some kind. static bool isCallStmt(const Stmt *S); - /// \brief Returns the result type of a function or method declaration. + /// Returns the result type of a function or method declaration. /// /// This will return a null QualType if the result type cannot be determined. static QualType getDeclaredResultType(const Decl *D); - /// \brief Returns true if the given decl is known to be variadic. + /// Returns true if the given decl is known to be variadic. /// /// \p D must not be null. static bool isVariadic(const Decl *D); @@ -393,10 +416,10 @@ public: /// Remember that the number of formal parameters may not match the number /// of arguments for all calls. However, the first parameter will always /// correspond with the argument value returned by \c getArgSVal(0). - virtual ArrayRef<ParmVarDecl*> parameters() const = 0; + virtual ArrayRef<ParmVarDecl *> parameters() const = 0; - typedef llvm::mapped_iterator<ArrayRef<ParmVarDecl*>::iterator, GetTypeFn> - param_type_iterator; + using param_type_iterator = + llvm::mapped_iterator<ArrayRef<ParmVarDecl *>::iterator, GetTypeFn>; /// Returns an iterator over the types of the call's formal parameters. /// @@ -416,18 +439,17 @@ public: void dump() const; }; - -/// \brief Represents a call to any sort of function that might have a +/// Represents a call to any sort of function that might have a /// FunctionDecl. class AnyFunctionCall : public CallEvent { protected: AnyFunctionCall(const Expr *E, ProgramStateRef St, const LocationContext *LCtx) - : CallEvent(E, St, LCtx) {} + : CallEvent(E, St, LCtx) {} AnyFunctionCall(const Decl *D, ProgramStateRef St, const LocationContext *LCtx) - : CallEvent(D, St, LCtx) {} - AnyFunctionCall(const AnyFunctionCall &Other) : CallEvent(Other) {} + : CallEvent(D, St, LCtx) {} + AnyFunctionCall(const AnyFunctionCall &Other) = default; public: // This function is overridden by subclasses, but they must return @@ -451,7 +473,7 @@ public: } }; -/// \brief Represents a C function or static C++ member function call. +/// Represents a C function or static C++ member function call. /// /// Example: \c fun() class SimpleFunctionCall : public AnyFunctionCall { @@ -460,9 +482,9 @@ class SimpleFunctionCall : public AnyFunctionCall { protected: SimpleFunctionCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) - : AnyFunctionCall(CE, St, LCtx) {} - SimpleFunctionCall(const SimpleFunctionCall &Other) - : AnyFunctionCall(Other) {} + : AnyFunctionCall(CE, St, LCtx) {} + SimpleFunctionCall(const SimpleFunctionCall &Other) = default; + void cloneTo(void *Dest) const override { new (Dest) SimpleFunctionCall(*this); } @@ -487,7 +509,7 @@ public: } }; -/// \brief Represents a call to a block. +/// Represents a call to a block. /// /// Example: <tt>^{ /* ... */ }()</tt> class BlockCall : public CallEvent { @@ -496,9 +518,9 @@ class BlockCall : public CallEvent { protected: BlockCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) - : CallEvent(CE, St, LCtx) {} + : CallEvent(CE, St, LCtx) {} + BlockCall(const BlockCall &Other) = default; - BlockCall(const BlockCall &Other) : CallEvent(Other) {} void cloneTo(void *Dest) const override { new (Dest) BlockCall(*this); } void getExtraInvalidatedValues(ValueList &Values, @@ -515,7 +537,7 @@ public: return getOriginExpr()->getArg(Index); } - /// \brief Returns the region associated with this instance of the block. + /// Returns the region associated with this instance of the block. /// /// This may be NULL if the block's origin is unknown. const BlockDataRegion *getBlockRegion() const; @@ -535,7 +557,7 @@ public: return BD->isConversionFromLambda(); } - /// \brief For a block converted from a C++ lambda, returns the block + /// For a block converted from a C++ lambda, returns the block /// VarRegion for the variable holding the captured C++ lambda record. const VarRegion *getRegionStoringCapturedLambda() const { assert(isConversionFromLambda()); @@ -594,28 +616,26 @@ public: } }; -/// \brief Represents a non-static C++ member function call, no matter how +/// Represents a non-static C++ member function call, no matter how /// it is written. class CXXInstanceCall : public AnyFunctionCall { protected: - void getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const override; - CXXInstanceCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) - : AnyFunctionCall(CE, St, LCtx) {} + : AnyFunctionCall(CE, St, LCtx) {} CXXInstanceCall(const FunctionDecl *D, ProgramStateRef St, const LocationContext *LCtx) - : AnyFunctionCall(D, St, LCtx) {} - + : AnyFunctionCall(D, St, LCtx) {} + CXXInstanceCall(const CXXInstanceCall &Other) = default; - CXXInstanceCall(const CXXInstanceCall &Other) : AnyFunctionCall(Other) {} + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; public: - /// \brief Returns the expression representing the implicit 'this' object. + /// Returns the expression representing the implicit 'this' object. virtual const Expr *getCXXThisExpr() const { return nullptr; } - /// \brief Returns the value of the implicit 'this' object. + /// Returns the value of the implicit 'this' object. virtual SVal getCXXThisVal() const; const FunctionDecl *getDecl() const override; @@ -631,7 +651,7 @@ public: } }; -/// \brief Represents a non-static C++ member function call. +/// Represents a non-static C++ member function call. /// /// Example: \c obj.fun() class CXXMemberCall : public CXXInstanceCall { @@ -640,9 +660,9 @@ class CXXMemberCall : public CXXInstanceCall { protected: CXXMemberCall(const CXXMemberCallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) - : CXXInstanceCall(CE, St, LCtx) {} + : CXXInstanceCall(CE, St, LCtx) {} + CXXMemberCall(const CXXMemberCall &Other) = default; - CXXMemberCall(const CXXMemberCall &Other) : CXXInstanceCall(Other) {} void cloneTo(void *Dest) const override { new (Dest) CXXMemberCall(*this); } public: @@ -671,7 +691,7 @@ public: } }; -/// \brief Represents a C++ overloaded operator call where the operator is +/// Represents a C++ overloaded operator call where the operator is /// implemented as a non-static member function. /// /// Example: <tt>iter + 1</tt> @@ -681,10 +701,9 @@ class CXXMemberOperatorCall : public CXXInstanceCall { protected: CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) - : CXXInstanceCall(CE, St, LCtx) {} + : CXXInstanceCall(CE, St, LCtx) {} + CXXMemberOperatorCall(const CXXMemberOperatorCall &Other) = default; - CXXMemberOperatorCall(const CXXMemberOperatorCall &Other) - : CXXInstanceCall(Other) {} void cloneTo(void *Dest) const override { new (Dest) CXXMemberOperatorCall(*this); } @@ -697,6 +716,7 @@ public: unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs() - 1; } + const Expr *getArgExpr(unsigned Index) const override { return getOriginExpr()->getArg(Index + 1); } @@ -710,7 +730,7 @@ public: } }; -/// \brief Represents an implicit call to a C++ destructor. +/// Represents an implicit call to a C++ destructor. /// /// This can occur at the end of a scope (for automatic objects), at the end /// of a full-expression (for temporaries), or as part of a delete. @@ -718,7 +738,7 @@ class CXXDestructorCall : public CXXInstanceCall { friend class CallEventManager; protected: - typedef llvm::PointerIntPair<const MemRegion *, 1, bool> DtorDataTy; + using DtorDataTy = llvm::PointerIntPair<const MemRegion *, 1, bool>; /// Creates an implicit destructor. /// @@ -730,12 +750,13 @@ protected: CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, const MemRegion *Target, bool IsBaseDestructor, ProgramStateRef St, const LocationContext *LCtx) - : CXXInstanceCall(DD, St, LCtx) { + : CXXInstanceCall(DD, St, LCtx) { Data = DtorDataTy(Target, IsBaseDestructor).getOpaqueValue(); Location = Trigger->getLocEnd(); } - CXXDestructorCall(const CXXDestructorCall &Other) : CXXInstanceCall(Other) {} + CXXDestructorCall(const CXXDestructorCall &Other) = default; + void cloneTo(void *Dest) const override {new (Dest) CXXDestructorCall(*this);} public: @@ -744,7 +765,7 @@ public: RuntimeDefinition getRuntimeDefinition() const override; - /// \brief Returns the value of the implicit 'this' object. + /// Returns the value of the implicit 'this' object. SVal getCXXThisVal() const override; /// Returns true if this is a call to a base class destructor. @@ -759,7 +780,7 @@ public: } }; -/// \brief Represents a call to a C++ constructor. +/// Represents a call to a C++ constructor. /// /// Example: \c T(1) class CXXConstructorCall : public AnyFunctionCall { @@ -775,11 +796,12 @@ protected: /// \param LCtx The location context at this point in the program. CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *Target, ProgramStateRef St, const LocationContext *LCtx) - : AnyFunctionCall(CE, St, LCtx) { + : AnyFunctionCall(CE, St, LCtx) { Data = Target; } - CXXConstructorCall(const CXXConstructorCall &Other) : AnyFunctionCall(Other){} + CXXConstructorCall(const CXXConstructorCall &Other) = default; + void cloneTo(void *Dest) const override { new (Dest) CXXConstructorCall(*this); } void getExtraInvalidatedValues(ValueList &Values, @@ -800,7 +822,7 @@ public: return getOriginExpr()->getArg(Index); } - /// \brief Returns the value of the implicit 'this' object. + /// Returns the value of the implicit 'this' object. SVal getCXXThisVal() const; void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, @@ -813,7 +835,7 @@ public: } }; -/// \brief Represents the memory allocation call in a C++ new-expression. +/// Represents the memory allocation call in a C++ new-expression. /// /// This is a call to "operator new". class CXXAllocatorCall : public AnyFunctionCall { @@ -822,9 +844,9 @@ class CXXAllocatorCall : public AnyFunctionCall { protected: CXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef St, const LocationContext *LCtx) - : AnyFunctionCall(E, St, LCtx) {} + : AnyFunctionCall(E, St, LCtx) {} + CXXAllocatorCall(const CXXAllocatorCall &Other) = default; - CXXAllocatorCall(const CXXAllocatorCall &Other) : AnyFunctionCall(Other) {} void cloneTo(void *Dest) const override { new (Dest) CXXAllocatorCall(*this); } public: @@ -854,7 +876,7 @@ public: } }; -/// \brief Represents the ways an Objective-C message send can occur. +/// Represents the ways an Objective-C message send can occur. // // Note to maintainers: OCM_Message should always be last, since it does not // need to fit in the Data field's low bits. @@ -864,7 +886,7 @@ enum ObjCMessageKind { OCM_Message }; -/// \brief Represents any expression that calls an Objective-C method. +/// Represents any expression that calls an Objective-C method. /// /// This includes all of the kinds listed in ObjCMessageKind. class ObjCMethodCall : public CallEvent { @@ -875,11 +897,12 @@ class ObjCMethodCall : public CallEvent { protected: ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St, const LocationContext *LCtx) - : CallEvent(Msg, St, LCtx) { + : CallEvent(Msg, St, LCtx) { Data = nullptr; } - ObjCMethodCall(const ObjCMethodCall &Other) : CallEvent(Other) {} + ObjCMethodCall(const ObjCMethodCall &Other) = default; + void cloneTo(void *Dest) const override { new (Dest) ObjCMethodCall(*this); } void getExtraInvalidatedValues(ValueList &Values, @@ -893,12 +916,15 @@ public: virtual const ObjCMessageExpr *getOriginExpr() const { return cast<ObjCMessageExpr>(CallEvent::getOriginExpr()); } + const ObjCMethodDecl *getDecl() const override { return getOriginExpr()->getMethodDecl(); } + unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs(); } + const Expr *getArgExpr(unsigned Index) const override { return getOriginExpr()->getArg(Index); } @@ -906,22 +932,24 @@ public: bool isInstanceMessage() const { return getOriginExpr()->isInstanceMessage(); } + ObjCMethodFamily getMethodFamily() const { return getOriginExpr()->getMethodFamily(); } + Selector getSelector() const { return getOriginExpr()->getSelector(); } SourceRange getSourceRange() const override; - /// \brief Returns the value of the receiver at the time of this call. + /// Returns the value of the receiver at the time of this call. SVal getReceiverSVal() const; - /// \brief Return the value of 'self' if available. + /// Return the value of 'self' if available. SVal getSelfSVal() const; - /// \brief Get the interface for the receiver. + /// Get the interface for the receiver. /// /// This works whether this is an instance message or a class message. /// However, it currently just uses the static type of the receiver. @@ -929,7 +957,7 @@ public: return getOriginExpr()->getReceiverInterface(); } - /// \brief Checks if the receiver refers to 'self' or 'super'. + /// Checks if the receiver refers to 'self' or 'super'. bool isReceiverSelfOrSuper() const; /// Returns how the message was written in the source (property access, @@ -971,8 +999,7 @@ public: } }; - -/// \brief Manages the lifetime of CallEvent objects. +/// Manages the lifetime of CallEvent objects. /// /// CallEventManager provides a way to create arbitrary CallEvents "on the /// stack" as if they were value objects by keeping a cache of CallEvent-sized @@ -984,7 +1011,8 @@ class CallEventManager { llvm::BumpPtrAllocator &Alloc; SmallVector<void *, 8> Cache; - typedef SimpleFunctionCall CallEventTemplateTy; + + using CallEventTemplateTy = SimpleFunctionCall; void reclaim(const void *Memory) { Cache.push_back(const_cast<void *>(Memory)); @@ -1032,11 +1060,9 @@ class CallEventManager { public: CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {} - CallEventRef<> getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State); - CallEventRef<> getSimpleCall(const CallExpr *E, ProgramStateRef State, const LocationContext *LCtx); @@ -1067,7 +1093,6 @@ public: } }; - template <typename T> CallEventRef<T> CallEvent::cloneWithState(ProgramStateRef NewState) const { assert(isa<T>(*this) && "Cloning to unrelated type"); @@ -1099,19 +1124,22 @@ inline void CallEvent::Release() const { this->~CallEvent(); } -} // end namespace ento -} // end namespace clang +} // namespace ento + +} // namespace clang namespace llvm { - // Support isa<>, cast<>, and dyn_cast<> for CallEventRef. - template<class T> struct simplify_type< clang::ento::CallEventRef<T> > { - typedef const T *SimpleType; - static SimpleType - getSimplifiedValue(clang::ento::CallEventRef<T> Val) { - return Val.get(); - } - }; -} +// Support isa<>, cast<>, and dyn_cast<> for CallEventRef. +template<class T> struct simplify_type< clang::ento::CallEventRef<T>> { + using SimpleType = const T *; + + static SimpleType + getSimplifiedValue(clang::ento::CallEventRef<T> Val) { + return Val.get(); + } +}; + +} // namespace llvm -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLEVENT_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 78d38a3d598dc..f3a0ca7b66081 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -111,17 +111,17 @@ public: return Eng.getStoreManager(); } - /// \brief Returns the previous node in the exploded graph, which includes + /// Returns the previous node in the exploded graph, which includes /// the state of the program before the checker ran. Note, checkers should /// not retain the node in their state since the nodes might get invalidated. ExplodedNode *getPredecessor() { return Pred; } const ProgramStateRef &getState() const { return Pred->getState(); } - /// \brief Check if the checker changed the state of the execution; ex: added + /// Check if the checker changed the state of the execution; ex: added /// a new transition or a bug report. bool isDifferent() { return Changed; } - /// \brief Returns the number of times the current block has been visited + /// Returns the number of times the current block has been visited /// along the analyzed path. unsigned blockCount() const { return NB.getContext().blockCount(); @@ -174,12 +174,12 @@ public: return Pred->getLocationContext()->getAnalysisDeclContext(); } - /// \brief Get the blockID. + /// Get the blockID. unsigned getBlockID() const { return NB.getContext().getBlock()->getBlockID(); } - /// \brief If the given node corresponds to a PostStore program point, + /// If the given node corresponds to a PostStore program point, /// retrieve the location region as it was uttered in the code. /// /// This utility can be useful for generating extensive diagnostics, for @@ -191,19 +191,19 @@ public: return nullptr; } - /// \brief Get the value of arbitrary expressions at this point in the path. + /// Get the value of arbitrary expressions at this point in the path. SVal getSVal(const Stmt *S) const { - return getState()->getSVal(S, getLocationContext()); + return Pred->getSVal(S); } - /// \brief Returns true if the value of \p E is greater than or equal to \p + /// Returns true if the value of \p E is greater than or equal to \p /// Val under unsigned comparison bool isGreaterOrEqual(const Expr *E, unsigned long long Val); /// Returns true if the value of \p E is negative. bool isNegative(const Expr *E); - /// \brief Generates a new transition in the program state graph + /// Generates a new transition in the program state graph /// (ExplodedGraph). Uses the default CheckerContext predecessor node. /// /// @param State The state of the generated node. If not specified, the state @@ -217,7 +217,7 @@ public: return addTransitionImpl(State ? State : getState(), false, nullptr, Tag); } - /// \brief Generates a new transition with the given predecessor. + /// Generates a new transition with the given predecessor. /// Allows checkers to generate a chain of nodes. /// /// @param State The state of the generated node. @@ -230,7 +230,7 @@ public: return addTransitionImpl(State, false, Pred, Tag); } - /// \brief Generate a sink node. Generating a sink stops exploration of the + /// Generate a sink node. Generating a sink stops exploration of the /// given path. To create a sink node for the purpose of reporting an error, /// checkers should use generateErrorNode() instead. ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, @@ -238,7 +238,7 @@ public: return addTransitionImpl(State ? State : getState(), true, Pred, Tag); } - /// \brief Generate a transition to a node that will be used to report + /// Generate a transition to a node that will be used to report /// an error. This node will be a sink. That is, it will stop exploration of /// the given path. /// @@ -251,7 +251,7 @@ public: (Tag ? Tag : Location.getTag())); } - /// \brief Generate a transition to a node that will be used to report + /// Generate a transition to a node that will be used to report /// an error. This node will not be a sink. That is, exploration will /// continue along this path. /// @@ -264,23 +264,23 @@ public: return addTransition(State, (Tag ? Tag : Location.getTag())); } - /// \brief Emit the diagnostics report. + /// Emit the diagnostics report. void emitReport(std::unique_ptr<BugReport> R) { Changed = true; Eng.getBugReporter().emitReport(std::move(R)); } - /// \brief Returns the word that should be used to refer to the declaration + /// Returns the word that should be used to refer to the declaration /// in the report. StringRef getDeclDescription(const Decl *D); - /// \brief Get the declaration of the called function (path-sensitive). + /// Get the declaration of the called function (path-sensitive). const FunctionDecl *getCalleeDecl(const CallExpr *CE) const; - /// \brief Get the name of the called function (path-sensitive). + /// Get the name of the called function (path-sensitive). StringRef getCalleeName(const FunctionDecl *FunDecl) const; - /// \brief Get the identifier of the called function (path-sensitive). + /// Get the identifier of the called function (path-sensitive). const IdentifierInfo *getCalleeIdentifier(const CallExpr *CE) const { const FunctionDecl *FunDecl = getCalleeDecl(CE); if (FunDecl) @@ -289,13 +289,13 @@ public: return nullptr; } - /// \brief Get the name of the called function (path-sensitive). + /// Get the name of the called function (path-sensitive). StringRef getCalleeName(const CallExpr *CE) const { const FunctionDecl *FunDecl = getCalleeDecl(CE); return getCalleeName(FunDecl); } - /// \brief Returns true if the callee is an externally-visible function in the + /// Returns true if the callee is an externally-visible function in the /// top-level namespace, such as \c malloc. /// /// If a name is provided, the function must additionally match the given @@ -308,7 +308,7 @@ public: static bool isCLibraryFunction(const FunctionDecl *FD, StringRef Name = StringRef()); - /// \brief Depending on wither the location corresponds to a macro, return + /// Depending on wither the location corresponds to a macro, return /// either the macro name or the token spelling. /// /// This could be useful when checkers' logic depends on whether a function diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h index 8dda6367c2e00..7f34a7a5b19dd 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h @@ -21,6 +21,8 @@ namespace clang { class Expr; class VarDecl; +class QualType; +class AttributedType; namespace ento { @@ -42,6 +44,25 @@ template <class T> bool containsStmt(const Stmt *S) { std::pair<const clang::VarDecl *, const clang::Expr *> parseAssignment(const Stmt *S); +// Do not reorder! The getMostNullable method relies on the order. +// Optimization: Most pointers expected to be unspecified. When a symbol has an +// unspecified or nonnull type non of the rules would indicate any problem for +// that symbol. For this reason only nullable and contradicted nullability are +// stored for a symbol. When a symbol is already contradicted, it can not be +// casted back to nullable. +enum class Nullability : char { + Contradicted, // Tracked nullability is contradicted by an explicit cast. Do + // not report any nullability related issue for this symbol. + // This nullability is propagated aggressively to avoid false + // positive results. See the comment on getMostNullable method. + Nullable, + Unspecified, + Nonnull +}; + +/// Get nullability annotation for a given type. +Nullability getNullabilityAnnotation(QualType Type); + } // end GR namespace } // end clang namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h index c01600d5c969e..d4f8fbaa43df3 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -1,4 +1,4 @@ -//== ConstraintManager.h - Constraints on symbolic values.-------*- C++ -*--==// +//===- ConstraintManager.h - Constraints on symbolic values. ----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -14,28 +14,44 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CONSTRAINTMANAGER_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CONSTRAINTMANAGER_H +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/SaveAndRestore.h" +#include <memory> +#include <utility> namespace llvm { + class APSInt; -} + +} // namespace llvm namespace clang { namespace ento { +class ProgramStateManager; class SubEngine; +class SymbolReaper; class ConditionTruthVal { Optional<bool> Val; + public: /// Construct a ConditionTruthVal indicating the constraint is constrained /// to either true or false, depending on the boolean value provided. ConditionTruthVal(bool constraint) : Val(constraint) {} /// Construct a ConstraintVal indicating the constraint is underconstrained. - ConditionTruthVal() {} + ConditionTruthVal() = default; + + /// \return Stored value, assuming that the value is known. + /// Crashes otherwise. + bool getValue() const { + return *Val; + } /// Return true if the constraint is perfectly constrained to 'true'. bool isConstrainedTrue() const { @@ -61,14 +77,14 @@ public: class ConstraintManager { public: - ConstraintManager() : NotifyAssumeClients(true) {} - + ConstraintManager() = default; virtual ~ConstraintManager(); + virtual ProgramStateRef assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption) = 0; - typedef std::pair<ProgramStateRef, ProgramStateRef> ProgramStatePair; + using ProgramStatePair = std::pair<ProgramStateRef, ProgramStateRef>; /// Returns a pair of states (StTrue, StFalse) where the given condition is /// assumed to be true or false, respectively. @@ -129,7 +145,7 @@ public: return ProgramStatePair(StInRange, StOutOfRange); } - /// \brief If a symbol is perfectly constrained to a constant, attempt + /// If a symbol is perfectly constrained to a constant, attempt /// to return the concrete value. /// /// Note that a ConstraintManager is not obligated to return a concretized @@ -166,7 +182,7 @@ protected: /// /// Note that this flag allows the ConstraintManager to be re-entrant, /// but not thread-safe. - bool NotifyAssumeClients; + bool NotifyAssumeClients = true; /// canReasonAbout - Not all ConstraintManagers can accurately reason about /// all SVal values. This method returns true if the ConstraintManager can @@ -187,8 +203,7 @@ CreateRangeConstraintManager(ProgramStateManager &statemgr, std::unique_ptr<ConstraintManager> CreateZ3ConstraintManager(ProgramStateManager &statemgr, SubEngine *subengine); -} // end GR namespace +} // namespace ento +} // namespace clang -} // end clang namespace - -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CONSTRAINTMANAGER_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 7472a7147fca6..5755818cd9716 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -1,4 +1,4 @@ -//==- CoreEngine.h - Path-Sensitive Dataflow Engine ----------------*- C++ -*-// +//===- CoreEngine.h - Path-Sensitive Dataflow Engine ------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -15,21 +15,33 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H -#include "clang/AST/Expr.h" +#include "clang/AST/Stmt.h" #include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include <cassert> #include <memory> +#include <utility> +#include <vector> namespace clang { -class ProgramPointTag; - +class AnalyzerOptions; +class CXXBindTemporaryExpr; +class Expr; +class LabelDecl; + namespace ento { -class NodeBuilder; +class FunctionSummariesTy; +class SubEngine; //===----------------------------------------------------------------------===// /// CoreEngine - Implements the core logic of the graph-reachability @@ -41,23 +53,23 @@ class NodeBuilder; /// at the statement and block-level. The analyses themselves must implement /// any transfer function logic and the sub-expression level (if any). class CoreEngine { - friend struct NodeBuilderContext; - friend class NodeBuilder; - friend class ExprEngine; friend class CommonNodeBuilder; + friend class EndOfFunctionNodeBuilder; + friend class ExprEngine; friend class IndirectGotoNodeBuilder; + friend class NodeBuilder; + friend struct NodeBuilderContext; friend class SwitchNodeBuilder; - friend class EndOfFunctionNodeBuilder; + public: - typedef std::vector<std::pair<BlockEdge, const ExplodedNode*> > - BlocksExhausted; + using BlocksExhausted = + std::vector<std::pair<BlockEdge, const ExplodedNode *>>; - typedef std::vector<std::pair<const CFGBlock*, const ExplodedNode*> > - BlocksAborted; + using BlocksAborted = + std::vector<std::pair<const CFGBlock *, const ExplodedNode *>>; private: - - SubEngine& SubEng; + SubEngine &SubEng; /// G - The simulation graph. Each node is a (location,state) pair. mutable ExplodedGraph G; @@ -106,17 +118,17 @@ private: ExplodedNode *Pred); private: - CoreEngine(const CoreEngine &) = delete; - void operator=(const CoreEngine &) = delete; - ExplodedNode *generateCallExitBeginNode(ExplodedNode *N, const ReturnStmt *RS); public: /// Construct a CoreEngine object to analyze the provided CFG. - CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS) - : SubEng(subengine), WList(WorkList::makeDFS()), - BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} + CoreEngine(SubEngine &subengine, + FunctionSummariesTy *FS, + AnalyzerOptions &Opts); + + CoreEngine(const CoreEngine &) = delete; + CoreEngine &operator=(const CoreEngine &) = delete; /// getGraph - Returns the exploded graph. ExplodedGraph &getGraph() { return G; } @@ -125,6 +137,7 @@ public: /// steps. Returns true if there is still simulation state on the worklist. bool ExecuteWorkList(const LocationContext *L, unsigned Steps, ProgramStateRef InitState); + /// Returns true if there is still simulation state on the worklist. bool ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, @@ -154,28 +167,31 @@ public: BlocksExhausted::const_iterator blocks_exhausted_begin() const { return blocksExhausted.begin(); } + BlocksExhausted::const_iterator blocks_exhausted_end() const { return blocksExhausted.end(); } + BlocksAborted::const_iterator blocks_aborted_begin() const { return blocksAborted.begin(); } + BlocksAborted::const_iterator blocks_aborted_end() const { return blocksAborted.end(); } - /// \brief Enqueue the given set of nodes onto the work list. + /// Enqueue the given set of nodes onto the work list. void enqueue(ExplodedNodeSet &Set); - /// \brief Enqueue nodes that were created as a result of processing + /// Enqueue nodes that were created as a result of processing /// a statement onto the work list. void enqueue(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx); - /// \brief enqueue the nodes corresponding to the end of function onto the + /// enqueue the nodes corresponding to the end of function onto the /// end of path / work list. void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS); - /// \brief Enqueue a single node created as a result of statement processing. + /// Enqueue a single node created as a result of statement processing. void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx); }; @@ -184,23 +200,24 @@ struct NodeBuilderContext { const CoreEngine &Eng; const CFGBlock *Block; const LocationContext *LC; + NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N) - : Eng(E), Block(B), LC(N->getLocationContext()) { assert(B); } + : Eng(E), Block(B), LC(N->getLocationContext()) { assert(B); } - /// \brief Return the CFGBlock associated with this builder. + /// Return the CFGBlock associated with this builder. const CFGBlock *getBlock() const { return Block; } - /// \brief Returns the number of times the current basic block has been + /// Returns the number of times the current basic block has been /// visited on the exploded graph path. unsigned blockCount() const { return Eng.WList->getBlockCounter().getNumVisited( - LC->getCurrentStackFrame(), + LC->getStackFrame(), Block->getBlockID()); } }; /// \class NodeBuilder -/// \brief This is the simplest builder which generates nodes in the +/// This is the simplest builder which generates nodes in the /// ExplodedGraph. /// /// The main benefit of the builder is that it automatically tracks the @@ -210,29 +227,29 @@ struct NodeBuilderContext { /// constructed nodes) but did not have any outgoing transitions added. class NodeBuilder { virtual void anchor(); + protected: const NodeBuilderContext &C; /// Specifies if the builder results have been finalized. For example, if it /// is set to false, autotransitions are yet to be generated. bool Finalized; - bool HasGeneratedNodes; - /// \brief The frontier set - a set of nodes which need to be propagated after + + bool HasGeneratedNodes = false; + + /// The frontier set - a set of nodes which need to be propagated after /// the builder dies. ExplodedNodeSet &Frontier; - /// Checkes if the results are ready. + /// Checks if the results are ready. virtual bool checkResults() { - if (!Finalized) - return false; - return true; + return Finalized; } bool hasNoSinksInFrontier() { - for (iterator I = Frontier.begin(), E = Frontier.end(); I != E; ++I) { - if ((*I)->isSink()) + for (const auto I : Frontier) + if (I->isSink()) return false; - } return true; } @@ -247,27 +264,27 @@ protected: public: NodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, const NodeBuilderContext &Ctx, bool F = true) - : C(Ctx), Finalized(F), HasGeneratedNodes(false), Frontier(DstSet) { + : C(Ctx), Finalized(F), Frontier(DstSet) { Frontier.Add(SrcNode); } NodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, const NodeBuilderContext &Ctx, bool F = true) - : C(Ctx), Finalized(F), HasGeneratedNodes(false), Frontier(DstSet) { + : C(Ctx), Finalized(F), Frontier(DstSet) { Frontier.insert(SrcSet); assert(hasNoSinksInFrontier()); } - virtual ~NodeBuilder() {} + virtual ~NodeBuilder() = default; - /// \brief Generates a node in the ExplodedGraph. + /// Generates a node in the ExplodedGraph. ExplodedNode *generateNode(const ProgramPoint &PP, ProgramStateRef State, ExplodedNode *Pred) { return generateNodeImpl(PP, State, Pred, false); } - /// \brief Generates a sink in the ExplodedGraph. + /// Generates a sink in the ExplodedGraph. /// /// When a node is marked as sink, the exploration from the node is stopped - /// the node becomes the last node on the path and certain kinds of bugs are @@ -284,14 +301,16 @@ public: return Frontier; } - typedef ExplodedNodeSet::iterator iterator; - /// \brief Iterators through the results frontier. - inline iterator begin() { + using iterator = ExplodedNodeSet::iterator; + + /// Iterators through the results frontier. + iterator begin() { finalizeResults(); assert(checkResults()); return Frontier.begin(); } - inline iterator end() { + + iterator end() { finalizeResults(); return Frontier.end(); } @@ -300,18 +319,20 @@ public: bool hasGeneratedNodes() { return HasGeneratedNodes; } void takeNodes(const ExplodedNodeSet &S) { - for (ExplodedNodeSet::iterator I = S.begin(), E = S.end(); I != E; ++I ) - Frontier.erase(*I); + for (const auto I : S) + Frontier.erase(I); } + void takeNodes(ExplodedNode *N) { Frontier.erase(N); } void addNodes(const ExplodedNodeSet &S) { Frontier.insert(S); } void addNodes(ExplodedNode *N) { Frontier.Add(N); } }; /// \class NodeBuilderWithSinks -/// \brief This node builder keeps track of the generated sink nodes. +/// This node builder keeps track of the generated sink nodes. class NodeBuilderWithSinks: public NodeBuilder { void anchor() override; + protected: SmallVector<ExplodedNode*, 2> sinksGenerated; ProgramPoint &Location; @@ -319,7 +340,7 @@ protected: public: NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet, const NodeBuilderContext &Ctx, ProgramPoint &L) - : NodeBuilder(Pred, DstSet, Ctx), Location(L) {} + : NodeBuilder(Pred, DstSet, Ctx), Location(L) {} ExplodedNode *generateNode(ProgramStateRef State, ExplodedNode *Pred, @@ -343,20 +364,20 @@ public: }; /// \class StmtNodeBuilder -/// \brief This builder class is useful for generating nodes that resulted from +/// This builder class is useful for generating nodes that resulted from /// visiting a statement. The main difference from its parent NodeBuilder is /// that it creates a statement specific ProgramPoint. class StmtNodeBuilder: public NodeBuilder { NodeBuilder *EnclosingBldr; -public: - /// \brief Constructs a StmtNodeBuilder. If the builder is going to process +public: + /// Constructs a StmtNodeBuilder. If the builder is going to process /// nodes currently owned by another builder(with larger scope), use /// Enclosing builder to transfer ownership. StmtNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, const NodeBuilderContext &Ctx, NodeBuilder *Enclosing = nullptr) - : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) { + : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) { if (EnclosingBldr) EnclosingBldr->takeNodes(SrcNode); } @@ -364,11 +385,10 @@ public: StmtNodeBuilder(ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, const NodeBuilderContext &Ctx, NodeBuilder *Enclosing = nullptr) - : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) { + : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) { if (EnclosingBldr) - for (ExplodedNodeSet::iterator I = SrcSet.begin(), - E = SrcSet.end(); I != E; ++I ) - EnclosingBldr->takeNodes(*I); + for (const auto I : SrcSet) + EnclosingBldr->takeNodes(I); } ~StmtNodeBuilder() override; @@ -397,22 +417,23 @@ public: } }; -/// \brief BranchNodeBuilder is responsible for constructing the nodes +/// BranchNodeBuilder is responsible for constructing the nodes /// corresponding to the two branches of the if statement - true and false. class BranchNodeBuilder: public NodeBuilder { - void anchor() override; const CFGBlock *DstT; const CFGBlock *DstF; bool InFeasibleTrue; bool InFeasibleFalse; + void anchor() override; + public: BranchNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, const NodeBuilderContext &C, const CFGBlock *dstT, const CFGBlock *dstF) - : NodeBuilder(SrcNode, DstSet, C), DstT(dstT), DstF(dstF), - InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { + : NodeBuilder(SrcNode, DstSet, C), DstT(dstT), DstF(dstF), + InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { // The branch node builder does not generate autotransitions. // If there are no successors it means that both branches are infeasible. takeNodes(SrcNode); @@ -421,8 +442,8 @@ public: BranchNodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, const NodeBuilderContext &C, const CFGBlock *dstT, const CFGBlock *dstF) - : NodeBuilder(SrcSet, DstSet, C), DstT(dstT), DstF(dstF), - InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { + : NodeBuilder(SrcSet, DstSet, C), DstT(dstT), DstF(dstF), + InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { takeNodes(SrcSet); } @@ -455,15 +476,16 @@ class IndirectGotoNodeBuilder { public: IndirectGotoNodeBuilder(ExplodedNode *pred, const CFGBlock *src, const Expr *e, const CFGBlock *dispatch, CoreEngine* eng) - : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {} + : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {} class iterator { + friend class IndirectGotoNodeBuilder; + CFGBlock::const_succ_iterator I; - friend class IndirectGotoNodeBuilder; iterator(CFGBlock::const_succ_iterator i) : I(i) {} - public: + public: iterator &operator++() { ++I; return *this; } bool operator!=(const iterator &X) const { return I != X.I; } @@ -501,12 +523,13 @@ class SwitchNodeBuilder { public: SwitchNodeBuilder(ExplodedNode *pred, const CFGBlock *src, const Expr *condition, CoreEngine* eng) - : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} + : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} class iterator { + friend class SwitchNodeBuilder; + CFGBlock::const_succ_reverse_iterator I; - friend class SwitchNodeBuilder; iterator(CFGBlock::const_succ_reverse_iterator i) : I(i) {} public: @@ -545,7 +568,8 @@ public: } }; -} // end ento namespace -} // end clang namespace +} // namespace ento + +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h index e13c6410c7be9..092e23ce73c8b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h @@ -14,7 +14,7 @@ namespace clang { namespace ento { -/// \brief Stores the currently inferred strictest bound on the runtime type +/// Stores the currently inferred strictest bound on the runtime type /// of a region in a given state along the analysis path. class DynamicTypeInfo { private: @@ -27,13 +27,13 @@ public: DynamicTypeInfo(QualType WithType, bool CanBeSub = true) : T(WithType), CanBeASubClass(CanBeSub) {} - /// \brief Return false if no dynamic type info is available. + /// Return false if no dynamic type info is available. bool isValid() const { return !T.isNull(); } - /// \brief Returns the currently inferred upper bound on the runtime type. + /// Returns the currently inferred upper bound on the runtime type. QualType getType() const { return T; } - /// \brief Returns false if the type information is precise (the type T is + /// 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; } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h index 555191d997099..2f8ead0746cac 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h @@ -1,4 +1,4 @@ -//== DynamicTypeMap.h - Dynamic type map ----------------------- -*- C++ -*--=// +//===- DynamicTypeMap.h - Dynamic type map ----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -13,19 +13,26 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.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 {}; -typedef llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo> - DynamicTypeMapImpl; + +using DynamicTypeMapImpl = + llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo>; + template <> struct ProgramStateTrait<DynamicTypeMap> : public ProgramStatePartialTrait<DynamicTypeMapImpl> { @@ -35,15 +42,15 @@ struct ProgramStateTrait<DynamicTypeMap> } }; -/// \brief Get dynamic type information for a region. +/// Get dynamic type information for a region. DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg); -/// \brief Set dynamic type information of the region; return the new state. +/// Set dynamic type information of the region; return the new state. ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, DynamicTypeInfo NewTy); -/// \brief Set dynamic type information of the region; return the new state. +/// Set dynamic type information of the region; return the new state. inline ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, QualType NewTy, bool CanBeSubClassed = true) { @@ -51,7 +58,10 @@ inline ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, DynamicTypeInfo(NewTy, CanBeSubClassed)); } -} // ento -} // clang +void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out, + const char *NL, const char *Sep); + +} // namespace ento +} // namespace clang #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h index c63ed4a013aa6..d49aa81bc9585 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h @@ -1,4 +1,4 @@ -//== Environment.h - Map from Stmt* to Locations/Values ---------*- C++ -*--==// +//===- Environment.h - Map from Stmt* to Locations/Values -------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -15,16 +15,17 @@ #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENVIRONMENT_H #include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/ImmutableMap.h" +#include <utility> namespace clang { -class LiveVariables; +class Stmt; namespace ento { -class EnvironmentManager; class SValBuilder; class SymbolReaper; @@ -32,7 +33,7 @@ class SymbolReaper; /// This allows the environment to manage context-sensitive bindings, /// which is essentially for modeling recursive function analysis, among /// other things. -class EnvironmentEntry : public std::pair<const Stmt*, +class EnvironmentEntry : public std::pair<const Stmt *, const StackFrameContext *> { public: EnvironmentEntry(const Stmt *s, const LocationContext *L); @@ -57,19 +58,17 @@ class Environment { private: friend class EnvironmentManager; - // Type definitions. - typedef llvm::ImmutableMap<EnvironmentEntry, SVal> BindingsTy; + using BindingsTy = llvm::ImmutableMap<EnvironmentEntry, SVal>; - // Data. BindingsTy ExprBindings; - Environment(BindingsTy eb) - : ExprBindings(eb) {} + Environment(BindingsTy eb) : ExprBindings(eb) {} SVal lookupExpr(const EnvironmentEntry &E) const; public: - typedef BindingsTy::iterator iterator; + using iterator = BindingsTy::iterator; + iterator begin() const { return ExprBindings.begin(); } iterator end() const { return ExprBindings.end(); } @@ -92,21 +91,19 @@ public: bool operator==(const Environment& RHS) const { return ExprBindings == RHS.ExprBindings; } - - void print(raw_ostream &Out, const char *NL, const char *Sep) const; - -private: - void printAux(raw_ostream &Out, bool printLocations, - const char *NL, const char *Sep) const; + + void print(raw_ostream &Out, const char *NL, const char *Sep, + const LocationContext *WithLC = nullptr) const; }; class EnvironmentManager { private: - typedef Environment::BindingsTy::Factory FactoryTy; + using FactoryTy = Environment::BindingsTy::Factory; + FactoryTy F; public: - EnvironmentManager(llvm::BumpPtrAllocator& Allocator) : F(Allocator) {} + EnvironmentManager(llvm::BumpPtrAllocator &Allocator) : F(Allocator) {} Environment getInitialEnvironment() { return Environment(F.getEmptyMap()); @@ -121,8 +118,8 @@ public: ProgramStateRef state); }; -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENVIRONMENT_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index dcea5e461d27c..c12198db6598d 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -1,4 +1,4 @@ -//=-- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -*- C++ -*-------==// +//===- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -19,17 +19,25 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H -#include "clang/AST/Decl.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include <cassert> +#include <cstdint> #include <memory> #include <utility> #include <vector> @@ -37,6 +45,10 @@ namespace clang { class CFG; +class Decl; +class Expr; +class ParentMap; +class Stmt; namespace ento { @@ -52,13 +64,13 @@ class ExplodedGraph; // successors to it at any time after creating it. class ExplodedNode : public llvm::FoldingSetNode { - friend class ExplodedGraph; - friend class CoreEngine; - friend class NodeBuilder; friend class BranchNodeBuilder; + friend class CoreEngine; + friend class EndOfFunctionNodeBuilder; + friend class ExplodedGraph; friend class IndirectGotoNodeBuilder; + friend class NodeBuilder; friend class SwitchNodeBuilder; - friend class EndOfFunctionNodeBuilder; /// Efficiently stores a list of ExplodedNodes, or an optional flag. /// @@ -135,7 +147,7 @@ public: } const StackFrameContext *getStackFrame() const { - return getLocationContext()->getCurrentStackFrame(); + return getLocation().getStackFrame(); } const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); } @@ -156,6 +168,11 @@ public: return Location.getAs<T>(); } + /// Get the value of an arbitrary expression at this node. + SVal getSVal(const Stmt *S) const { + return getState()->getSVal(S, getLocationContext()); + } + static void Profile(llvm::FoldingSetNodeID &ID, const ProgramPoint &Loc, const ProgramStateRef &state, @@ -198,10 +215,10 @@ public: } // Iterators over successor and predecessor vertices. - typedef ExplodedNode* const * succ_iterator; - typedef const ExplodedNode* const * const_succ_iterator; - typedef ExplodedNode* const * pred_iterator; - typedef const ExplodedNode* const * const_pred_iterator; + using succ_iterator = ExplodedNode * const *; + using const_succ_iterator = const ExplodedNode * const *; + using pred_iterator = ExplodedNode * const *; + using const_pred_iterator = const ExplodedNode * const *; pred_iterator pred_begin() { return Preds.begin(); } pred_iterator pred_end() { return Preds.end(); } @@ -226,10 +243,10 @@ public: // For debugging. public: - class Auditor { public: virtual ~Auditor(); + virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst) = 0; }; @@ -240,15 +257,15 @@ private: void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } }; -typedef llvm::DenseMap<const ExplodedNode *, const ExplodedNode *> - InterExplodedGraphMap; +using InterExplodedGraphMap = + llvm::DenseMap<const ExplodedNode *, const ExplodedNode *>; class ExplodedGraph { protected: friend class CoreEngine; // Type definitions. - typedef std::vector<ExplodedNode *> NodeVector; + using NodeVector = std::vector<ExplodedNode *>; /// The roots of the simulation graph. Usually there will be only /// one, but clients are free to establish multiple subgraphs within a single @@ -268,7 +285,7 @@ protected: BumpVectorContext BVC; /// NumNodes - The number of nodes in the graph. - unsigned NumNodes; + unsigned NumNodes = 0; /// A list of recently allocated nodes that can potentially be recycled. NodeVector ChangedNodes; @@ -279,14 +296,16 @@ protected: /// Determines how often nodes are reclaimed. /// /// If this is 0, nodes will never be reclaimed. - unsigned ReclaimNodeInterval; + unsigned ReclaimNodeInterval = 0; /// Counter to determine when to reclaim nodes. unsigned ReclaimCounter; public: + ExplodedGraph(); + ~ExplodedGraph(); - /// \brief Retrieve the node associated with a (Location,State) pair, + /// Retrieve the node associated with a (Location,State) pair, /// where the 'Location' is a ProgramPoint in the CFG. If no node for /// this pair exists, it is created. IsNew is set to true if /// the node was freshly created. @@ -294,7 +313,7 @@ public: bool IsSink = false, bool* IsNew = nullptr); - /// \brief Create a node for a (Location, State) pair, + /// Create a node for a (Location, State) pair, /// but don't store it for deduplication later. This /// is useful when copying an already completed /// ExplodedGraph for further processing. @@ -318,10 +337,6 @@ public: return V; } - ExplodedGraph(); - - ~ExplodedGraph(); - unsigned num_roots() const { return Roots.size(); } unsigned num_eops() const { return EndNodes.size(); } @@ -331,14 +346,14 @@ public: void reserve(unsigned NodeCount) { Nodes.reserve(NodeCount); } // Iterators. - typedef ExplodedNode NodeTy; - typedef llvm::FoldingSet<ExplodedNode> AllNodesTy; - typedef NodeVector::iterator roots_iterator; - typedef NodeVector::const_iterator const_roots_iterator; - typedef NodeVector::iterator eop_iterator; - typedef NodeVector::const_iterator const_eop_iterator; - typedef AllNodesTy::iterator node_iterator; - typedef AllNodesTy::const_iterator const_node_iterator; + using NodeTy = ExplodedNode; + using AllNodesTy = llvm::FoldingSet<ExplodedNode>; + using roots_iterator = NodeVector::iterator; + using const_roots_iterator = NodeVector::const_iterator; + using eop_iterator = NodeVector::iterator; + using const_eop_iterator = NodeVector::const_iterator; + using node_iterator = AllNodesTy::iterator; + using const_node_iterator = AllNodesTy::const_iterator; node_iterator nodes_begin() { return Nodes.begin(); } @@ -367,7 +382,7 @@ public: llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); } BumpVectorContext &getNodeAllocator() { return BVC; } - typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> NodeMap; + using NodeMap = llvm::DenseMap<const ExplodedNode *, ExplodedNode *>; /// Creates a trimmed version of the graph that only contains paths leading /// to the given nodes. @@ -394,7 +409,7 @@ public: /// was called. void reclaimRecentlyAllocatedNodes(); - /// \brief Returns true if nodes for the given expression kind are always + /// Returns true if nodes for the given expression kind are always /// kept around. static bool isInterestingLValueExpr(const Expr *Ex); @@ -404,29 +419,30 @@ private: }; class ExplodedNodeSet { - typedef llvm::SmallSetVector<ExplodedNode*, 4> ImplTy; + using ImplTy = llvm::SmallSetVector<ExplodedNode *, 4>; ImplTy Impl; public: ExplodedNodeSet(ExplodedNode *N) { - assert (N && !static_cast<ExplodedNode*>(N)->isSink()); + assert(N && !static_cast<ExplodedNode*>(N)->isSink()); Impl.insert(N); } - ExplodedNodeSet() {} + ExplodedNodeSet() = default; - inline void Add(ExplodedNode *N) { + void Add(ExplodedNode *N) { if (N && !static_cast<ExplodedNode*>(N)->isSink()) Impl.insert(N); } - typedef ImplTy::iterator iterator; - typedef ImplTy::const_iterator const_iterator; + using iterator = ImplTy::iterator; + using const_iterator = ImplTy::const_iterator; unsigned size() const { return Impl.size(); } bool empty() const { return Impl.empty(); } bool erase(ExplodedNode *N) { return Impl.remove(N); } void clear() { Impl.clear(); } + void insert(const ExplodedNodeSet &S) { assert(&S != this); if (empty()) @@ -435,24 +451,25 @@ public: Impl.insert(S.begin(), S.end()); } - inline iterator begin() { return Impl.begin(); } - inline iterator end() { return Impl.end(); } + iterator begin() { return Impl.begin(); } + iterator end() { return Impl.end(); } - inline const_iterator begin() const { return Impl.begin(); } - inline const_iterator end() const { return Impl.end(); } + const_iterator begin() const { return Impl.begin(); } + const_iterator end() const { return Impl.end(); } }; -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang // GraphTraits namespace llvm { + template<> struct GraphTraits<clang::ento::ExplodedNode*> { - typedef clang::ento::ExplodedNode *NodeRef; - typedef clang::ento::ExplodedNode::succ_iterator ChildIteratorType; - typedef llvm::df_iterator<NodeRef> nodes_iterator; + using NodeRef = clang::ento::ExplodedNode *; + using ChildIteratorType = clang::ento::ExplodedNode::succ_iterator; + using nodes_iterator = llvm::df_iterator<NodeRef>; static NodeRef getEntryNode(NodeRef N) { return N; } @@ -466,9 +483,9 @@ namespace llvm { }; template<> struct GraphTraits<const clang::ento::ExplodedNode*> { - typedef const clang::ento::ExplodedNode *NodeRef; - typedef clang::ento::ExplodedNode::const_succ_iterator ChildIteratorType; - typedef llvm::df_iterator<NodeRef> nodes_iterator; + using NodeRef = const clang::ento::ExplodedNode *; + using ChildIteratorType = clang::ento::ExplodedNode::const_succ_iterator; + using nodes_iterator = llvm::df_iterator<NodeRef>; static NodeRef getEntryNode(NodeRef N) { return N; } @@ -481,6 +498,6 @@ namespace llvm { static nodes_iterator nodes_end(NodeRef N) { return df_end(N); } }; -} // end llvm namespace +} // namespace llvm -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 712cd6361e118..25849e94c8ff1 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -1,4 +1,4 @@ -//===-- ExprEngine.h - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-=// +//===- ExprEngine.h - Path-Sensitive Expression-Level Dataflow --*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -18,32 +18,68 @@ #include "clang/AST/Expr.h" #include "clang/AST/Type.h" +#include "clang/Analysis/CFG.h" #include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.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/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/ArrayRef.h" +#include <cassert> +#include <utility> namespace clang { class AnalysisDeclContextManager; +class AnalyzerOptions; +class ASTContext; +class ConstructionContext; +class CXXBindTemporaryExpr; class CXXCatchStmt; class CXXConstructExpr; class CXXDeleteExpr; class CXXNewExpr; -class CXXTemporaryObjectExpr; class CXXThisExpr; +class Decl; +class DeclStmt; +class GCCAsmStmt; +class LambdaExpr; +class LocationContext; class MaterializeTemporaryExpr; +class MSAsmStmt; +class NamedDecl; class ObjCAtSynchronizedStmt; class ObjCForCollectionStmt; +class ObjCIvarRefExpr; +class ObjCMessageExpr; +class ReturnStmt; +class Stmt; + +namespace cross_tu { + +class CrossTranslationUnitContext; + +} // namespace cross_tu namespace ento { -class AnalysisManager; +class BasicValueFactory; class CallEvent; -class CXXConstructorCall; +class CheckerManager; +class ConstraintManager; +class CXXTempObjectRegion; +class MemRegion; +class RegionAndSymbolInvalidationTraits; +class SymbolManager; class ExprEngine : public SubEngine { public: @@ -51,11 +87,35 @@ public: enum InliningModes { /// Follow the default settings for inlining callees. Inline_Regular = 0, + /// Do minimal inlining of callees. Inline_Minimal = 0x1 }; + /// Hints for figuring out of a call should be inlined during evalCall(). + struct EvalCallOptions { + /// This call is a constructor or a destructor for which we do not currently + /// compute the this-region correctly. + bool IsCtorOrDtorWithImproperlyModeledTargetRegion = false; + + /// This call is a constructor or a destructor for a single element within + /// an array, a part of array construction or destruction. + bool IsArrayCtorOrDtor = false; + + /// This call is a constructor or a destructor of a temporary value. + bool IsTemporaryCtorOrDtor = false; + + /// This call is a constructor for a temporary that is lifetime-extended + /// by binding it to a reference-type field within an aggregate, + /// for example 'A { const C &c; }; A a = { C() };' + bool IsTemporaryLifetimeExtendedViaAggregate = false; + + EvalCallOptions() {} + }; + private: + cross_tu::CrossTranslationUnitContext &CTU; + AnalysisManager &AMgr; AnalysisDeclContextManager &AnalysisDeclContexts; @@ -63,19 +123,19 @@ private: CoreEngine Engine; /// G - the simulation graph. - ExplodedGraph& G; + ExplodedGraph &G; /// StateMgr - Object that manages the data for all created states. ProgramStateManager StateMgr; /// SymMgr - Object that manages the symbol information. - SymbolManager& SymMgr; + SymbolManager &SymMgr; /// svalBuilder - SValBuilder object that creates SVals from expressions. SValBuilder &svalBuilder; - unsigned int currStmtIdx; - const NodeBuilderContext *currBldrCtx; + unsigned int currStmtIdx = 0; + const NodeBuilderContext *currBldrCtx = nullptr; /// Helper object to determine if an Objective-C message expression /// implicitly never returns. @@ -97,10 +157,9 @@ private: InliningModes HowToInline; public: - ExprEngine(AnalysisManager &mgr, bool gcEnabled, - SetOfConstDecls *VisitedCalleesIn, - FunctionSummariesTy *FS, - InliningModes HowToInlineIn); + ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, AnalysisManager &mgr, + bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, + FunctionSummariesTy *FS, InliningModes HowToInlineIn); ~ExprEngine() override; @@ -130,7 +189,12 @@ public: SValBuilder &getSValBuilder() { return svalBuilder; } - BugReporter& getBugReporter() { return BR; } + BugReporter &getBugReporter() { return BR; } + + cross_tu::CrossTranslationUnitContext * + getCrossTranslationUnitContext() override { + return &CTU; + } const NodeBuilderContext &getBuilderContext() { assert(currBldrCtx); @@ -150,16 +214,16 @@ public: /// Visualize a trimmed ExplodedGraph that only contains paths to the given /// nodes. - void ViewGraph(ArrayRef<const ExplodedNode*> Nodes); + void ViewGraph(ArrayRef<const ExplodedNode *> Nodes); /// getInitialState - Return the initial state used for the root vertex /// in the ExplodedGraph. ProgramStateRef getInitialState(const LocationContext *InitLoc) override; - ExplodedGraph& getGraph() { return G; } - const ExplodedGraph& getGraph() const { return G; } + ExplodedGraph &getGraph() { return G; } + const ExplodedGraph &getGraph() const { return G; } - /// \brief Run the analyzer's garbage collection - remove dead symbols and + /// Run the analyzer's garbage collection - remove dead symbols and /// bindings from the state. /// /// Checkers can participate in this process with two callbacks: @@ -194,7 +258,7 @@ public: void processCFGElement(const CFGElement E, ExplodedNode *Pred, unsigned StmtIdx, NodeBuilderContext *Ctx) override; - void ProcessStmt(const CFGStmt S, ExplodedNode *Pred); + void ProcessStmt(const Stmt *S, ExplodedNode *Pred); void ProcessLoopExit(const Stmt* S, ExplodedNode *Pred); @@ -299,25 +363,26 @@ public: const CallEvent *Call) override; /// printState - Called by ProgramStateManager to print checker-specific data. - void printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep) override; + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep, + const LocationContext *LCtx = nullptr) override; - ProgramStateManager& getStateManager() override { return StateMgr; } + ProgramStateManager &getStateManager() override { return StateMgr; } - StoreManager& getStoreManager() { return StateMgr.getStoreManager(); } + StoreManager &getStoreManager() { return StateMgr.getStoreManager(); } - ConstraintManager& getConstraintManager() { + ConstraintManager &getConstraintManager() { return StateMgr.getConstraintManager(); } // FIXME: Remove when we migrate over to just using SValBuilder. - BasicValueFactory& getBasicVals() { + BasicValueFactory &getBasicVals() { return StateMgr.getBasicVals(); } // FIXME: Remove when we migrate over to just using ValueManager. - SymbolManager& getSymbolManager() { return SymMgr; } - const SymbolManager& getSymbolManager() const { return SymMgr; } + SymbolManager &getSymbolManager() { return SymMgr; } + const SymbolManager &getSymbolManager() const { return SymMgr; } // Functions for external checking of whether we have unfinished work bool wasBlocksExhausted() const { return Engine.wasBlocksExhausted(); } @@ -363,7 +428,7 @@ public: /// VisitCast - Transfer function logic for all casts (implicit and explicit). void VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst); + ExplodedNodeSet &Dst); /// VisitCompoundLiteralExpr - Transfer function logic for compound literals. void VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, @@ -390,9 +455,9 @@ public: /// VisitMemberExpr - Transfer function for member expressions. void VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, - ExplodedNodeSet &Dst); + ExplodedNodeSet &Dst); - /// VisitMemberExpr - Transfer function for builtin atomic expressions + /// VisitAtomicExpr - Transfer function for builtin atomic expressions void VisitAtomicExpr(const AtomicExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); @@ -422,7 +487,7 @@ public: /// VisitUnaryExprOrTypeTraitExpr - Transfer function for sizeof. void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, - ExplodedNode *Pred, ExplodedNodeSet &Dst); + ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitUnaryOperator - Transfer function logic for unary operators. void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode *Pred, @@ -448,7 +513,8 @@ public: void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, - ExplodedNode *Pred, ExplodedNodeSet &Dst); + ExplodedNode *Pred, ExplodedNodeSet &Dst, + const EvalCallOptions &Options); void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, ExplodedNode *Pred, @@ -471,7 +537,7 @@ public: void evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, const Expr *Ex); - std::pair<const ProgramPointTag *, const ProgramPointTag*> + static std::pair<const ProgramPointTag *, const ProgramPointTag *> geteagerlyAssumeBinOpBifurcationTags(); SVal evalMinus(SVal X) { @@ -499,7 +565,6 @@ public: StmtNodeBuilder &Bldr); public: - SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc L, NonLoc R, QualType T) { return svalBuilder.evalBinOpNN(state, op, L, R, T); @@ -539,6 +604,11 @@ protected: const CallEvent *Call, RegionAndSymbolInvalidationTraits &ITraits) override; + /// A simple wrapper when you only need to notify checkers of pointer-escape + /// of a single value. + ProgramStateRef escapeValue(ProgramStateRef State, SVal V, + PointerEscapeKind K) const; + public: // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. @@ -561,7 +631,13 @@ public: ExplodedNode *Pred, ProgramStateRef St, SVal TargetLV, SVal Val, const ProgramPointTag *tag = nullptr); - /// \brief Create a new state in which the call return value is binded to the + /// Return the CFG element corresponding to the worklist element + /// that is currently being processed by ExprEngine. + CFGElement getCurrentCFGElement() { + return (*currBldrCtx->getBlock())[currStmtIdx]; + } + + /// Create a new state in which the call return value is binded to the /// call origin expression. ProgramStateRef bindReturnValue(const CallEvent &Call, const LocationContext *LCtx, @@ -572,9 +648,11 @@ public: void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call); - /// \brief Default implementation of call evaluation. + /// Default implementation of call evaluation. void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, - const CallEvent &Call); + const CallEvent &Call, + const EvalCallOptions &CallOpts = {}); + private: void evalLoadCommon(ExplodedNodeSet &Dst, const Expr *NodeEx, /* Eventually will be a CFGStmt */ @@ -598,19 +676,33 @@ private: void examineStackFrames(const Decl *D, const LocationContext *LCtx, bool &IsRecursive, unsigned &StackDepth); + enum CallInlinePolicy { + CIP_Allowed, + CIP_DisallowedOnce, + CIP_DisallowedAlways + }; + + /// See if a particular call should be inlined, by only looking + /// at the call event and the current state of analysis. + CallInlinePolicy mayInlineCallKind(const CallEvent &Call, + const ExplodedNode *Pred, + AnalyzerOptions &Opts, + const EvalCallOptions &CallOpts); + /// Checks our policies and decides weither the given call should be inlined. bool shouldInlineCall(const CallEvent &Call, const Decl *D, - const ExplodedNode *Pred); + const ExplodedNode *Pred, + const EvalCallOptions &CallOpts = {}); bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); - /// \brief Conservatively evaluate call by invalidating regions and binding + /// Conservatively evaluate call by invalidating regions and binding /// a conjured return value. void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); - /// \brief Either inline or process the call conservatively (or both), based + /// Either inline or process the call conservatively (or both), based /// on DynamicDispatchBifurcation data. void BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, @@ -634,6 +726,17 @@ private: const Expr *InitWithAdjustments, const Expr *Result = nullptr); + /// Returns a region representing the first element of a (possibly + /// multi-dimensional) array, for the purposes of element construction or + /// destruction. + /// + /// On return, \p Ty will be set to the base type of the array. + /// + /// If the type is not an array type at all, the original value is returned. + /// Otherwise the "IsArray" flag is set. + static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, + QualType &Ty, bool &IsArray); + /// For a DeclStmt or CXXInitCtorInitializer, walk backward in the current CFG /// block to find the constructor expression that directly constructed into /// the storage for this statement. Returns null if the constructor for this @@ -641,20 +744,69 @@ private: /// constructing into an existing region. const CXXConstructExpr *findDirectConstructorForCurrentCFGElement(); - /// For a CXXConstructExpr, walk forward in the current CFG block to find the - /// CFGElement for the DeclStmt or CXXInitCtorInitializer for which is - /// directly constructed by this constructor. Returns None if the current - /// constructor expression did not directly construct into an existing - /// region. - Optional<CFGElement> findElementDirectlyInitializedByCurrentConstructor(); - - /// For a given constructor, look forward in the current CFG block to - /// determine the region into which an object will be constructed by \p CE. - /// Returns either a field or local variable region if the object will be - /// directly constructed in an existing region or a temporary object region - /// if not. - const MemRegion *getRegionForConstructedObject(const CXXConstructExpr *CE, - ExplodedNode *Pred); + /// Update the program state with all the path-sensitive information + /// that's necessary to perform construction of an object with a given + /// syntactic construction context. If the construction context is unavailable + /// or unusable for any reason, a dummy temporary region is returned, and the + /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts. + /// Returns the updated program state and the new object's this-region. + std::pair<ProgramStateRef, SVal> prepareForObjectConstruction( + const Expr *E, ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC, EvalCallOptions &CallOpts); + + /// Store the location of a C++ object corresponding to a statement + /// until the statement is actually encountered. For example, if a DeclStmt + /// has CXXConstructExpr as its initializer, the object would be considered + /// to be "under construction" between CXXConstructExpr and DeclStmt. + /// This allows, among other things, to keep bindings to variable's fields + /// made within the constructor alive until its declaration actually + /// goes into scope. + static ProgramStateRef addObjectUnderConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC, SVal V); + + /// Mark the object sa fully constructed, cleaning up the state trait + /// that tracks objects under construction. + static ProgramStateRef finishObjectConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC); + + /// If the given statement corresponds to an object under construction, + /// being part of its construciton context, retrieve that object's location. + static Optional<SVal> getObjectUnderConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC); + + /// If the given expression corresponds to a temporary that was used for + /// passing into an elidable copy/move constructor and that constructor + /// was actually elided, track that we also need to elide the destructor. + static ProgramStateRef elideDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC); + + /// Stop tracking the destructor that corresponds to an elided constructor. + static ProgramStateRef + cleanupElidedDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC); + + /// Returns true if the given expression corresponds to a temporary that + /// was constructed for passing into an elidable copy/move constructor + /// and that constructor was actually elided. + static bool isDestructorElided(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC); + + /// Check if all objects under construction have been fully constructed + /// for the given context range (including FromLC, not including ToLC). + /// This is useful for assertions. Also checks if elided destructors + /// were cleaned up. + static bool areAllObjectsFullyConstructed(ProgramStateRef State, + const LocationContext *FromLC, + const LocationContext *ToLC); }; /// Traits for storing the call processing policy inside GDM. @@ -668,8 +820,8 @@ struct ProgramStateTrait<ReplayWithoutInlining> : static void *GDMIndex() { static int index = 0; return &index; } }; -} // end ento namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPRENGINE_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h index ce81c98c206b2..b70faa10f0b2c 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h @@ -1,4 +1,4 @@ -//== FunctionSummary.h - Stores summaries of functions. ------------*- C++ -*-// +//===- FunctionSummary.h - Stores summaries of functions. -------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -18,15 +18,18 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallBitVector.h" +#include <cassert> #include <deque> +#include <utility> namespace clang { - namespace ento { -typedef std::deque<Decl*> SetOfDecls; -typedef llvm::DenseSet<const Decl*> SetOfConstDecls; + +using SetOfDecls = std::deque<Decl *>; +using SetOfConstDecls = llvm::DenseSet<const Decl *>; class FunctionSummariesTy { class FunctionSummary { @@ -47,13 +50,12 @@ class FunctionSummariesTy { /// The number of times the function has been inlined. unsigned TimesInlined : 32; - FunctionSummary() : - TotalBasicBlocks(0), - InlineChecked(0), - TimesInlined(0) {} + FunctionSummary() + : TotalBasicBlocks(0), InlineChecked(0), MayInline(0), + TimesInlined(0) {} }; - typedef llvm::DenseMap<const Decl *, FunctionSummary> MapTy; + using MapTy = llvm::DenseMap<const Decl *, FunctionSummary>; MapTy Map; public: @@ -62,7 +64,8 @@ public: if (I != Map.end()) return I; - typedef std::pair<const Decl *, FunctionSummary> KVPair; + using KVPair = std::pair<const Decl *, FunctionSummary>; + I = Map.insert(KVPair(D, FunctionSummary())).first; assert(I != Map.end()); return I; @@ -132,9 +135,9 @@ public: unsigned getTotalNumBasicBlocks(); unsigned getTotalNumVisitedBasicBlocks(); - }; -}} // end clang ento namespaces +} // namespace ento +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_FUNCTIONSUMMARY_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h b/include/clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h index 3168733e4258e..f494c5d6dab82 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h @@ -22,7 +22,7 @@ namespace clang { namespace ento { -/// \brief Get the states that result from widening the loop. +/// Get the states that result from widening the loop. /// /// Widen the loop by invalidating anything that might be modified /// by the loop body in any iteration. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 8ab6656230888..f3846eba6b963 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -1,4 +1,4 @@ -//== MemRegion.h - Abstract memory regions for static analysis --*- C++ -*--==// +//==- MemRegion.h - Abstract memory regions for static analysis -*- C++ -*--==// // // The LLVM Compiler Infrastructure // @@ -19,24 +19,39 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" -#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <cstdint> +#include <limits> #include <string> +#include <utility> namespace clang { +class AnalysisDeclContext; +class CXXRecordDecl; +class Decl; class LocationContext; class StackFrameContext; namespace ento { class CodeTextRegion; +class MemRegion; class MemRegionManager; class MemSpaceRegion; class SValBuilder; @@ -46,7 +61,7 @@ class VarRegion; /// Represent a region's offset within the top level base region. class RegionOffset { /// The base region. - const MemRegion *R; + const MemRegion *R = nullptr; /// The bit offset within the base region. Can be negative. int64_t Offset; @@ -54,9 +69,9 @@ class RegionOffset { public: // We're using a const instead of an enumeration due to the size required; // Visual Studio will only create enumerations of size int, not long long. - static const int64_t Symbolic = INT64_MAX; + static const int64_t Symbolic = std::numeric_limits<int64_t>::max(); - RegionOffset() : R(nullptr) {} + RegionOffset() = default; RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {} const MemRegion *getRegion() const { return R; } @@ -86,6 +101,7 @@ public: private: const Kind kind; + mutable Optional<RegionOffset> cachedOffset; protected: MemRegion(Kind k) : kind(k) {} @@ -103,11 +119,12 @@ public: const MemRegion *getBaseRegion() const; /// Check if the region is a subregion of the given region. + /// Each region is a subregion of itself. virtual bool isSubRegionOf(const MemRegion *R) const; const MemRegion *StripCasts(bool StripBaseCasts = true) const; - /// \brief If this is a symbolic region, returns the region. Otherwise, + /// If this is a symbolic region, returns the region. Otherwise, /// goes up the base chain looking for the first symbolic base region. const SymbolicRegion *getSymbolicBase() const; @@ -122,24 +139,24 @@ public: /// Compute the offset within the top level memory object. RegionOffset getAsOffset() const; - /// \brief Get a string representation of a region for debug use. + /// Get a string representation of a region for debug use. std::string getString() const; virtual void dumpToStream(raw_ostream &os) const; void dump() const; - /// \brief Returns true if this region can be printed in a user-friendly way. + /// Returns true if this region can be printed in a user-friendly way. virtual bool canPrintPretty() const; - /// \brief Print the region for use in diagnostics. + /// Print the region for use in diagnostics. virtual void printPretty(raw_ostream &os) const; - /// \brief Returns true if this region's textual representation can be used + /// Returns true if this region's textual representation can be used /// as part of a larger expression. virtual bool canPrintPrettyAsExpr() const; - /// \brief Print the region as expression. + /// Print the region as expression. /// /// When this region represents a subexpression, the method is for printing /// an expression containing it. @@ -151,7 +168,6 @@ public: virtual bool isBoundable() const { return false; } - /// Get descriptive name for memory region. The name is obtained from /// the variable/field declaration retrieved from the memory region. /// Regions that point to an element of an array are returned as: "arr[0]". @@ -162,7 +178,6 @@ public: /// \returns variable name for memory region std::string getDescriptiveName(bool UseQuotes = true) const; - /// Retrieve source range from memory region. The range retrieval /// is based on the decl obtained from the memory region. /// For a VarRegion the range of the base region is returned. @@ -171,7 +186,7 @@ public: /// The client is responsible for checking if the returned range is valid. /// /// \returns source range for declaration retrieved from memory region - clang::SourceRange sourceRange() const; + SourceRange sourceRange() const; }; /// MemSpaceRegion - A memory region that represents a "memory space"; @@ -229,7 +244,7 @@ public: } }; -/// \brief The region of the static variables within the current CodeTextRegion +/// The region of the static variables within the current CodeTextRegion /// scope. /// /// Currently, only the static locals are placed there, so we know that these @@ -240,7 +255,7 @@ class StaticGlobalSpaceRegion : public GlobalsSpaceRegion { const CodeTextRegion *CR; StaticGlobalSpaceRegion(MemRegionManager *mgr, const CodeTextRegion *cr) - : GlobalsSpaceRegion(mgr, StaticGlobalSpaceRegionKind), CR(cr) { + : GlobalsSpaceRegion(mgr, StaticGlobalSpaceRegionKind), CR(cr) { assert(cr); } @@ -256,14 +271,14 @@ public: } }; -/// \brief The region for all the non-static global variables. +/// The region for all the non-static global variables. /// /// This class is further split into subclasses for efficient implementation of /// invalidating a set of related global values as is done in /// RegionStoreManager::invalidateRegions (instead of finding all the dependent /// globals, we invalidate the whole parent region). class NonStaticGlobalSpaceRegion : public GlobalsSpaceRegion { - virtual void anchor() override; + void anchor() override; protected: NonStaticGlobalSpaceRegion(MemRegionManager *mgr, Kind k) @@ -272,7 +287,6 @@ protected: } public: - static bool classof(const MemRegion *R) { Kind k = R->getKind(); return k >= BEGIN_NON_STATIC_GLOBAL_MEMSPACES && @@ -280,16 +294,15 @@ public: } }; -/// \brief The region containing globals which are defined in system/external +/// The region containing globals which are defined in system/external /// headers and are considered modifiable by system calls (ex: errno). class GlobalSystemSpaceRegion : public NonStaticGlobalSpaceRegion { friend class MemRegionManager; GlobalSystemSpaceRegion(MemRegionManager *mgr) - : NonStaticGlobalSpaceRegion(mgr, GlobalSystemSpaceRegionKind) {} + : NonStaticGlobalSpaceRegion(mgr, GlobalSystemSpaceRegionKind) {} public: - void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { @@ -297,7 +310,7 @@ public: } }; -/// \brief The region containing globals which are considered not to be modified +/// The region containing globals which are considered not to be modified /// or point to data which could be modified as a result of a function call /// (system or internal). Ex: Const global scalars would be modeled as part of /// this region. This region also includes most system globals since they have @@ -306,10 +319,9 @@ class GlobalImmutableSpaceRegion : public NonStaticGlobalSpaceRegion { friend class MemRegionManager; GlobalImmutableSpaceRegion(MemRegionManager *mgr) - : NonStaticGlobalSpaceRegion(mgr, GlobalImmutableSpaceRegionKind) {} + : NonStaticGlobalSpaceRegion(mgr, GlobalImmutableSpaceRegionKind) {} public: - void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { @@ -317,17 +329,16 @@ public: } }; -/// \brief The region containing globals which can be modified by calls to +/// The region containing globals which can be modified by calls to /// "internally" defined functions - (for now just) functions other then system /// calls. class GlobalInternalSpaceRegion : public NonStaticGlobalSpaceRegion { friend class MemRegionManager; GlobalInternalSpaceRegion(MemRegionManager *mgr) - : NonStaticGlobalSpaceRegion(mgr, GlobalInternalSpaceRegionKind) {} + : NonStaticGlobalSpaceRegion(mgr, GlobalInternalSpaceRegionKind) {} public: - void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { @@ -339,9 +350,9 @@ class HeapSpaceRegion : public MemSpaceRegion { friend class MemRegionManager; HeapSpaceRegion(MemRegionManager *mgr) - : MemSpaceRegion(mgr, HeapSpaceRegionKind) {} -public: + : MemSpaceRegion(mgr, HeapSpaceRegionKind) {} +public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { @@ -351,11 +362,11 @@ public: class UnknownSpaceRegion : public MemSpaceRegion { friend class MemRegionManager; + UnknownSpaceRegion(MemRegionManager *mgr) : MemSpaceRegion(mgr, UnknownSpaceRegionKind) {} public: - void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { @@ -370,7 +381,7 @@ class StackSpaceRegion : public MemSpaceRegion { protected: StackSpaceRegion(MemRegionManager *mgr, Kind k, const StackFrameContext *sfc) - : MemSpaceRegion(mgr, k), SFC(sfc) { + : MemSpaceRegion(mgr, k), SFC(sfc) { assert(classof(this)); assert(sfc); } @@ -388,10 +399,11 @@ public: class StackLocalsSpaceRegion : public StackSpaceRegion { friend class MemRegionManager; + StackLocalsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) - : StackSpaceRegion(mgr, StackLocalsSpaceRegionKind, sfc) {} -public: + : StackSpaceRegion(mgr, StackLocalsSpaceRegionKind, sfc) {} +public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { @@ -402,10 +414,11 @@ public: class StackArgumentsSpaceRegion : public StackSpaceRegion { private: friend class MemRegionManager; + StackArgumentsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) - : StackSpaceRegion(mgr, StackArgumentsSpaceRegionKind, sfc) {} -public: + : StackSpaceRegion(mgr, StackArgumentsSpaceRegionKind, sfc) {} +public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { @@ -413,7 +426,6 @@ public: } }; - /// SubRegion - A region that subsets another larger region. Most regions /// are subclasses of SubRegion. class SubRegion : public MemRegion { @@ -421,6 +433,7 @@ class SubRegion : public MemRegion { protected: const MemRegion* superRegion; + SubRegion(const MemRegion *sReg, Kind k) : MemRegion(k), superRegion(sReg) { assert(classof(this)); assert(sReg); @@ -454,8 +467,10 @@ public: class AllocaRegion : public SubRegion { friend class MemRegionManager; - unsigned Cnt; // Block counter. Used to distinguish different pieces of - // memory allocated by alloca at the same call site. + // Block counter. Used to distinguish different pieces of memory allocated by + // alloca at the same call site. + unsigned Cnt; + const Expr *Ex; AllocaRegion(const Expr *ex, unsigned cnt, const MemSpaceRegion *superRegion) @@ -467,7 +482,6 @@ class AllocaRegion : public SubRegion { unsigned Cnt, const MemRegion *superRegion); public: - const Expr *getExpr() const { return Ex; } bool isBoundable() const override { return true; } @@ -485,7 +499,7 @@ public: /// TypedRegion - An abstract class representing regions that are typed. class TypedRegion : public SubRegion { - virtual void anchor() override; + void anchor() override; protected: TypedRegion(const MemRegion *sReg, Kind k) : SubRegion(sReg, k) { @@ -509,7 +523,7 @@ public: /// TypedValueRegion - An abstract class representing regions having a typed value. class TypedValueRegion : public TypedRegion { - virtual void anchor() override; + void anchor() override; protected: TypedValueRegion(const MemRegion* sReg, Kind k) : TypedRegion(sReg, k) { @@ -541,9 +555,8 @@ public: } }; - class CodeTextRegion : public TypedRegion { - virtual void anchor() override; + void anchor() override; protected: CodeTextRegion(const MemSpaceRegion *sreg, Kind k) : TypedRegion(sreg, k) { @@ -566,7 +579,7 @@ class FunctionCodeRegion : public CodeTextRegion { const NamedDecl *FD; FunctionCodeRegion(const NamedDecl *fd, const CodeSpaceRegion* sreg) - : CodeTextRegion(sreg, FunctionCodeRegionKind), FD(fd) { + : CodeTextRegion(sreg, FunctionCodeRegionKind), FD(fd) { assert(isa<ObjCMethodDecl>(fd) || isa<FunctionDecl>(fd)); } @@ -576,7 +589,7 @@ class FunctionCodeRegion : public CodeTextRegion { public: QualType getLocationType() const override { const ASTContext &Ctx = getContext(); - if (const FunctionDecl *D = dyn_cast<FunctionDecl>(FD)) { + if (const auto *D = dyn_cast<FunctionDecl>(FD)) { return Ctx.getPointerType(D->getType()); } @@ -585,7 +598,7 @@ public: // TODO: We might want to return a different type here (ex: id (*ty)(...)) // depending on how it is used. - return QualType(); + return {}; } const NamedDecl *getDecl() const { @@ -601,7 +614,6 @@ public: } }; - /// BlockCodeRegion - A region that represents code texts of blocks (closures). /// Blocks are represented with two kinds of regions. BlockCodeRegions /// represent the "code", while BlockDataRegions represent instances of blocks, @@ -657,15 +669,15 @@ class BlockDataRegion : public TypedRegion { friend class MemRegionManager; const BlockCodeRegion *BC; - const LocationContext *LC; // Can be null */ + const LocationContext *LC; // Can be null unsigned BlockCount; - void *ReferencedVars; - void *OriginalVars; + void *ReferencedVars = nullptr; + void *OriginalVars = nullptr; BlockDataRegion(const BlockCodeRegion *bc, const LocationContext *lc, unsigned count, const MemSpaceRegion *sreg) : TypedRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), - BlockCount(count), ReferencedVars(nullptr), OriginalVars(nullptr) { + BlockCount(count) { assert(bc); assert(lc); assert(isa<GlobalImmutableSpaceRegion>(sreg) || @@ -679,7 +691,7 @@ class BlockDataRegion : public TypedRegion { public: const BlockCodeRegion *getCodeRegion() const { return BC; } - + const BlockDecl *getDecl() const { return BC->getDecl(); } QualType getLocationType() const override { return BC->getLocationType(); } @@ -687,14 +699,16 @@ public: class referenced_vars_iterator { const MemRegion * const *R; const MemRegion * const *OriginalR; + public: explicit referenced_vars_iterator(const MemRegion * const *r, const MemRegion * const *originalR) - : R(r), OriginalR(originalR) {} + : R(r), OriginalR(originalR) {} const VarRegion *getCapturedRegion() const { return cast<VarRegion>(*R); } + const VarRegion *getOriginalRegion() const { return cast<VarRegion>(*OriginalR); } @@ -703,10 +717,12 @@ public: assert((R == nullptr) == (I.R == nullptr)); return I.R == R; } + bool operator!=(const referenced_vars_iterator &I) const { assert((R == nullptr) == (I.R == nullptr)); return I.R != R; } + referenced_vars_iterator &operator++() { ++R; ++OriginalR; @@ -728,6 +744,7 @@ public: static bool classof(const MemRegion* R) { return R->getKind() == BlockDataRegionKind; } + private: void LazyInitializeReferencedVars(); std::pair<const VarRegion *, const VarRegion *> @@ -754,9 +771,7 @@ class SymbolicRegion : public SubRegion { } public: - SymbolRef getSymbol() const { - return sym; - } + SymbolRef getSymbol() const { return sym; } bool isBoundable() const override { return true; } @@ -779,24 +794,21 @@ public: class StringRegion : public TypedValueRegion { friend class MemRegionManager; - const StringLiteral* Str; + const StringLiteral *Str; StringRegion(const StringLiteral *str, const GlobalInternalSpaceRegion *sreg) : TypedValueRegion(sreg, StringRegionKind), Str(str) { assert(str); } - static void ProfileRegion(llvm::FoldingSetNodeID& ID, - const StringLiteral* Str, - const MemRegion* superRegion); + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + const StringLiteral *Str, + const MemRegion *superRegion); public: + const StringLiteral *getStringLiteral() const { return Str; } - const StringLiteral* getStringLiteral() const { return Str; } - - QualType getValueType() const override { - return Str->getType(); - } + QualType getValueType() const override { return Str->getType(); } DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; @@ -817,7 +829,7 @@ public: class ObjCStringRegion : public TypedValueRegion { friend class MemRegionManager; - const ObjCStringLiteral* Str; + const ObjCStringLiteral *Str; ObjCStringRegion(const ObjCStringLiteral *str, const GlobalInternalSpaceRegion *sreg) @@ -825,17 +837,14 @@ class ObjCStringRegion : public TypedValueRegion { assert(str); } - static void ProfileRegion(llvm::FoldingSetNodeID& ID, - const ObjCStringLiteral* Str, - const MemRegion* superRegion); + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + const ObjCStringLiteral *Str, + const MemRegion *superRegion); public: - - const ObjCStringLiteral* getObjCStringLiteral() const { return Str; } + const ObjCStringLiteral *getObjCStringLiteral() const { return Str; } - QualType getValueType() const override { - return Str->getType(); - } + QualType getValueType() const override { return Str->getType(); } bool isBoundable() const override { return false; } @@ -869,10 +878,9 @@ class CompoundLiteralRegion : public TypedValueRegion { static void ProfileRegion(llvm::FoldingSetNodeID& ID, const CompoundLiteralExpr *CL, const MemRegion* superRegion); + public: - QualType getValueType() const override { - return CL->getType(); - } + QualType getValueType() const override { return CL->getType(); } bool isBoundable() const override { return !CL->isFileScope(); } @@ -943,13 +951,13 @@ public: void dumpToStream(raw_ostream &os) const override; - static bool classof(const MemRegion* R) { - return R->getKind() == VarRegionKind; - } - bool canPrintPrettyAsExpr() const override; void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == VarRegionKind; + } }; /// CXXThisRegion - Represents the region for the implicit 'this' parameter @@ -961,7 +969,10 @@ class CXXThisRegion : public TypedValueRegion { CXXThisRegion(const PointerType *thisPointerTy, const StackArgumentsSpaceRegion *sReg) : TypedValueRegion(sReg, CXXThisRegionKind), - ThisPointerTy(thisPointerTy) {} + ThisPointerTy(thisPointerTy) { + assert(ThisPointerTy->getPointeeType()->getAsCXXRecordDecl() && + "Invalid region type!"); + } static void ProfileRegion(llvm::FoldingSetNodeID &ID, const PointerType *PT, @@ -988,7 +999,7 @@ class FieldRegion : public DeclRegion { friend class MemRegionManager; FieldRegion(const FieldDecl *fd, const SubRegion* sReg) - : DeclRegion(fd, sReg, FieldRegionKind) {} + : DeclRegion(fd, sReg, FieldRegionKind) {} static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FieldDecl *FD, const MemRegion* superRegion) { @@ -1005,16 +1016,16 @@ public: DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; - static bool classof(const MemRegion* R) { - return R->getKind() == FieldRegionKind; - } - void dumpToStream(raw_ostream &os) const override; bool canPrintPretty() const override; void printPretty(raw_ostream &os) const override; bool canPrintPrettyAsExpr() const override; void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == FieldRegionKind; + } }; class ObjCIvarRegion : public DeclRegion { @@ -1038,12 +1049,11 @@ public: return R->getKind() == ObjCIvarRegionKind; } }; + //===----------------------------------------------------------------------===// // Auxiliary data classes for use with MemRegions. //===----------------------------------------------------------------------===// -class ElementRegion; - class RegionRawOffset { friend class ElementRegion; @@ -1051,7 +1061,7 @@ class RegionRawOffset { CharUnits Offset; RegionRawOffset(const MemRegion* reg, CharUnits offset = CharUnits::Zero()) - : Region(reg), Offset(offset) {} + : Region(reg), Offset(offset) {} public: // FIXME: Eventually support symbolic offsets. @@ -1062,7 +1072,7 @@ public: void dump() const; }; -/// \brief ElementRegin is used to represent both array elements and casts. +/// ElementRegin is used to represent both array elements and casts. class ElementRegion : public TypedValueRegion { friend class MemRegionManager; @@ -1070,27 +1080,25 @@ class ElementRegion : public TypedValueRegion { NonLoc Index; ElementRegion(QualType elementType, NonLoc Idx, const SubRegion *sReg) - : TypedValueRegion(sReg, ElementRegionKind), - ElementType(elementType), Index(Idx) { + : TypedValueRegion(sReg, ElementRegionKind), ElementType(elementType), + Index(Idx) { assert((!Idx.getAs<nonloc::ConcreteInt>() || Idx.castAs<nonloc::ConcreteInt>().getValue().isSigned()) && "The index must be signed"); + assert(!elementType.isNull() && !elementType->isVoidType() && + "Invalid region type!"); } static void ProfileRegion(llvm::FoldingSetNodeID& ID, QualType elementType, SVal Idx, const MemRegion* superRegion); public: - NonLoc getIndex() const { return Index; } - QualType getValueType() const override { - return ElementType; - } + QualType getValueType() const override { return ElementType; } + + QualType getElementType() const { return ElementType; } - QualType getElementType() const { - return ElementType; - } /// Compute the offset within the array. The array might also be a subobject. RegionRawOffset getAsArrayOffset() const; @@ -1122,9 +1130,7 @@ class CXXTempObjectRegion : public TypedValueRegion { public: const Expr *getExpr() const { return Ex; } - QualType getValueType() const override { - return Ex->getType(); - } + QualType getValueType() const override { return Ex->getType(); } void dumpToStream(raw_ostream &os) const override; @@ -1161,18 +1167,18 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const override; - static bool classof(const MemRegion *region) { - return region->getKind() == CXXBaseObjectRegionKind; - } - bool canPrintPrettyAsExpr() const override; void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion *region) { + return region->getKind() == CXXBaseObjectRegionKind; + } }; template<typename RegionTy> const RegionTy* MemRegion::getAs() const { - if (const RegionTy* RT = dyn_cast<RegionTy>(this)) + if (const auto *RT = dyn_cast<RegionTy>(this)) return RT; return nullptr; @@ -1187,11 +1193,10 @@ class MemRegionManager { llvm::BumpPtrAllocator& A; llvm::FoldingSet<MemRegion> Regions; - GlobalInternalSpaceRegion *InternalGlobals; - GlobalSystemSpaceRegion *SystemGlobals; - GlobalImmutableSpaceRegion *ImmutableGlobals; + GlobalInternalSpaceRegion *InternalGlobals = nullptr; + GlobalSystemSpaceRegion *SystemGlobals = nullptr; + GlobalImmutableSpaceRegion *ImmutableGlobals = nullptr; - llvm::DenseMap<const StackFrameContext *, StackLocalsSpaceRegion *> StackLocalsSpaceRegions; llvm::DenseMap<const StackFrameContext *, StackArgumentsSpaceRegion *> @@ -1199,16 +1204,12 @@ class MemRegionManager { llvm::DenseMap<const CodeTextRegion *, StaticGlobalSpaceRegion *> StaticsGlobalSpaceRegions; - HeapSpaceRegion *heap; - UnknownSpaceRegion *unknown; - CodeSpaceRegion *code; + HeapSpaceRegion *heap = nullptr; + UnknownSpaceRegion *unknown = nullptr; + CodeSpaceRegion *code = nullptr; public: - MemRegionManager(ASTContext &c, llvm::BumpPtrAllocator &a) - : C(c), A(a), InternalGlobals(nullptr), SystemGlobals(nullptr), - ImmutableGlobals(nullptr), heap(nullptr), unknown(nullptr), - code(nullptr) {} - + MemRegionManager(ASTContext &c, llvm::BumpPtrAllocator &a) : C(c), A(a) {} ~MemRegionManager(); ASTContext &getContext() { return C; } @@ -1256,13 +1257,13 @@ public: const CXXThisRegion *getCXXThisRegion(QualType thisPointerTy, const LocationContext *LC); - /// \brief Retrieve or create a "symbolic" memory region. + /// Retrieve or create a "symbolic" memory region. const SymbolicRegion* getSymbolicRegion(SymbolRef Sym); - /// \brief Return a unique symbolic region belonging to heap memory space. + /// Return a unique symbolic region belonging to heap memory space. const SymbolicRegion *getSymbolicHeapRegion(SymbolRef sym); - const StringRegion *getStringRegion(const StringLiteral* Str); + const StringRegion *getStringRegion(const StringLiteral *Str); const ObjCStringRegion *getObjCStringRegion(const ObjCStringLiteral *Str); @@ -1381,24 +1382,28 @@ inline ASTContext &MemRegion::getContext() const { /// Information about invalidation for a particular region/symbol. class RegionAndSymbolInvalidationTraits { - typedef unsigned char StorageTypeForKinds; + using StorageTypeForKinds = unsigned char; + llvm::DenseMap<const MemRegion *, StorageTypeForKinds> MRTraitsMap; llvm::DenseMap<SymbolRef, StorageTypeForKinds> SymTraitsMap; - typedef llvm::DenseMap<const MemRegion *, StorageTypeForKinds>::const_iterator - const_region_iterator; - typedef llvm::DenseMap<SymbolRef, StorageTypeForKinds>::const_iterator - const_symbol_iterator; + using const_region_iterator = + llvm::DenseMap<const MemRegion *, StorageTypeForKinds>::const_iterator; + using const_symbol_iterator = + llvm::DenseMap<SymbolRef, StorageTypeForKinds>::const_iterator; public: - /// \brief Describes different invalidation traits. + /// Describes different invalidation traits. enum InvalidationKinds { /// Tells that a region's contents is not changed. TK_PreserveContents = 0x1, + /// Suppress pointer-escaping of a region. TK_SuppressEscape = 0x2, + // Do not invalidate super region. TK_DoNotInvalidateSuperRegion = 0x4, + /// When applied to a MemSpaceRegion, indicates the entire memory space /// should be invalidated. TK_EntireMemSpace = 0x8 @@ -1416,8 +1421,7 @@ public: //===----------------------------------------------------------------------===// // Pretty-printing regions. //===----------------------------------------------------------------------===// -inline raw_ostream &operator<<(raw_ostream &os, - const clang::ento::MemRegion *R) { +inline raw_ostream &operator<<(raw_ostream &os, const MemRegion *R) { R->dumpToStream(os); return os; } @@ -1426,4 +1430,4 @@ inline raw_ostream &operator<<(raw_ostream &os, } // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_MEMREGION_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index dd2564b0a3c3e..17ab7379fdba9 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -177,20 +177,20 @@ public: /// /// This returns a new state with the added constraint on \p cond. /// If no new state is feasible, NULL is returned. - ProgramStateRef assume(DefinedOrUnknownSVal cond, bool assumption) const; + LLVM_NODISCARD ProgramStateRef assume(DefinedOrUnknownSVal cond, + bool assumption) const; /// Assumes both "true" and "false" for \p cond, and returns both /// corresponding states (respectively). /// /// This is more efficient than calling assume() twice. Note that one (but not /// both) of the returned states may be NULL. - std::pair<ProgramStateRef, ProgramStateRef> + LLVM_NODISCARD std::pair<ProgramStateRef, ProgramStateRef> assume(DefinedOrUnknownSVal cond) const; - ProgramStateRef assumeInBound(DefinedOrUnknownSVal idx, - DefinedOrUnknownSVal upperBound, - bool assumption, - QualType IndexType = QualType()) const; + LLVM_NODISCARD ProgramStateRef + assumeInBound(DefinedOrUnknownSVal idx, DefinedOrUnknownSVal upperBound, + bool assumption, QualType IndexType = QualType()) const; /// Assumes that the value of \p Val is bounded with [\p From; \p To] /// (if \p assumption is "true") or it is fully out of this range @@ -198,24 +198,31 @@ public: /// /// This returns a new state with the added constraint on \p cond. /// If no new state is feasible, NULL is returned. - ProgramStateRef assumeInclusiveRange(DefinedOrUnknownSVal Val, - const llvm::APSInt &From, - const llvm::APSInt &To, - bool assumption) const; + LLVM_NODISCARD ProgramStateRef assumeInclusiveRange(DefinedOrUnknownSVal Val, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool assumption) const; /// Assumes given range both "true" and "false" for \p Val, and returns both /// corresponding states (respectively). /// /// This is more efficient than calling assume() twice. Note that one (but not /// both) of the returned states may be NULL. - std::pair<ProgramStateRef, ProgramStateRef> + LLVM_NODISCARD std::pair<ProgramStateRef, ProgramStateRef> assumeInclusiveRange(DefinedOrUnknownSVal Val, const llvm::APSInt &From, const llvm::APSInt &To) const; - /// \brief Check if the given SVal is constrained to zero or is a zero + /// Check if the given SVal is not constrained to zero and is not + /// a zero constant. + ConditionTruthVal isNonNull(SVal V) const; + + /// Check if the given SVal is constrained to zero or is a zero /// constant. ConditionTruthVal isNull(SVal V) const; + /// \return Whether values \p Lhs and \p Rhs are equal. + ConditionTruthVal areEqual(SVal Lhs, SVal Rhs) const; + /// Utility method for getting regions. const VarRegion* getRegion(const VarDecl *D, const LocationContext *LC) const; @@ -225,21 +232,34 @@ public: /// Create a new state by binding the value 'V' to the statement 'S' in the /// state's environment. - ProgramStateRef BindExpr(const Stmt *S, const LocationContext *LCtx, - SVal V, bool Invalidate = true) const; - - ProgramStateRef bindLoc(Loc location, - SVal V, - const LocationContext *LCtx, - bool notifyChanges = true) const; - - ProgramStateRef bindLoc(SVal location, SVal V, const LocationContext *LCtx) const; - - ProgramStateRef bindDefault(SVal loc, SVal V, const LocationContext *LCtx) const; - - ProgramStateRef killBinding(Loc LV) const; - - /// \brief Returns the state with bindings for the given regions + LLVM_NODISCARD ProgramStateRef BindExpr(const Stmt *S, + const LocationContext *LCtx, SVal V, + bool Invalidate = true) const; + + LLVM_NODISCARD ProgramStateRef bindLoc(Loc location, SVal V, + const LocationContext *LCtx, + bool notifyChanges = true) const; + + LLVM_NODISCARD ProgramStateRef bindLoc(SVal location, SVal V, + const LocationContext *LCtx) const; + + /// Initializes the region of memory represented by \p loc with an initial + /// value. Once initialized, all values loaded from any sub-regions of that + /// region will be equal to \p V, unless overwritten later by the program. + /// This method should not be used on regions that are already initialized. + /// If you need to indicate that memory contents have suddenly become unknown + /// within a certain region of memory, consider invalidateRegions(). + LLVM_NODISCARD ProgramStateRef + bindDefaultInitial(SVal loc, SVal V, const LocationContext *LCtx) const; + + /// Performs C++ zero-initialization procedure on the region of memory + /// represented by \p loc. + LLVM_NODISCARD ProgramStateRef + bindDefaultZero(SVal loc, const LocationContext *LCtx) const; + + LLVM_NODISCARD ProgramStateRef killBinding(Loc LV) const; + + /// Returns the state with bindings for the given regions /// cleared from the store. /// /// Optionally invalidates global regions as well. @@ -257,14 +277,14 @@ public: /// the call and should be considered directly invalidated. /// \param ITraits information about special handling for a particular /// region/symbol. - ProgramStateRef + LLVM_NODISCARD ProgramStateRef invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned BlockCount, const LocationContext *LCtx, bool CausesPointerEscape, InvalidatedSymbols *IS = nullptr, const CallEvent *Call = nullptr, RegionAndSymbolInvalidationTraits *ITraits = nullptr) const; - ProgramStateRef + LLVM_NODISCARD ProgramStateRef invalidateRegions(ArrayRef<SVal> Regions, const Expr *E, unsigned BlockCount, const LocationContext *LCtx, bool CausesPointerEscape, InvalidatedSymbols *IS = nullptr, @@ -273,8 +293,15 @@ public: /// enterStackFrame - Returns the state for entry to the given stack frame, /// preserving the current state. - ProgramStateRef enterStackFrame(const CallEvent &Call, - const StackFrameContext *CalleeCtx) const; + LLVM_NODISCARD ProgramStateRef enterStackFrame( + const CallEvent &Call, const StackFrameContext *CalleeCtx) const; + + /// Get the lvalue for a base class object reference. + Loc getLValue(const CXXBaseSpecifier &BaseSpec, const SubRegion *Super) const; + + /// Get the lvalue for a base class object reference. + Loc getLValue(const CXXRecordDecl *BaseClass, const SubRegion *Super, + bool IsVirtual) const; /// Get the lvalue for a variable reference. Loc getLValue(const VarDecl *D, const LocationContext *LC) const; @@ -299,24 +326,24 @@ public: SVal getSValAsScalarOrLoc(const Stmt *Ex, const LocationContext *LCtx) const; - /// \brief Return the value bound to the specified location. + /// Return the value bound to the specified location. /// Returns UnknownVal() if none found. SVal getSVal(Loc LV, QualType T = QualType()) const; /// Returns the "raw" SVal bound to LV before any value simplfication. SVal getRawSVal(Loc LV, QualType T= QualType()) const; - /// \brief Return the value bound to the specified location. + /// Return the value bound to the specified location. /// Returns UnknownVal() if none found. SVal getSVal(const MemRegion* R, QualType T = QualType()) const; - /// \brief Return the value bound to the specified location, assuming + /// Return the value bound to the specified location, assuming /// that the value is a scalar integer or an enumeration or a pointer. /// Returns UnknownVal() if none found or the region is not known to hold /// a value of such type. SVal getSValAsScalarOrLoc(const MemRegion *R) const; - /// \brief Visits the symbols reachable from the given SVal using the provided + /// Visits the symbols reachable from the given SVal using the provided /// SymbolVisitor. /// /// This is a convenience API. Consider using ScanReachableSymbols class @@ -325,12 +352,12 @@ public: /// \sa ScanReachableSymbols bool scanReachableSymbols(SVal val, SymbolVisitor& visitor) const; - /// \brief Visits the symbols reachable from the SVals in the given range + /// Visits the symbols reachable from the SVals in the given range /// using the provided SymbolVisitor. bool scanReachableSymbols(const SVal *I, const SVal *E, SymbolVisitor &visitor) const; - /// \brief Visits the symbols reachable from the regions in the given + /// Visits the symbols reachable from the regions in the given /// MemRegions range using the provided SymbolVisitor. bool scanReachableSymbols(const MemRegion * const *I, const MemRegion * const *E, @@ -345,27 +372,29 @@ public: const MemRegion * const *end) const; /// Create a new state in which the statement is marked as tainted. - ProgramStateRef addTaint(const Stmt *S, const LocationContext *LCtx, - TaintTagType Kind = TaintTagGeneric) const; + LLVM_NODISCARD ProgramStateRef + addTaint(const Stmt *S, const LocationContext *LCtx, + TaintTagType Kind = TaintTagGeneric) const; /// Create a new state in which the value is marked as tainted. - ProgramStateRef addTaint(SVal V, TaintTagType Kind = TaintTagGeneric) const; + LLVM_NODISCARD ProgramStateRef + addTaint(SVal V, TaintTagType Kind = TaintTagGeneric) const; /// Create a new state in which the symbol is marked as tainted. - ProgramStateRef addTaint(SymbolRef S, + LLVM_NODISCARD ProgramStateRef addTaint(SymbolRef S, TaintTagType Kind = TaintTagGeneric) const; /// Create a new state in which the region symbol is marked as tainted. - ProgramStateRef addTaint(const MemRegion *R, - TaintTagType Kind = TaintTagGeneric) const; + LLVM_NODISCARD ProgramStateRef + addTaint(const MemRegion *R, TaintTagType Kind = TaintTagGeneric) const; /// Create a new state in a which a sub-region of a given symbol is tainted. /// This might be necessary when referring to regions that can not have an /// individual symbol, e.g. if they are represented by the default binding of /// a LazyCompoundVal. - ProgramStateRef addPartialTaint(SymbolRef ParentSym, - const SubRegion *SubRegion, - TaintTagType Kind = TaintTagGeneric) const; + LLVM_NODISCARD ProgramStateRef + addPartialTaint(SymbolRef ParentSym, const SubRegion *SubRegion, + TaintTagType Kind = TaintTagGeneric) const; /// Check if the statement is tainted in the current state. bool isTainted(const Stmt *S, const LocationContext *LCtx, @@ -380,8 +409,9 @@ public: void *const* FindGDM(void *K) const; - template<typename T> - ProgramStateRef add(typename ProgramStateTrait<T>::key_type K) const; + template <typename T> + LLVM_NODISCARD ProgramStateRef + add(typename ProgramStateTrait<T>::key_type K) const; template <typename T> typename ProgramStateTrait<T>::data_type @@ -399,27 +429,31 @@ public: template <typename T> typename ProgramStateTrait<T>::context_type get_context() const; + template <typename T> + LLVM_NODISCARD ProgramStateRef + remove(typename ProgramStateTrait<T>::key_type K) const; - template<typename T> - ProgramStateRef remove(typename ProgramStateTrait<T>::key_type K) const; - - template<typename T> - ProgramStateRef remove(typename ProgramStateTrait<T>::key_type K, - typename ProgramStateTrait<T>::context_type C) const; template <typename T> - ProgramStateRef remove() const; + LLVM_NODISCARD ProgramStateRef + remove(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::context_type C) const; - template<typename T> - ProgramStateRef set(typename ProgramStateTrait<T>::data_type D) const; + template <typename T> LLVM_NODISCARD ProgramStateRef remove() const; - template<typename T> - ProgramStateRef set(typename ProgramStateTrait<T>::key_type K, - typename ProgramStateTrait<T>::value_type E) const; + template <typename T> + LLVM_NODISCARD ProgramStateRef + set(typename ProgramStateTrait<T>::data_type D) const; - template<typename T> - ProgramStateRef set(typename ProgramStateTrait<T>::key_type K, - typename ProgramStateTrait<T>::value_type E, - typename ProgramStateTrait<T>::context_type C) const; + template <typename T> + LLVM_NODISCARD ProgramStateRef + set(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::value_type E) const; + + template <typename T> + LLVM_NODISCARD ProgramStateRef + set(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::value_type E, + typename ProgramStateTrait<T>::context_type C) const; template<typename T> bool contains(typename ProgramStateTrait<T>::key_type key) const { @@ -428,9 +462,10 @@ public: } // Pretty-printing. - void print(raw_ostream &Out, const char *nl = "\n", - const char *sep = "") const; - void printDOT(raw_ostream &Out) const; + void print(raw_ostream &Out, const char *nl = "\n", const char *sep = "", + const LocationContext *CurrentLC = nullptr) const; + void printDOT(raw_ostream &Out, + const LocationContext *CurrentLC = nullptr) const; void printTaint(raw_ostream &Out, const char *nl = "\n", const char *sep = "") const; @@ -705,6 +740,22 @@ inline ProgramStateRef ProgramState::bindLoc(SVal LV, SVal V, const LocationCont return this; } +inline Loc ProgramState::getLValue(const CXXBaseSpecifier &BaseSpec, + const SubRegion *Super) const { + const auto *Base = BaseSpec.getType()->getAsCXXRecordDecl(); + return loc::MemRegionVal( + getStateManager().getRegionManager().getCXXBaseObjectRegion( + Base, Super, BaseSpec.isVirtual())); +} + +inline Loc ProgramState::getLValue(const CXXRecordDecl *BaseClass, + const SubRegion *Super, + bool IsVirtual) const { + return loc::MemRegionVal( + getStateManager().getRegionManager().getCXXBaseObjectRegion( + BaseClass, Super, IsVirtual)); +} + inline Loc ProgramState::getLValue(const VarDecl *VD, const LocationContext *LC) const { return getStateManager().StoreMgr->getLValueVar(VD, LC); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h index a04fa90059555..5555b292534c5 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h @@ -14,33 +14,28 @@ // //===----------------------------------------------------------------------===// - #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableSet.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/DataTypes.h" - -namespace llvm { - template <typename K, typename D, typename I> class ImmutableMap; - template <typename K, typename I> class ImmutableSet; - template <typename T> class ImmutableList; - template <typename T> class ImmutableListImpl; -} +#include <cstdint> namespace clang { - namespace ento { + template <typename T> struct ProgramStatePartialTrait; /// Declares a program state trait for type \p Type called \p Name, and - /// introduce a typedef named \c NameTy. + /// introduce a type named \c NameTy. /// The macro should not be used inside namespaces, or for traits that must /// be accessible from more than one translation unit. #define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type) \ namespace { \ class Name {}; \ - typedef Type Name ## Ty; \ + using Name ## Ty = Type; \ } \ namespace clang { \ namespace ento { \ @@ -52,28 +47,30 @@ namespace ento { } \ } - // Partial-specialization for ImmutableMap. - template <typename Key, typename Data, typename Info> - struct ProgramStatePartialTrait< llvm::ImmutableMap<Key,Data,Info> > { - typedef llvm::ImmutableMap<Key,Data,Info> data_type; - typedef typename data_type::Factory& context_type; - typedef Key key_type; - typedef Data value_type; - typedef const value_type* lookup_type; - - static inline data_type MakeData(void *const* p) { - return p ? data_type((typename data_type::TreeTy*) *p) + struct ProgramStatePartialTrait<llvm::ImmutableMap<Key, Data, Info>> { + using data_type = llvm::ImmutableMap<Key, Data, Info>; + using context_type = typename data_type::Factory &; + using key_type = Key; + using value_type = Data; + using lookup_type = const value_type *; + + static data_type MakeData(void *const *p) { + return p ? data_type((typename data_type::TreeTy *) *p) : data_type(nullptr); } - static inline void *MakeVoidPtr(data_type B) { + + static void *MakeVoidPtr(data_type B) { return B.getRoot(); } + static lookup_type Lookup(data_type B, key_type K) { return B.lookup(K); } - static data_type Set(data_type B, key_type K, value_type E,context_type F){ + + static data_type Set(data_type B, key_type K, value_type E, + context_type F) { return F.add(B, K, E); } @@ -85,8 +82,8 @@ namespace ento { return B.contains(K); } - static inline context_type MakeContext(void *p) { - return *((typename data_type::Factory*) p); + static context_type MakeContext(void *p) { + return *((typename data_type::Factory *) p); } static void *CreateContext(llvm::BumpPtrAllocator& Alloc) { @@ -94,7 +91,7 @@ namespace ento { } static void DeleteContext(void *Ctx) { - delete (typename data_type::Factory*) Ctx; + delete (typename data_type::Factory *) Ctx; } }; @@ -107,21 +104,19 @@ namespace ento { /// can deal with. #define CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value) llvm::ImmutableMap<Key, Value> - // Partial-specialization for ImmutableSet. - template <typename Key, typename Info> - struct ProgramStatePartialTrait< llvm::ImmutableSet<Key,Info> > { - typedef llvm::ImmutableSet<Key,Info> data_type; - typedef typename data_type::Factory& context_type; - typedef Key key_type; + struct ProgramStatePartialTrait<llvm::ImmutableSet<Key, Info>> { + using data_type = llvm::ImmutableSet<Key, Info>; + using context_type = typename data_type::Factory &; + using key_type = Key; - static inline data_type MakeData(void *const* p) { - return p ? data_type((typename data_type::TreeTy*) *p) + static data_type MakeData(void *const *p) { + return p ? data_type((typename data_type::TreeTy *) *p) : data_type(nullptr); } - static inline void *MakeVoidPtr(data_type B) { + static void *MakeVoidPtr(data_type B) { return B.getRoot(); } @@ -137,27 +132,25 @@ namespace ento { return B.contains(K); } - static inline context_type MakeContext(void *p) { - return *((typename data_type::Factory*) p); + static context_type MakeContext(void *p) { + return *((typename data_type::Factory *) p); } - static void *CreateContext(llvm::BumpPtrAllocator& Alloc) { + static void *CreateContext(llvm::BumpPtrAllocator &Alloc) { return new typename data_type::Factory(Alloc); } static void DeleteContext(void *Ctx) { - delete (typename data_type::Factory*) Ctx; + delete (typename data_type::Factory *) Ctx; } }; - // Partial-specialization for ImmutableList. - template <typename T> - struct ProgramStatePartialTrait< llvm::ImmutableList<T> > { - typedef llvm::ImmutableList<T> data_type; - typedef T key_type; - typedef typename data_type::Factory& context_type; + struct ProgramStatePartialTrait<llvm::ImmutableList<T>> { + using data_type = llvm::ImmutableList<T>; + using key_type = T; + using context_type = typename data_type::Factory &; static data_type Add(data_type L, key_type K, context_type F) { return F.add(K, L); @@ -167,83 +160,84 @@ namespace ento { return L.contains(K); } - static inline data_type MakeData(void *const* p) { - return p ? data_type((const llvm::ImmutableListImpl<T>*) *p) + static data_type MakeData(void *const *p) { + return p ? data_type((const llvm::ImmutableListImpl<T> *) *p) : data_type(nullptr); } - static inline void *MakeVoidPtr(data_type D) { + static void *MakeVoidPtr(data_type D) { return const_cast<llvm::ImmutableListImpl<T> *>(D.getInternalPointer()); } - static inline context_type MakeContext(void *p) { - return *((typename data_type::Factory*) p); + static context_type MakeContext(void *p) { + return *((typename data_type::Factory *) p); } - static void *CreateContext(llvm::BumpPtrAllocator& Alloc) { + static void *CreateContext(llvm::BumpPtrAllocator &Alloc) { return new typename data_type::Factory(Alloc); } static void DeleteContext(void *Ctx) { - delete (typename data_type::Factory*) Ctx; + delete (typename data_type::Factory *) Ctx; } }; - // Partial specialization for bool. template <> struct ProgramStatePartialTrait<bool> { - typedef bool data_type; + using data_type = bool; - static inline data_type MakeData(void *const* p) { + static data_type MakeData(void *const *p) { return p ? (data_type) (uintptr_t) *p : data_type(); } - static inline void *MakeVoidPtr(data_type d) { - return (void*) (uintptr_t) d; + + static void *MakeVoidPtr(data_type d) { + return (void *) (uintptr_t) d; } }; // Partial specialization for unsigned. template <> struct ProgramStatePartialTrait<unsigned> { - typedef unsigned data_type; + using data_type = unsigned; - static inline data_type MakeData(void *const* p) { + static data_type MakeData(void *const *p) { return p ? (data_type) (uintptr_t) *p : data_type(); } - static inline void *MakeVoidPtr(data_type d) { - return (void*) (uintptr_t) d; + + static void *MakeVoidPtr(data_type d) { + return (void *) (uintptr_t) d; } }; // Partial specialization for void*. - template <> struct ProgramStatePartialTrait<void*> { - typedef void *data_type; + template <> struct ProgramStatePartialTrait<void *> { + using data_type = void *; - static inline data_type MakeData(void *const* p) { + static data_type MakeData(void *const *p) { return p ? *p : data_type(); } - static inline void *MakeVoidPtr(data_type d) { + + static void *MakeVoidPtr(data_type d) { return d; } }; // Partial specialization for const void *. template <> struct ProgramStatePartialTrait<const void *> { - typedef const void *data_type; + using data_type = const void *; - static inline data_type MakeData(void * const *p) { + static data_type MakeData(void *const *p) { return p ? *p : data_type(); } - static inline void *MakeVoidPtr(data_type d) { + static void *MakeVoidPtr(data_type d) { return const_cast<void *>(d); } }; -} // end ento namespace - -} // end clang namespace +} // namespace ento +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h new file mode 100644 index 0000000000000..d2ba1f7c9529a --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -0,0 +1,216 @@ +//== RangedConstraintManager.h ----------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Ranged constraint manager, built on SimpleConstraintManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" + +namespace clang { + +namespace ento { + +/// A Range represents the closed range [from, to]. The caller must +/// guarantee that from <= to. Note that Range is immutable, so as not +/// to subvert RangeSet's immutability. +class Range : public std::pair<const llvm::APSInt *, const llvm::APSInt *> { +public: + Range(const llvm::APSInt &from, const llvm::APSInt &to) + : std::pair<const llvm::APSInt *, const llvm::APSInt *>(&from, &to) { + assert(from <= to); + } + bool Includes(const llvm::APSInt &v) const { + return *first <= v && v <= *second; + } + const llvm::APSInt &From() const { return *first; } + const llvm::APSInt &To() const { return *second; } + const llvm::APSInt *getConcreteValue() const { + return &From() == &To() ? &From() : nullptr; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(&From()); + ID.AddPointer(&To()); + } +}; + +class RangeTrait : public llvm::ImutContainerInfo<Range> { +public: + // When comparing if one Range is less than another, we should compare + // the actual APSInt values instead of their pointers. This keeps the order + // consistent (instead of comparing by pointer values) and can potentially + // be used to speed up some of the operations in RangeSet. + static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { + return *lhs.first < *rhs.first || + (!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second); + } +}; + +/// RangeSet contains a set of ranges. If the set is empty, then +/// there the value of a symbol is overly constrained and there are no +/// possible values for that symbol. +class RangeSet { + typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet; + PrimRangeSet ranges; // no need to make const, since it is an + // ImmutableSet - this allows default operator= + // to work. +public: + typedef PrimRangeSet::Factory Factory; + typedef PrimRangeSet::iterator iterator; + + RangeSet(PrimRangeSet RS) : ranges(RS) {} + + /// Create a new set with all ranges of this set and RS. + /// Possible intersections are not checked here. + RangeSet addRange(Factory &F, const RangeSet &RS) { + PrimRangeSet Ranges(RS.ranges); + for (const auto &range : ranges) + Ranges = F.add(Ranges, range); + return RangeSet(Ranges); + } + + iterator begin() const { return ranges.begin(); } + iterator end() const { return ranges.end(); } + + bool isEmpty() const { return ranges.isEmpty(); } + + /// Construct a new RangeSet representing '{ [from, to] }'. + RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) + : ranges(F.add(F.getEmptySet(), Range(from, to))) {} + + /// Profile - Generates a hash profile of this RangeSet for use + /// by FoldingSet. + void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } + + /// getConcreteValue - If a symbol is contrained to equal a specific integer + /// constant then this method returns that value. Otherwise, it returns + /// NULL. + const llvm::APSInt *getConcreteValue() const { + return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr; + } + +private: + void IntersectInRange(BasicValueFactory &BV, Factory &F, + const llvm::APSInt &Lower, const llvm::APSInt &Upper, + PrimRangeSet &newRanges, PrimRangeSet::iterator &i, + PrimRangeSet::iterator &e) const; + + const llvm::APSInt &getMinValue() const; + + bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const; + +public: + RangeSet Intersect(BasicValueFactory &BV, Factory &F, llvm::APSInt Lower, + llvm::APSInt Upper) const; + + RangeSet Negate(BasicValueFactory &BV, Factory &F) const; + + void print(raw_ostream &os) const; + + bool operator==(const RangeSet &other) const { + return ranges == other.ranges; + } +}; + + +class ConstraintRange {}; +using ConstraintRangeTy = llvm::ImmutableMap<SymbolRef, RangeSet>; + +template <> +struct ProgramStateTrait<ConstraintRange> + : public ProgramStatePartialTrait<ConstraintRangeTy> { + static void *GDMIndex() { static int Index; return &Index; } +}; + + +class RangedConstraintManager : public SimpleConstraintManager { +public: + RangedConstraintManager(SubEngine *SE, SValBuilder &SB) + : SimpleConstraintManager(SE, SB) {} + + ~RangedConstraintManager() override; + + //===------------------------------------------------------------------===// + // Implementation for interface from SimpleConstraintManager. + //===------------------------------------------------------------------===// + + ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; + + ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; + + ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; + +protected: + /// Assume a constraint between a symbolic expression and a concrete integer. + virtual ProgramStateRef assumeSymRel(ProgramStateRef State, SymbolRef Sym, + BinaryOperator::Opcode op, + const llvm::APSInt &Int); + + //===------------------------------------------------------------------===// + // Interface that subclasses must implement. + //===------------------------------------------------------------------===// + + // Each of these is of the form "$Sym+Adj <> V", where "<>" is the comparison + // operation for the method being invoked. + + virtual ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymOutsideInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; + + //===------------------------------------------------------------------===// + // Internal implementation. + //===------------------------------------------------------------------===// +private: + static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h new file mode 100644 index 0000000000000..19d3d5973e0fd --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h @@ -0,0 +1,77 @@ +//== SMTConstraintManager.h -------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic API, which will be the base class for +// every SMT solver specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTSolver.h" + +namespace clang { +namespace ento { + +class SMTConstraintManager : public clang::ento::SimpleConstraintManager { + SMTSolverRef &Solver; + +public: + SMTConstraintManager(clang::ento::SubEngine *SE, clang::ento::SValBuilder &SB, + SMTSolverRef &S) + : SimpleConstraintManager(SE, SB), Solver(S) {} + virtual ~SMTConstraintManager() = default; + + //===------------------------------------------------------------------===// + // Implementation for interface from SimpleConstraintManager. + //===------------------------------------------------------------------===// + + ProgramStateRef assumeSym(ProgramStateRef state, SymbolRef Sym, + bool Assumption) override; + + ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; + + ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; + + //===------------------------------------------------------------------===// + // Implementation for interface from ConstraintManager. + //===------------------------------------------------------------------===// + + ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; + + const llvm::APSInt *getSymVal(ProgramStateRef State, + SymbolRef Sym) const override; + + /// Dumps SMT formula + LLVM_DUMP_METHOD void dump() const { Solver->dump(); } + +protected: + // Check whether a new model is satisfiable, and update the program state. + virtual ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym, + const SMTExprRef &Exp) = 0; + + /// Given a program state, construct the logical conjunction and add it to + /// the solver + virtual void addStateConstraints(ProgramStateRef State) const = 0; + + // Generate and check a Z3 model, using the given constraint. + ConditionTruthVal checkModel(ProgramStateRef State, + const SMTExprRef &Exp) const; +}; // end class SMTConstraintManager + +} // namespace ento +} // namespace clang + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SMTContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTContext.h new file mode 100644 index 0000000000000..45c9df4ef4019 --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTContext.h @@ -0,0 +1,31 @@ +//== SMTContext.h -----------------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic Context API, which will be the base class +// for every SMT solver context specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONTEXT_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONTEXT_H + +namespace clang { +namespace ento { + +/// Generic base class for SMT contexts +class SMTContext { +public: + SMTContext() = default; + virtual ~SMTContext() = default; +}; + +} // namespace ento +} // namespace clang + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SMTExpr.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTExpr.h new file mode 100644 index 0000000000000..9dedf96cfaf83 --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTExpr.h @@ -0,0 +1,62 @@ +//== SMTExpr.h --------------------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic Expr API, which will be the base class +// for every SMT solver expr specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTEXPR_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTEXPR_H + +#include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/FoldingSet.h" + +namespace clang { +namespace ento { + +/// Generic base class for SMT exprs +class SMTExpr { +public: + SMTExpr() = default; + virtual ~SMTExpr() = default; + + bool operator<(const SMTExpr &Other) const { + llvm::FoldingSetNodeID ID1, ID2; + Profile(ID1); + Other.Profile(ID2); + return ID1 < ID2; + } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const { + static int Tag = 0; + ID.AddPointer(&Tag); + } + + friend bool operator==(SMTExpr const &LHS, SMTExpr const &RHS) { + return LHS.equal_to(RHS); + } + + virtual void print(raw_ostream &OS) const = 0; + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } + +protected: + /// Query the SMT solver and returns true if two sorts are equal (same kind + /// and bit width). This does not check if the two sorts are the same objects. + virtual bool equal_to(SMTExpr const &other) const = 0; +}; + +/// Shared pointer for SMTExprs, used by SMTSolver API. +using SMTExprRef = std::shared_ptr<SMTExpr>; + +} // namespace ento +} // namespace clang + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SMTSolver.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTSolver.h new file mode 100644 index 0000000000000..a43ca486901bc --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTSolver.h @@ -0,0 +1,996 @@ +//== SMTSolver.h ------------------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic Solver API, which will be the base class +// for every SMT solver specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTSOLVER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTSOLVER_H + +#include "clang/AST/Expr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTSort.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" + +namespace clang { +namespace ento { + +/// Generic base class for SMT Solvers +/// +/// This class is responsible for wrapping all sorts and expression generation, +/// through the mk* methods. It also provides methods to create SMT expressions +/// straight from clang's AST, through the from* methods. +class SMTSolver { +public: + SMTSolver() = default; + virtual ~SMTSolver() = default; + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } + + // Returns an appropriate floating-point sort for the given bitwidth. + SMTSortRef getFloatSort(unsigned BitWidth) { + switch (BitWidth) { + case 16: + return getFloat16Sort(); + case 32: + return getFloat32Sort(); + case 64: + return getFloat64Sort(); + case 128: + return getFloat128Sort(); + default:; + } + llvm_unreachable("Unsupported floating-point bitwidth!"); + } + + // Returns an appropriate sort, given a QualType and it's bit width. + SMTSortRef mkSort(const QualType &Ty, unsigned BitWidth) { + if (Ty->isBooleanType()) + return getBoolSort(); + + if (Ty->isRealFloatingType()) + return getFloatSort(BitWidth); + + return getBitvectorSort(BitWidth); + } + + /// Constructs an SMTExprRef from an unary operator. + SMTExprRef fromUnOp(const UnaryOperator::Opcode Op, const SMTExprRef &Exp) { + switch (Op) { + case UO_Minus: + return mkBVNeg(Exp); + + case UO_Not: + return mkBVNot(Exp); + + case UO_LNot: + return mkNot(Exp); + + default:; + } + llvm_unreachable("Unimplemented opcode"); + } + + /// Constructs an SMTExprRef from a floating-point unary operator. + SMTExprRef fromFloatUnOp(const UnaryOperator::Opcode Op, + const SMTExprRef &Exp) { + switch (Op) { + case UO_Minus: + return mkFPNeg(Exp); + + case UO_LNot: + return fromUnOp(Op, Exp); + + default:; + } + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a n-ary binary operator. + SMTExprRef fromNBinOp(const BinaryOperator::Opcode Op, + const std::vector<SMTExprRef> &ASTs) { + assert(!ASTs.empty()); + + if (Op != BO_LAnd && Op != BO_LOr) + llvm_unreachable("Unimplemented opcode"); + + SMTExprRef res = ASTs.front(); + for (std::size_t i = 1; i < ASTs.size(); ++i) + res = (Op == BO_LAnd) ? mkAnd(res, ASTs[i]) : mkOr(res, ASTs[i]); + return res; + } + + /// Construct an SMTExprRef from a binary operator. + SMTExprRef fromBinOp(const SMTExprRef &LHS, const BinaryOperator::Opcode Op, + const SMTExprRef &RHS, bool isSigned) { + assert(*getSort(LHS) == *getSort(RHS) && "AST's must have the same sort!"); + + switch (Op) { + // Multiplicative operators + case BO_Mul: + return mkBVMul(LHS, RHS); + + case BO_Div: + return isSigned ? mkBVSDiv(LHS, RHS) : mkBVUDiv(LHS, RHS); + + case BO_Rem: + return isSigned ? mkBVSRem(LHS, RHS) : mkBVURem(LHS, RHS); + + // Additive operators + case BO_Add: + return mkBVAdd(LHS, RHS); + + case BO_Sub: + return mkBVSub(LHS, RHS); + + // Bitwise shift operators + case BO_Shl: + return mkBVShl(LHS, RHS); + + case BO_Shr: + return isSigned ? mkBVAshr(LHS, RHS) : mkBVLshr(LHS, RHS); + + // Relational operators + case BO_LT: + return isSigned ? mkBVSlt(LHS, RHS) : mkBVUlt(LHS, RHS); + + case BO_GT: + return isSigned ? mkBVSgt(LHS, RHS) : mkBVUgt(LHS, RHS); + + case BO_LE: + return isSigned ? mkBVSle(LHS, RHS) : mkBVUle(LHS, RHS); + + case BO_GE: + return isSigned ? mkBVSge(LHS, RHS) : mkBVUge(LHS, RHS); + + // Equality operators + case BO_EQ: + return mkEqual(LHS, RHS); + + case BO_NE: + return fromUnOp(UO_LNot, fromBinOp(LHS, BO_EQ, RHS, isSigned)); + + // Bitwise operators + case BO_And: + return mkBVAnd(LHS, RHS); + + case BO_Xor: + return mkBVXor(LHS, RHS); + + case BO_Or: + return mkBVOr(LHS, RHS); + + // Logical operators + case BO_LAnd: + return mkAnd(LHS, RHS); + + case BO_LOr: + return mkOr(LHS, RHS); + + default:; + } + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a special floating-point binary operator. + SMTExprRef fromFloatSpecialBinOp(const SMTExprRef &LHS, + const BinaryOperator::Opcode Op, + const llvm::APFloat::fltCategory &RHS) { + switch (Op) { + // Equality operators + case BO_EQ: + switch (RHS) { + case llvm::APFloat::fcInfinity: + return mkFPIsInfinite(LHS); + + case llvm::APFloat::fcNaN: + return mkFPIsNaN(LHS); + + case llvm::APFloat::fcNormal: + return mkFPIsNormal(LHS); + + case llvm::APFloat::fcZero: + return mkFPIsZero(LHS); + } + break; + + case BO_NE: + return fromFloatUnOp(UO_LNot, fromFloatSpecialBinOp(LHS, BO_EQ, RHS)); + + default:; + } + + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a floating-point binary operator. + SMTExprRef fromFloatBinOp(const SMTExprRef &LHS, + const BinaryOperator::Opcode Op, + const SMTExprRef &RHS) { + assert(*getSort(LHS) == *getSort(RHS) && "AST's must have the same sort!"); + + switch (Op) { + // Multiplicative operators + case BO_Mul: + return mkFPMul(LHS, RHS); + + case BO_Div: + return mkFPDiv(LHS, RHS); + + case BO_Rem: + return mkFPRem(LHS, RHS); + + // Additive operators + case BO_Add: + return mkFPAdd(LHS, RHS); + + case BO_Sub: + return mkFPSub(LHS, RHS); + + // Relational operators + case BO_LT: + return mkFPLt(LHS, RHS); + + case BO_GT: + return mkFPGt(LHS, RHS); + + case BO_LE: + return mkFPLe(LHS, RHS); + + case BO_GE: + return mkFPGe(LHS, RHS); + + // Equality operators + case BO_EQ: + return mkFPEqual(LHS, RHS); + + case BO_NE: + return fromFloatUnOp(UO_LNot, fromFloatBinOp(LHS, BO_EQ, RHS)); + + // Logical operators + case BO_LAnd: + case BO_LOr: + return fromBinOp(LHS, Op, RHS, false); + + default:; + } + + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a QualType FromTy to a QualType ToTy, and + /// their bit widths. + SMTExprRef fromCast(const SMTExprRef &Exp, QualType ToTy, uint64_t ToBitWidth, + QualType FromTy, uint64_t FromBitWidth) { + if ((FromTy->isIntegralOrEnumerationType() && + ToTy->isIntegralOrEnumerationType()) || + (FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) || + (FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) || + (FromTy->isReferenceType() ^ ToTy->isReferenceType())) { + + if (FromTy->isBooleanType()) { + assert(ToBitWidth > 0 && "BitWidth must be positive!"); + return mkIte(Exp, mkBitvector(llvm::APSInt("1"), ToBitWidth), + mkBitvector(llvm::APSInt("0"), ToBitWidth)); + } + + if (ToBitWidth > FromBitWidth) + return FromTy->isSignedIntegerOrEnumerationType() + ? mkBVSignExt(ToBitWidth - FromBitWidth, Exp) + : mkBVZeroExt(ToBitWidth - FromBitWidth, Exp); + + if (ToBitWidth < FromBitWidth) + return mkBVExtract(ToBitWidth - 1, 0, Exp); + + // Both are bitvectors with the same width, ignore the type cast + return Exp; + } + + if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) { + if (ToBitWidth != FromBitWidth) + return mkFPtoFP(Exp, getFloatSort(ToBitWidth)); + + return Exp; + } + + if (FromTy->isIntegralOrEnumerationType() && ToTy->isRealFloatingType()) { + SMTSortRef Sort = getFloatSort(ToBitWidth); + return FromTy->isSignedIntegerOrEnumerationType() ? mkFPtoSBV(Exp, Sort) + : mkFPtoUBV(Exp, Sort); + } + + if (FromTy->isRealFloatingType() && ToTy->isIntegralOrEnumerationType()) + return ToTy->isSignedIntegerOrEnumerationType() + ? mkSBVtoFP(Exp, ToBitWidth) + : mkUBVtoFP(Exp, ToBitWidth); + + llvm_unreachable("Unsupported explicit type cast!"); + } + + // Callback function for doCast parameter on APSInt type. + llvm::APSInt castAPSInt(const llvm::APSInt &V, QualType ToTy, + uint64_t ToWidth, QualType FromTy, + uint64_t FromWidth) { + APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType()); + return TargetType.convert(V); + } + + // Generate an SMTExprRef that represents the given symbolic expression. + // Sets the hasComparison parameter if the expression has a comparison + // operator. + // Sets the RetTy parameter to the final return type after promotions and + // casts. + SMTExprRef getExpr(ASTContext &Ctx, SymbolRef Sym, QualType *RetTy = nullptr, + bool *hasComparison = nullptr) { + if (hasComparison) { + *hasComparison = false; + } + + return getSymExpr(Ctx, Sym, RetTy, hasComparison); + } + + // Generate an SMTExprRef that compares the expression to zero. + SMTExprRef getZeroExpr(ASTContext &Ctx, const SMTExprRef &Exp, QualType Ty, + bool Assumption) { + + if (Ty->isRealFloatingType()) { + llvm::APFloat Zero = + llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty)); + return fromFloatBinOp(Exp, Assumption ? BO_EQ : BO_NE, fromAPFloat(Zero)); + } + + if (Ty->isIntegralOrEnumerationType() || Ty->isAnyPointerType() || + Ty->isBlockPointerType() || Ty->isReferenceType()) { + + // Skip explicit comparison for boolean types + bool isSigned = Ty->isSignedIntegerOrEnumerationType(); + if (Ty->isBooleanType()) + return Assumption ? fromUnOp(UO_LNot, Exp) : Exp; + + return fromBinOp(Exp, Assumption ? BO_EQ : BO_NE, + fromInt("0", Ctx.getTypeSize(Ty)), isSigned); + } + + llvm_unreachable("Unsupported type for zero value!"); + } + + // Recursive implementation to unpack and generate symbolic expression. + // Sets the hasComparison and RetTy parameters. See getExpr(). + SMTExprRef getSymExpr(ASTContext &Ctx, SymbolRef Sym, QualType *RetTy, + bool *hasComparison) { + if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { + if (RetTy) + *RetTy = Sym->getType(); + + return fromData(SD->getSymbolID(), Sym->getType(), + Ctx.getTypeSize(Sym->getType())); + } + + if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { + if (RetTy) + *RetTy = Sym->getType(); + + QualType FromTy; + SMTExprRef Exp = + getSymExpr(Ctx, SC->getOperand(), &FromTy, hasComparison); + // Casting an expression with a comparison invalidates it. Note that this + // must occur after the recursive call above. + // e.g. (signed char) (x > 0) + if (hasComparison) + *hasComparison = false; + return getCastExpr(Ctx, Exp, FromTy, Sym->getType()); + } + + if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + SMTExprRef Exp = getSymBinExpr(Ctx, BSE, hasComparison, RetTy); + // Set the hasComparison parameter, in post-order traversal order. + if (hasComparison) + *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode()); + return Exp; + } + + llvm_unreachable("Unsupported SymbolRef type!"); + } + + // Wrapper to generate SMTExprRef from SymbolCast data. + SMTExprRef getCastExpr(ASTContext &Ctx, const SMTExprRef &Exp, + QualType FromTy, QualType ToTy) { + return fromCast(Exp, ToTy, Ctx.getTypeSize(ToTy), FromTy, + Ctx.getTypeSize(FromTy)); + } + + // Wrapper to generate SMTExprRef from BinarySymExpr. + // Sets the hasComparison and RetTy parameters. See getSMTExprRef(). + SMTExprRef getSymBinExpr(ASTContext &Ctx, const BinarySymExpr *BSE, + bool *hasComparison, QualType *RetTy) { + QualType LTy, RTy; + BinaryOperator::Opcode Op = BSE->getOpcode(); + + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { + SMTExprRef LHS = getSymExpr(Ctx, SIE->getLHS(), <y, hasComparison); + llvm::APSInt NewRInt; + std::tie(NewRInt, RTy) = fixAPSInt(Ctx, SIE->getRHS()); + SMTExprRef RHS = fromAPSInt(NewRInt); + return getBinExpr(Ctx, LHS, LTy, Op, RHS, RTy, RetTy); + } + + if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { + llvm::APSInt NewLInt; + std::tie(NewLInt, LTy) = fixAPSInt(Ctx, ISE->getLHS()); + SMTExprRef LHS = fromAPSInt(NewLInt); + SMTExprRef RHS = getSymExpr(Ctx, ISE->getRHS(), &RTy, hasComparison); + return getBinExpr(Ctx, LHS, LTy, Op, RHS, RTy, RetTy); + } + + if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { + SMTExprRef LHS = getSymExpr(Ctx, SSM->getLHS(), <y, hasComparison); + SMTExprRef RHS = getSymExpr(Ctx, SSM->getRHS(), &RTy, hasComparison); + return getBinExpr(Ctx, LHS, LTy, Op, RHS, RTy, RetTy); + } + + llvm_unreachable("Unsupported BinarySymExpr type!"); + } + + // Wrapper to generate SMTExprRef from unpacked binary symbolic expression. + // Sets the RetTy parameter. See getSMTExprRef(). + SMTExprRef getBinExpr(ASTContext &Ctx, const SMTExprRef &LHS, QualType LTy, + BinaryOperator::Opcode Op, const SMTExprRef &RHS, + QualType RTy, QualType *RetTy) { + SMTExprRef NewLHS = LHS; + SMTExprRef NewRHS = RHS; + doTypeConversion(Ctx, NewLHS, NewRHS, LTy, RTy); + + // Update the return type parameter if the output type has changed. + if (RetTy) { + // A boolean result can be represented as an integer type in C/C++, but at + // this point we only care about the SMT sorts. Set it as a boolean type + // to avoid subsequent SMT errors. + if (BinaryOperator::isComparisonOp(Op) || + BinaryOperator::isLogicalOp(Op)) { + *RetTy = Ctx.BoolTy; + } else { + *RetTy = LTy; + } + + // If the two operands are pointers and the operation is a subtraction, + // the result is of type ptrdiff_t, which is signed + if (LTy->isAnyPointerType() && RTy->isAnyPointerType() && Op == BO_Sub) { + *RetTy = Ctx.getPointerDiffType(); + } + } + + return LTy->isRealFloatingType() + ? fromFloatBinOp(NewLHS, Op, NewRHS) + : fromBinOp(NewLHS, Op, NewRHS, + LTy->isSignedIntegerOrEnumerationType()); + } + + // Wrapper to generate SMTExprRef from a range. If From == To, an equality + // will be created instead. + SMTExprRef getRangeExpr(ASTContext &Ctx, SymbolRef Sym, + const llvm::APSInt &From, const llvm::APSInt &To, + bool InRange) { + // Convert lower bound + QualType FromTy; + llvm::APSInt NewFromInt; + std::tie(NewFromInt, FromTy) = fixAPSInt(Ctx, From); + SMTExprRef FromExp = fromAPSInt(NewFromInt); + + // Convert symbol + QualType SymTy; + SMTExprRef Exp = getExpr(Ctx, Sym, &SymTy); + + // Construct single (in)equality + if (From == To) + return getBinExpr(Ctx, Exp, SymTy, InRange ? BO_EQ : BO_NE, FromExp, + FromTy, /*RetTy=*/nullptr); + + QualType ToTy; + llvm::APSInt NewToInt; + std::tie(NewToInt, ToTy) = fixAPSInt(Ctx, To); + SMTExprRef ToExp = fromAPSInt(NewToInt); + assert(FromTy == ToTy && "Range values have different types!"); + + // Construct two (in)equalities, and a logical and/or + SMTExprRef LHS = getBinExpr(Ctx, Exp, SymTy, InRange ? BO_GE : BO_LT, + FromExp, FromTy, /*RetTy=*/nullptr); + SMTExprRef RHS = + getBinExpr(Ctx, Exp, SymTy, InRange ? BO_LE : BO_GT, ToExp, ToTy, + /*RetTy=*/nullptr); + + return fromBinOp(LHS, InRange ? BO_LAnd : BO_LOr, RHS, + SymTy->isSignedIntegerOrEnumerationType()); + } + + // Recover the QualType of an APSInt. + // TODO: Refactor to put elsewhere + QualType getAPSIntType(ASTContext &Ctx, const llvm::APSInt &Int) { + return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned()); + } + + // Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1. + std::pair<llvm::APSInt, QualType> fixAPSInt(ASTContext &Ctx, + const llvm::APSInt &Int) { + llvm::APSInt NewInt; + + // FIXME: This should be a cast from a 1-bit integer type to a boolean type, + // but the former is not available in Clang. Instead, extend the APSInt + // directly. + if (Int.getBitWidth() == 1 && getAPSIntType(Ctx, Int).isNull()) { + NewInt = Int.extend(Ctx.getTypeSize(Ctx.BoolTy)); + } else + NewInt = Int; + + return std::make_pair(NewInt, getAPSIntType(Ctx, NewInt)); + } + + // Perform implicit type conversion on binary symbolic expressions. + // May modify all input parameters. + // TODO: Refactor to use built-in conversion functions + void doTypeConversion(ASTContext &Ctx, SMTExprRef &LHS, SMTExprRef &RHS, + QualType <y, QualType &RTy) { + assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!"); + + // Perform type conversion + if ((LTy->isIntegralOrEnumerationType() && + RTy->isIntegralOrEnumerationType()) && + (LTy->isArithmeticType() && RTy->isArithmeticType())) { + doIntTypeConversion<SMTExprRef, &SMTSolver::fromCast>(Ctx, LHS, LTy, RHS, + RTy); + return; + } + + if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) { + doFloatTypeConversion<SMTExprRef, &SMTSolver::fromCast>(Ctx, LHS, LTy, + RHS, RTy); + return; + } + + if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) || + (LTy->isBlockPointerType() || RTy->isBlockPointerType()) || + (LTy->isReferenceType() || RTy->isReferenceType())) { + // TODO: Refactor to Sema::FindCompositePointerType(), and + // Sema::CheckCompareOperands(). + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + // Cast the non-pointer type to the pointer type. + // TODO: Be more strict about this. + if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) || + (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) || + (LTy->isReferenceType() ^ RTy->isReferenceType())) { + if (LTy->isNullPtrType() || LTy->isBlockPointerType() || + LTy->isReferenceType()) { + LHS = fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } else { + RHS = fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } + } + + // Cast the void pointer type to the non-void pointer type. + // For void types, this assumes that the casted value is equal to the + // value of the original pointer, and does not account for alignment + // requirements. + if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) { + assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) && + "Pointer types have different bitwidths!"); + if (RTy->isVoidPointerType()) + RTy = LTy; + else + LTy = RTy; + } + + if (LTy == RTy) + return; + } + + // Fallback: for the solver, assume that these types don't really matter + if ((LTy.getCanonicalType() == RTy.getCanonicalType()) || + (LTy->isObjCObjectPointerType() && RTy->isObjCObjectPointerType())) { + LTy = RTy; + return; + } + + // TODO: Refine behavior for invalid type casts + } + + // Perform implicit integer type conversion. + // May modify all input parameters. + // TODO: Refactor to use Sema::handleIntegerConversion() + template <typename T, T (SMTSolver::*doCast)(const T &, QualType, uint64_t, + QualType, uint64_t)> + void doIntTypeConversion(ASTContext &Ctx, T &LHS, QualType <y, T &RHS, + QualType &RTy) { + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!"); + // Always perform integer promotion before checking type equality. + // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion + if (LTy->isPromotableIntegerType()) { + QualType NewTy = Ctx.getPromotedIntegerType(LTy); + uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); + LHS = (this->*doCast)(LHS, NewTy, NewBitWidth, LTy, LBitWidth); + LTy = NewTy; + LBitWidth = NewBitWidth; + } + if (RTy->isPromotableIntegerType()) { + QualType NewTy = Ctx.getPromotedIntegerType(RTy); + uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); + RHS = (this->*doCast)(RHS, NewTy, NewBitWidth, RTy, RBitWidth); + RTy = NewTy; + RBitWidth = NewBitWidth; + } + + if (LTy == RTy) + return; + + // Perform integer type conversion + // Note: Safe to skip updating bitwidth because this must terminate + bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType(); + bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType(); + + int order = Ctx.getIntegerTypeOrder(LTy, RTy); + if (isLSignedTy == isRSignedTy) { + // Same signedness; use the higher-ranked type + if (order == 1) { + RHS = (this->*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (this->*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else if (order != (isLSignedTy ? 1 : -1)) { + // The unsigned type has greater than or equal rank to the + // signed type, so use the unsigned type + if (isRSignedTy) { + RHS = (this->*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (this->*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else if (LBitWidth != RBitWidth) { + // The two types are different widths; if we are here, that + // means the signed type is larger than the unsigned type, so + // use the signed type. + if (isLSignedTy) { + RHS = (this->*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (this->*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else { + // The signed type is higher-ranked than the unsigned type, + // but isn't actually any bigger (like unsigned int and long + // on most 32-bit systems). Use the unsigned type corresponding + // to the signed type. + QualType NewTy = + Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy); + RHS = (this->*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = NewTy; + LHS = (this->*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = NewTy; + } + } + + // Perform implicit floating-point type conversion. + // May modify all input parameters. + // TODO: Refactor to use Sema::handleFloatConversion() + template <typename T, T (SMTSolver::*doCast)(const T &, QualType, uint64_t, + QualType, uint64_t)> + void doFloatTypeConversion(ASTContext &Ctx, T &LHS, QualType <y, T &RHS, + QualType &RTy) { + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + // Perform float-point type promotion + if (!LTy->isRealFloatingType()) { + LHS = (this->*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + LBitWidth = RBitWidth; + } + if (!RTy->isRealFloatingType()) { + RHS = (this->*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + RBitWidth = LBitWidth; + } + + if (LTy == RTy) + return; + + // If we have two real floating types, convert the smaller operand to the + // bigger result + // Note: Safe to skip updating bitwidth because this must terminate + int order = Ctx.getFloatingTypeOrder(LTy, RTy); + if (order > 0) { + RHS = (this->*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else if (order == 0) { + LHS = (this->*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } else { + llvm_unreachable("Unsupported floating-point type cast!"); + } + } + + // Returns a boolean sort. + virtual SMTSortRef getBoolSort() = 0; + + // Returns an appropriate bitvector sort for the given bitwidth. + virtual SMTSortRef getBitvectorSort(const unsigned BitWidth) = 0; + + // Returns a floating-point sort of width 16 + virtual SMTSortRef getFloat16Sort() = 0; + + // Returns a floating-point sort of width 32 + virtual SMTSortRef getFloat32Sort() = 0; + + // Returns a floating-point sort of width 64 + virtual SMTSortRef getFloat64Sort() = 0; + + // Returns a floating-point sort of width 128 + virtual SMTSortRef getFloat128Sort() = 0; + + // Returns an appropriate sort for the given AST. + virtual SMTSortRef getSort(const SMTExprRef &AST) = 0; + + // Returns a new SMTExprRef from an SMTExpr + virtual SMTExprRef newExprRef(const SMTExpr &E) const = 0; + + /// Given a constraint, adds it to the solver + virtual void addConstraint(const SMTExprRef &Exp) const = 0; + + /// Creates a bitvector addition operation + virtual SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector subtraction operation + virtual SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector multiplication operation + virtual SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed modulus operation + virtual SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned modulus operation + virtual SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed division operation + virtual SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned division operation + virtual SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector logical shift left operation + virtual SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector arithmetic shift right operation + virtual SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector logical shift right operation + virtual SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector negation operation + virtual SMTExprRef mkBVNeg(const SMTExprRef &Exp) = 0; + + /// Creates a bitvector not operation + virtual SMTExprRef mkBVNot(const SMTExprRef &Exp) = 0; + + /// Creates a bitvector xor operation + virtual SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector or operation + virtual SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector and operation + virtual SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned less-than operation + virtual SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed less-than operation + virtual SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned greater-than operation + virtual SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed greater-than operation + virtual SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned less-equal-than operation + virtual SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed less-equal-than operation + virtual SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned greater-equal-than operation + virtual SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed greater-equal-than operation + virtual SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean not operation + virtual SMTExprRef mkNot(const SMTExprRef &Exp) = 0; + + /// Creates a boolean equality operation + virtual SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean and operation + virtual SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean or operation + virtual SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean ite operation + virtual SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T, + const SMTExprRef &F) = 0; + + /// Creates a bitvector sign extension operation + virtual SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) = 0; + + /// Creates a bitvector zero extension operation + virtual SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) = 0; + + /// Creates a bitvector extract operation + virtual SMTExprRef mkBVExtract(unsigned High, unsigned Low, + const SMTExprRef &Exp) = 0; + + /// Creates a bitvector concat operation + virtual SMTExprRef mkBVConcat(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a floating-point negation operation + virtual SMTExprRef mkFPNeg(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isInfinite operation + virtual SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isNaN operation + virtual SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isNormal operation + virtual SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isZero operation + virtual SMTExprRef mkFPIsZero(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point multiplication operation + virtual SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point division operation + virtual SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point remainder operation + virtual SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point addition operation + virtual SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point subtraction operation + virtual SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point less-than operation + virtual SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point greater-than operation + virtual SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point less-than-or-equal operation + virtual SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point greater-than-or-equal operation + virtual SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point equality operation + virtual SMTExprRef mkFPEqual(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a floating-point conversion from floatint-point to floating-point + /// operation + virtual SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from floatint-point to signed + /// bitvector operation + virtual SMTExprRef mkFPtoSBV(const SMTExprRef &From, + const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from floatint-point to unsigned + /// bitvector operation + virtual SMTExprRef mkFPtoUBV(const SMTExprRef &From, + const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from signed bitvector to + /// floatint-point operation + virtual SMTExprRef mkSBVtoFP(const SMTExprRef &From, unsigned ToWidth) = 0; + + /// Creates a floating-point conversion from unsigned bitvector to + /// floatint-point operation + virtual SMTExprRef mkUBVtoFP(const SMTExprRef &From, unsigned ToWidth) = 0; + + /// Creates a new symbol, given a name and a sort + virtual SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) = 0; + + // Returns an appropriate floating-point rounding mode. + virtual SMTExprRef getFloatRoundingMode() = 0; + + // If the a model is available, returns the value of a given bitvector symbol + virtual llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth, + bool isUnsigned) = 0; + + // If the a model is available, returns the value of a given boolean symbol + virtual bool getBoolean(const SMTExprRef &Exp) = 0; + + /// Constructs an SMTExprRef from a boolean. + virtual SMTExprRef mkBoolean(const bool b) = 0; + + /// Constructs an SMTExprRef from a finite APFloat. + virtual SMTExprRef mkFloat(const llvm::APFloat Float) = 0; + + /// Constructs an SMTExprRef from an APSInt and its bit width + virtual SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) = 0; + + /// Given an expression, extract the value of this operand in the model. + virtual bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) = 0; + + /// Given an expression extract the value of this operand in the model. + virtual bool getInterpretation(const SMTExprRef &Exp, + llvm::APFloat &Float) = 0; + + /// Construct an SMTExprRef value from a boolean. + virtual SMTExprRef fromBoolean(const bool Bool) = 0; + + /// Construct an SMTExprRef value from a finite APFloat. + virtual SMTExprRef fromAPFloat(const llvm::APFloat &Float) = 0; + + /// Construct an SMTExprRef value from an APSInt. + virtual SMTExprRef fromAPSInt(const llvm::APSInt &Int) = 0; + + /// Construct an SMTExprRef value from an integer. + virtual SMTExprRef fromInt(const char *Int, uint64_t BitWidth) = 0; + + /// Construct an SMTExprRef from a SymbolData. + virtual SMTExprRef fromData(const SymbolID ID, const QualType &Ty, + uint64_t BitWidth) = 0; + + /// Check if the constraints are satisfiable + virtual ConditionTruthVal check() const = 0; + + /// Push the current solver state + virtual void push() = 0; + + /// Pop the previous solver state + virtual void pop(unsigned NumStates = 1) = 0; + + /// Reset the solver and remove all constraints. + virtual void reset() const = 0; + + virtual void print(raw_ostream &OS) const = 0; +}; + +/// Shared pointer for SMTSolvers. +using SMTSolverRef = std::shared_ptr<SMTSolver>; + +/// Convenience method to create and Z3Solver object +std::unique_ptr<SMTSolver> CreateZ3Solver(); + +} // namespace ento +} // namespace clang + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SMTSort.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTSort.h new file mode 100644 index 0000000000000..41ef573f0c713 --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SMTSort.h @@ -0,0 +1,91 @@ +//== SMTSort.h --------------------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic Sort API, which will be the base class +// for every SMT solver sort specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTSORT_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTSORT_H + +#include "clang/Basic/TargetInfo.h" + +namespace clang { +namespace ento { + +/// Generic base class for SMT sorts +class SMTSort { +public: + SMTSort() = default; + virtual ~SMTSort() = default; + + /// Returns true if the sort is a bitvector, calls isBitvectorSortImpl(). + virtual bool isBitvectorSort() const { return isBitvectorSortImpl(); } + + /// Returns true if the sort is a floating-point, calls isFloatSortImpl(). + virtual bool isFloatSort() const { return isFloatSortImpl(); } + + /// Returns true if the sort is a boolean, calls isBooleanSortImpl(). + virtual bool isBooleanSort() const { return isBooleanSortImpl(); } + + /// Returns the bitvector size, fails if the sort is not a bitvector + /// Calls getBitvectorSortSizeImpl(). + virtual unsigned getBitvectorSortSize() const { + assert(isBitvectorSort() && "Not a bitvector sort!"); + unsigned Size = getBitvectorSortSizeImpl(); + assert(Size && "Size is zero!"); + return Size; + }; + + /// Returns the floating-point size, fails if the sort is not a floating-point + /// Calls getFloatSortSizeImpl(). + virtual unsigned getFloatSortSize() const { + assert(isFloatSort() && "Not a floating-point sort!"); + unsigned Size = getFloatSortSizeImpl(); + assert(Size && "Size is zero!"); + return Size; + }; + + friend bool operator==(SMTSort const &LHS, SMTSort const &RHS) { + return LHS.equal_to(RHS); + } + + virtual void print(raw_ostream &OS) const = 0; + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } + +protected: + /// Query the SMT solver and returns true if two sorts are equal (same kind + /// and bit width). This does not check if the two sorts are the same objects. + virtual bool equal_to(SMTSort const &other) const = 0; + + /// Query the SMT solver and checks if a sort is bitvector. + virtual bool isBitvectorSortImpl() const = 0; + + /// Query the SMT solver and checks if a sort is floating-point. + virtual bool isFloatSortImpl() const = 0; + + /// Query the SMT solver and checks if a sort is boolean. + virtual bool isBooleanSortImpl() const = 0; + + /// Query the SMT solver and returns the sort bit width. + virtual unsigned getBitvectorSortSizeImpl() const = 0; + + /// Query the SMT solver and returns the sort bit width. + virtual unsigned getFloatSortSizeImpl() const = 0; +}; + +/// Shared pointer for SMTSorts, used by SMTSolver API. +using SMTSortRef = std::shared_ptr<SMTSort>; + +} // namespace ento +} // namespace clang + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index d58d0a690c88e..31300d69f2ff7 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -16,21 +16,43 @@ #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALBUILDER_H #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/Optional.h" +#include <cstdint> namespace clang { +class BlockDecl; class CXXBoolLiteralExpr; +class CXXMethodDecl; +class CXXRecordDecl; +class DeclaratorDecl; +class FunctionDecl; +class LocationContext; +class StackFrameContext; +class Stmt; namespace ento { +class ConditionTruthVal; +class ProgramStateManager; +class StoreRef; + class SValBuilder { virtual void anchor(); + protected: ASTContext &Context; @@ -62,14 +84,12 @@ public: public: SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, ProgramStateManager &stateMgr) - : Context(context), BasicVals(context, alloc), - SymMgr(context, BasicVals, alloc), - MemMgr(context, alloc), - StateMgr(stateMgr), - ArrayIndexTy(context.LongLongTy), - ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {} + : Context(context), BasicVals(context, alloc), + SymMgr(context, BasicVals, alloc), MemMgr(context, alloc), + StateMgr(stateMgr), ArrayIndexTy(context.LongLongTy), + ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {} - virtual ~SValBuilder() {} + virtual ~SValBuilder() = default; bool haveSameType(const SymExpr *Sym1, const SymExpr *Sym2) { return haveSameType(Sym1->getType(), Sym2->getType()); @@ -124,7 +144,12 @@ public: SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type); - + + /// \return Whether values in \p lhs and \p rhs are equal at \p state. + ConditionTruthVal areEqual(ProgramStateRef state, SVal lhs, SVal rhs); + + SVal evalEQ(ProgramStateRef state, SVal lhs, SVal rhs); + DefinedOrUnknownSVal evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs); @@ -173,7 +198,7 @@ public: /// Make a unique symbol for value of region. DefinedOrUnknownSVal getRegionValueSymbolVal(const TypedValueRegion *region); - /// \brief Create a new symbol with a unique 'name'. + /// Create a new symbol with a unique 'name'. /// /// We resort to conjured symbols when we cannot construct a derived symbol. /// The advantage of symbols derived/built from other symbols is that we @@ -188,12 +213,12 @@ public: const LocationContext *LCtx, QualType type, unsigned count); - DefinedOrUnknownSVal conjureSymbolVal(const Stmt *stmt, const LocationContext *LCtx, QualType type, unsigned visitCount); - /// \brief Conjure a symbol representing heap allocated memory region. + + /// Conjure a symbol representing heap allocated memory region. /// /// Note, the expression should represent a location. DefinedOrUnknownSVal getConjuredHeapSymbolVal(const Expr *E, @@ -304,7 +329,7 @@ public: NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type); - /// \brief Create a NonLoc value for cast. + /// Create a NonLoc value for cast. NonLoc makeNonLoc(const SymExpr *operand, QualType fromTy, QualType toTy); nonloc::ConcreteInt makeTruthVal(bool b, QualType type) { @@ -342,6 +367,15 @@ public: return loc::ConcreteInt(BasicVals.getValue(integer)); } + /// Make an SVal that represents the given symbol. This follows the convention + /// of representing Loc-type symbols (symbolic pointers and references) + /// as Loc values wrapping the symbol rather than as plain symbol values. + SVal makeSymbolVal(SymbolRef Sym) { + if (Loc::isLocType(Sym->getType())) + return makeLoc(Sym); + return nonloc::SymbolVal(Sym); + } + /// Return a memory region for the 'this' object reference. loc::MemRegionVal getCXXThis(const CXXMethodDecl *D, const StackFrameContext *SFC); @@ -355,8 +389,8 @@ SValBuilder* createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, ProgramStateManager &stateMgr); -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALBUILDER_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index 06132587b4aaf..0fde63e9eb09b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -1,4 +1,4 @@ -//== SVals.h - Abstract Values for Static Analysis ---------*- C++ -*--==// +//===- SVals.h - Abstract Values for Static Analysis ------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -16,11 +16,18 @@ #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H #include "clang/AST/Expr.h" +#include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <cstdint> +#include <utility> //==------------------------------------------------------------------------==// // Base SVal types. @@ -28,34 +35,40 @@ namespace clang { +class CXXBaseSpecifier; +class DeclaratorDecl; +class FunctionDecl; +class LabelDecl; + namespace ento { +class BasicValueFactory; class CompoundValData; class LazyCompoundValData; -class PointerToMemberData; -class ProgramState; -class BasicValueFactory; class MemRegion; -class TypedValueRegion; -class MemRegionManager; -class ProgramStateManager; +class PointerToMemberData; class SValBuilder; +class TypedValueRegion; namespace nonloc { + /// Sub-kinds for NonLoc values. enum Kind { #define NONLOC_SVAL(Id, Parent) Id ## Kind, #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" }; -} + +} // namespace nonloc namespace loc { + /// Sub-kinds for Loc values. enum Kind { #define LOC_SVAL(Id, Parent) Id ## Kind, #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" }; -} + +} // namespace loc /// SVal - This represents a symbolic expression, which can be either /// an L-value or an R-value. @@ -71,22 +84,21 @@ public: enum { BaseBits = 2, BaseMask = 0x3 }; protected: - const void *Data; + const void *Data = nullptr; /// The lowest 2 bits are a BaseKind (0 -- 3). /// The higher bits are an unsigned "kind" value. - unsigned Kind; + unsigned Kind = 0; explicit SVal(const void *d, bool isLoc, unsigned ValKind) - : Data(d), Kind((isLoc ? LocKind : NonLocKind) | (ValKind << BaseBits)) {} + : Data(d), Kind((isLoc ? LocKind : NonLocKind) | (ValKind << BaseBits)) {} - explicit SVal(BaseKind k, const void *D = nullptr) - : Data(D), Kind(k) {} + explicit SVal(BaseKind k, const void *D = nullptr) : Data(D), Kind(k) {} public: - explicit SVal() : Data(nullptr), Kind(0) {} + explicit SVal() = default; - /// \brief Convert to the specified SVal type, asserting that this SVal is of + /// Convert to the specified SVal type, asserting that this SVal is of /// the desired type. template<typename T> T castAs() const { @@ -94,7 +106,7 @@ public: return *static_cast<const T *>(this); } - /// \brief Convert to the specified SVal type, returning None if this SVal is + /// Convert to the specified SVal type, returning None if this SVal is /// not of the desired type. template<typename T> Optional<T> getAs() const { @@ -103,38 +115,38 @@ public: return *static_cast<const T *>(this); } - inline unsigned getRawKind() const { return Kind; } - inline BaseKind getBaseKind() const { return (BaseKind) (Kind & BaseMask); } - inline unsigned getSubKind() const { return (Kind & ~BaseMask) >> BaseBits; } + unsigned getRawKind() const { return Kind; } + BaseKind getBaseKind() const { return (BaseKind) (Kind & BaseMask); } + unsigned getSubKind() const { return (Kind & ~BaseMask) >> BaseBits; } // This method is required for using SVal in a FoldingSetNode. It // extracts a unique signature for this SVal object. - inline void Profile(llvm::FoldingSetNodeID& ID) const { + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger((unsigned) getRawKind()); ID.AddPointer(Data); } - inline bool operator==(const SVal& R) const { + bool operator==(const SVal &R) const { return getRawKind() == R.getRawKind() && Data == R.Data; } - inline bool operator!=(const SVal& R) const { + bool operator!=(const SVal &R) const { return !(*this == R); } - inline bool isUnknown() const { + bool isUnknown() const { return getRawKind() == UnknownValKind; } - inline bool isUndef() const { + bool isUndef() const { return getRawKind() == UndefinedValKind; } - inline bool isUnknownOrUndef() const { + bool isUnknownOrUndef() const { return getRawKind() <= UnknownValKind; } - inline bool isValid() const { + bool isValid() const { return getRawKind() > UnknownValKind; } @@ -152,7 +164,7 @@ public: /// Otherwise return 0. const FunctionDecl *getAsFunctionDecl() const; - /// \brief If this SVal is a location and wraps a symbol, return that + /// If this SVal is a location and wraps a symbol, return that /// SymbolRef. Otherwise return 0. /// /// Casts are ignored during lookup. @@ -163,7 +175,7 @@ public: /// Get the symbol in the SVal or its base region. SymbolRef getLocSymbolInBase() const; - /// \brief If this SVal wraps a symbol return that SymbolRef. + /// If this SVal wraps a symbol return that SymbolRef. /// Otherwise, return 0. /// /// Casts are ignored during lookup. @@ -175,7 +187,7 @@ public: /// return that expression. Otherwise return NULL. const SymExpr *getAsSymbolicExpression() const; - const SymExpr* getAsSymExpr() const; + const SymExpr *getAsSymExpr() const; const MemRegion *getAsRegion() const; @@ -183,7 +195,7 @@ public: void dump() const; SymExpr::symbol_iterator symbol_begin() const { - const SymExpr *SE = getAsSymbolicExpression(); + const SymExpr *SE = getAsSymbol(/*IncludeBaseRegions=*/true); if (SE) return SE->symbol_begin(); else @@ -206,28 +218,28 @@ public: private: friend class SVal; + static bool isKind(const SVal& V) { return V.getBaseKind() == UndefinedValKind; } }; class DefinedOrUnknownSVal : public SVal { -private: +public: // We want calling these methods to be a compiler error since they are // tautologically false. bool isUndef() const = delete; bool isValid() const = delete; protected: - DefinedOrUnknownSVal() {} + DefinedOrUnknownSVal() = default; explicit DefinedOrUnknownSVal(const void *d, bool isLoc, unsigned ValKind) - : SVal(d, isLoc, ValKind) {} - - explicit DefinedOrUnknownSVal(BaseKind k, void *D = nullptr) - : SVal(k, D) {} + : SVal(d, isLoc, ValKind) {} + explicit DefinedOrUnknownSVal(BaseKind k, void *D = nullptr) : SVal(k, D) {} private: friend class SVal; + static bool isKind(const SVal& V) { return !V.isUndef(); } @@ -239,37 +251,43 @@ public: private: friend class SVal; + static bool isKind(const SVal &V) { return V.getBaseKind() == UnknownValKind; } }; class DefinedSVal : public DefinedOrUnknownSVal { -private: +public: // We want calling these methods to be a compiler error since they are // tautologically true/false. bool isUnknown() const = delete; bool isUnknownOrUndef() const = delete; bool isValid() const = delete; + protected: - DefinedSVal() {} + DefinedSVal() = default; explicit DefinedSVal(const void *d, bool isLoc, unsigned ValKind) - : DefinedOrUnknownSVal(d, isLoc, ValKind) {} + : DefinedOrUnknownSVal(d, isLoc, ValKind) {} + private: friend class SVal; + static bool isKind(const SVal& V) { return !V.isUnknownOrUndef(); } }; - -/// \brief Represents an SVal that is guaranteed to not be UnknownVal. +/// Represents an SVal that is guaranteed to not be UnknownVal. class KnownSVal : public SVal { - KnownSVal() {} friend class SVal; + + KnownSVal() = default; + static bool isKind(const SVal &V) { return !V.isUnknown(); } + public: KnownSVal(const DefinedSVal &V) : SVal(V) {} KnownSVal(const UndefinedVal &V) : SVal(V) {} @@ -277,20 +295,21 @@ public: class NonLoc : public DefinedSVal { protected: - NonLoc() {} + NonLoc() = default; explicit NonLoc(unsigned SubKind, const void *d) - : DefinedSVal(d, false, SubKind) {} + : DefinedSVal(d, false, SubKind) {} public: void dumpToStream(raw_ostream &Out) const; - static inline bool isCompoundType(QualType T) { + static bool isCompoundType(QualType T) { return T->isArrayType() || T->isRecordType() || T->isComplexType() || T->isVectorType(); } private: friend class SVal; + static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind; } @@ -298,20 +317,21 @@ private: class Loc : public DefinedSVal { protected: - Loc() {} + Loc() = default; explicit Loc(unsigned SubKind, const void *D) - : DefinedSVal(const_cast<void*>(D), true, SubKind) {} + : DefinedSVal(const_cast<void *>(D), true, SubKind) {} public: void dumpToStream(raw_ostream &Out) const; - static inline bool isLocType(QualType T) { + static bool isLocType(QualType T) { return T->isAnyPointerType() || T->isBlockPointerType() || T->isReferenceType() || T->isNullPtrType(); } private: friend class SVal; + static bool isKind(const SVal& V) { return V.getBaseKind() == LocKind; } @@ -323,14 +343,17 @@ private: namespace nonloc { -/// \brief Represents symbolic expression. +/// Represents symbolic expression that isn't a location. class SymbolVal : public NonLoc { public: SymbolVal() = delete; - SymbolVal(SymbolRef sym) : NonLoc(SymbolValKind, sym) { assert(sym); } + SymbolVal(SymbolRef sym) : NonLoc(SymbolValKind, sym) { + assert(sym); + assert(!Loc::isLocType(sym->getType())); + } SymbolRef getSymbol() const { - return (const SymExpr*) Data; + return (const SymExpr *) Data; } bool isExpression() const { @@ -339,6 +362,7 @@ public: private: friend class SVal; + static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == SymbolValKind; @@ -349,13 +373,13 @@ private: } }; -/// \brief Value representing integer constant. +/// Value representing integer constant. class ConcreteInt : public NonLoc { public: explicit ConcreteInt(const llvm::APSInt& V) : NonLoc(ConcreteIntKind, &V) {} const llvm::APSInt& getValue() const { - return *static_cast<const llvm::APSInt*>(Data); + return *static_cast<const llvm::APSInt *>(Data); } // Transfer functions for binary/unary operations on ConcreteInts. @@ -368,7 +392,9 @@ public: private: friend class SVal; - ConcreteInt() {} + + ConcreteInt() = default; + static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == ConcreteIntKind; @@ -392,7 +418,6 @@ class LocAsInteger : public NonLoc { } public: - Loc getLoc() const { const std::pair<SVal, uintptr_t> *D = static_cast<const std::pair<SVal, uintptr_t> *>(Data); @@ -414,7 +439,9 @@ public: private: friend class SVal; - LocAsInteger() {} + + LocAsInteger() = default; + static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == LocAsIntegerKind; @@ -432,16 +459,19 @@ class CompoundVal : public NonLoc { public: const CompoundValData* getValue() const { - return static_cast<const CompoundValData*>(Data); + return static_cast<const CompoundValData *>(Data); } - typedef llvm::ImmutableList<SVal>::iterator iterator; + using iterator = llvm::ImmutableList<SVal>::iterator; + iterator begin() const; iterator end() const; private: friend class SVal; - CompoundVal() {} + + CompoundVal() = default; + static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == CompoundValKind; } @@ -455,30 +485,35 @@ class LazyCompoundVal : public NonLoc { friend class ento::SValBuilder; explicit LazyCompoundVal(const LazyCompoundValData *D) - : NonLoc(LazyCompoundValKind, D) {} + : NonLoc(LazyCompoundValKind, D) {} + public: const LazyCompoundValData *getCVData() const { - return static_cast<const LazyCompoundValData*>(Data); + return static_cast<const LazyCompoundValData *>(Data); } + const void *getStore() const; const TypedValueRegion *getRegion() const; private: friend class SVal; - LazyCompoundVal() {} + + LazyCompoundVal() = default; + static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == LazyCompoundValKind; } + static bool isKind(const NonLoc& V) { return V.getSubKind() == LazyCompoundValKind; } }; -/// \brief Value representing pointer-to-member. +/// Value representing pointer-to-member. /// /// This value is qualified as NonLoc because neither loading nor storing -/// operations are aplied to it. Instead, the analyzer uses the L-value coming +/// operations are applied to it. Instead, the analyzer uses the L-value coming /// from pointer-to-member applied to an object. /// This SVal is represented by a DeclaratorDecl which can be a member function /// pointer or a member data pointer and a list of CXXBaseSpecifiers. This list @@ -488,28 +523,36 @@ class PointerToMember : public NonLoc { friend class ento::SValBuilder; public: - typedef llvm::PointerUnion<const DeclaratorDecl *, - const PointerToMemberData *> PTMDataType; + using PTMDataType = + llvm::PointerUnion<const DeclaratorDecl *, const PointerToMemberData *>; + const PTMDataType getPTMData() const { return PTMDataType::getFromOpaqueValue(const_cast<void *>(Data)); } + bool isNullMemberPointer() const { return getPTMData().isNull(); } + const DeclaratorDecl *getDecl() const; + template<typename AdjustedDecl> - const AdjustedDecl* getDeclAs() const { + const AdjustedDecl *getDeclAs() const { return dyn_cast_or_null<AdjustedDecl>(getDecl()); } - typedef llvm::ImmutableList<const CXXBaseSpecifier *>::iterator iterator; + + using iterator = llvm::ImmutableList<const CXXBaseSpecifier *>::iterator; + iterator begin() const; iterator end() const; private: - explicit PointerToMember(const PTMDataType D) - : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {} friend class SVal; - PointerToMember() {} + + PointerToMember() = default; + explicit PointerToMember(const PTMDataType D) + : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {} + static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == PointerToMemberKind; @@ -520,7 +563,7 @@ private: } }; -} // end namespace ento::nonloc +} // namespace nonloc //==------------------------------------------------------------------------==// // Subclasses of Loc. @@ -535,12 +578,14 @@ public: } const LabelDecl *getLabel() const { - return static_cast<const LabelDecl*>(Data); + return static_cast<const LabelDecl *>(Data); } private: friend class SVal; - GotoLabel() {} + + GotoLabel() = default; + static bool isKind(const SVal& V) { return V.getBaseKind() == LocKind && V.getSubKind() == GotoLabelKind; } @@ -550,19 +595,18 @@ private: } }; - class MemRegionVal : public Loc { public: explicit MemRegionVal(const MemRegion* r) : Loc(MemRegionValKind, r) { assert(r); } - /// \brief Get the underlining region. - const MemRegion* getRegion() const { - return static_cast<const MemRegion*>(Data); + /// Get the underlining region. + const MemRegion *getRegion() const { + return static_cast<const MemRegion *>(Data); } - /// \brief Get the underlining region and strip casts. + /// Get the underlining region and strip casts. const MemRegion* stripCasts(bool StripBaseCasts = true) const; template <typename REGION> @@ -570,17 +614,19 @@ public: return dyn_cast<REGION>(getRegion()); } - inline bool operator==(const MemRegionVal& R) const { + bool operator==(const MemRegionVal &R) const { return getRegion() == R.getRegion(); } - inline bool operator!=(const MemRegionVal& R) const { + bool operator!=(const MemRegionVal &R) const { return getRegion() != R.getRegion(); } private: friend class SVal; - MemRegionVal() {} + + MemRegionVal() = default; + static bool isKind(const SVal& V) { return V.getBaseKind() == LocKind && V.getSubKind() == MemRegionValKind; @@ -595,8 +641,8 @@ class ConcreteInt : public Loc { public: explicit ConcreteInt(const llvm::APSInt& V) : Loc(ConcreteIntKind, &V) {} - const llvm::APSInt& getValue() const { - return *static_cast<const llvm::APSInt*>(Data); + const llvm::APSInt &getValue() const { + return *static_cast<const llvm::APSInt *>(Data); } // Transfer functions for binary/unary operations on ConcreteInts. @@ -605,7 +651,9 @@ public: private: friend class SVal; - ConcreteInt() {} + + ConcreteInt() = default; + static bool isKind(const SVal& V) { return V.getBaseKind() == LocKind && V.getSubKind() == ConcreteIntKind; @@ -616,11 +664,11 @@ private: } }; -} // end ento::loc namespace +} // namespace loc -} // end ento namespace +} // namespace ento -} // end clang namespace +} // namespace clang namespace llvm { @@ -629,6 +677,6 @@ template <> struct isPodLike<clang::ento::SVal> { static const bool value = true; }; -} // end llvm namespace +} // namespace llvm -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h index 2c9802bbc5360..d64b90e2d380a 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h @@ -75,6 +75,7 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// + SValBuilder &getSValBuilder() const { return SVB; } BasicValueFactory &getBasicVals() const { return SVB.getBasicValueFactory(); } SymbolManager &getSymbolManager() const { return SVB.getSymbolManager(); } @@ -85,8 +86,8 @@ private: bool Assumption); }; -} // end GR namespace +} // end namespace ento -} // end clang namespace +} // end namespace clang #endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index 25d20a17afaa7..dc0644df8aabe 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -1,4 +1,4 @@ -//== Store.h - Interface for maps from Locations to Values ------*- C++ -*--==// +//===- Store.h - Interface for maps from Locations to Values ----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -14,27 +14,42 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STORE_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STORE_H +#include "clang/AST/Type.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include <cassert> +#include <cstdint> +#include <memory> namespace clang { -class Stmt; +class ASTContext; +class CastExpr; +class CompoundLiteralExpr; +class CXXBasePath; +class Decl; class Expr; +class LocationContext; class ObjCIvarDecl; -class CXXBasePath; class StackFrameContext; namespace ento { class CallEvent; -class ProgramState; class ProgramStateManager; class ScanReachableSymbols; +class SymbolReaper; -typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols; +using InvalidatedSymbols = llvm::DenseSet<SymbolRef>; class StoreManager { protected: @@ -48,7 +63,7 @@ protected: StoreManager(ProgramStateManager &stateMgr); public: - virtual ~StoreManager() {} + virtual ~StoreManager() = default; /// Return the value bound to specified location in a given state. /// \param[in] store The store in which to make the lookup. @@ -92,9 +107,17 @@ public: /// by \c val bound to the location given for \c loc. virtual StoreRef Bind(Store store, Loc loc, SVal val) = 0; - virtual StoreRef BindDefault(Store store, const MemRegion *R, SVal V); + /// Return a store with the specified value bound to all sub-regions of the + /// region. The region must not have previous bindings. If you need to + /// invalidate existing bindings, consider invalidateRegions(). + virtual StoreRef BindDefaultInitial(Store store, const MemRegion *R, + SVal V) = 0; - /// \brief Create a new store with the specified binding removed. + /// Return a store with in which all values within the given region are + /// reset to zero. This method is allowed to overwrite previous bindings. + virtual StoreRef BindDefaultZero(Store store, const MemRegion *R) = 0; + + /// Create a new store with the specified binding removed. /// \param ST the original store, that is the basis for the new store. /// \param L the location whose binding should be removed. virtual StoreRef killBinding(Store ST, Loc L) = 0; @@ -147,7 +170,7 @@ public: SVal evalDerivedToBase(SVal Derived, QualType DerivedPtrType, bool IsVirtual); - /// \brief Attempts to do a down cast. Used to model BaseToDerived and C++ + /// Attempts to do a down cast. Used to model BaseToDerived and C++ /// dynamic_cast. /// The callback may result in the following 3 scenarios: /// - Successful cast (ex: derived is subclass of base). @@ -168,7 +191,7 @@ public: const MemRegion *castRegion(const MemRegion *region, QualType CastToTy); virtual StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, - SymbolReaper& SymReaper) = 0; + SymbolReaper &SymReaper) = 0; virtual bool includedInBindings(Store store, const MemRegion *region) const = 0; @@ -182,7 +205,7 @@ public: /// associated with the object is recycled. virtual void decrementReferenceCount(Store store) {} - typedef SmallVector<const MemRegion *, 8> InvalidatedRegions; + using InvalidatedRegions = SmallVector<const MemRegion *, 8>; /// invalidateRegions - Clears out the specified regions from the store, /// marking their values as unknown. Depending on the store, this may also @@ -235,6 +258,7 @@ public: class BindingsHandler { public: virtual ~BindingsHandler(); + virtual bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion *region, SVal val) = 0; }; @@ -242,16 +266,16 @@ public: class FindUniqueBinding : public BindingsHandler { SymbolRef Sym; - const MemRegion* Binding; - bool First; + const MemRegion* Binding = nullptr; + bool First = true; public: - FindUniqueBinding(SymbolRef sym) - : Sym(sym), Binding(nullptr), First(true) {} + FindUniqueBinding(SymbolRef sym) : Sym(sym) {} + + explicit operator bool() { return First && Binding; } bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, SVal val) override; - explicit operator bool() { return First && Binding; } const MemRegion *getRegion() { return Binding; } }; @@ -266,22 +290,21 @@ protected: /// CastRetrievedVal - Used by subclasses of StoreManager to implement /// implicit casts that arise from loads from regions that are reinterpreted /// as another region. - SVal CastRetrievedVal(SVal val, const TypedValueRegion *region, - QualType castTy, bool performTestOnly = true); + SVal CastRetrievedVal(SVal val, const TypedValueRegion *region, + QualType castTy); private: SVal getLValueFieldOrIvar(const Decl *decl, SVal base); }; - inline StoreRef::StoreRef(Store store, StoreManager & smgr) - : store(store), mgr(smgr) { + : store(store), mgr(smgr) { if (store) mgr.incrementReferenceCount(store); } -inline StoreRef::StoreRef(const StoreRef &sr) - : store(sr.store), mgr(sr.mgr) +inline StoreRef::StoreRef(const StoreRef &sr) + : store(sr.store), mgr(sr.mgr) { if (store) mgr.incrementReferenceCount(store); @@ -308,8 +331,8 @@ CreateRegionStoreManager(ProgramStateManager &StMgr); std::unique_ptr<StoreManager> CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr); -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STORE_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h b/include/clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h index 958c8c377ea2a..c5b4e5036bdf4 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h @@ -1,4 +1,4 @@ -//== StoreRef.h - Smart pointer for store objects ---------------*- C++ -*--==// +//===- StoreRef.h - Smart pointer for store objects -------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -19,33 +19,36 @@ namespace clang { namespace ento { +class StoreManager; + /// Store - This opaque type encapsulates an immutable mapping from /// locations to values. At a high-level, it represents the symbolic /// memory model. Different subclasses of StoreManager may choose /// different types to represent the locations and values. -typedef const void *Store; - -class StoreManager; +using Store = const void *; class StoreRef { Store store; StoreManager &mgr; + public: - StoreRef(Store, StoreManager &); - StoreRef(const StoreRef &); - StoreRef &operator=(StoreRef const &); + StoreRef(Store store, StoreManager &smgr); + StoreRef(const StoreRef &sr); + StoreRef &operator=(StoreRef const &newStore); + ~StoreRef(); bool operator==(const StoreRef &x) const { assert(&mgr == &x.mgr); return x.store == store; } + bool operator!=(const StoreRef &x) const { return !operator==(x); } - ~StoreRef(); - Store getStore() const { return store; } const StoreManager &getStoreManager() const { return mgr; } }; -}} -#endif +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STOREREF_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index 8ccd34751bbe1..e574ffdd6a159 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -24,6 +24,10 @@ class CFGElement; class LocationContext; class Stmt; +namespace cross_tu { +class CrossTranslationUnitContext; +} + namespace ento { struct NodeBuilderContext; @@ -49,6 +53,9 @@ public: virtual AnalysisManager &getAnalysisManager() = 0; + virtual cross_tu::CrossTranslationUnitContext * + getCrossTranslationUnitContext() = 0; + virtual ProgramStateManager &getStateManager() = 0; /// Called by CoreEngine. Used to generate new successor @@ -155,7 +162,8 @@ public: /// printState - Called by ProgramStateManager to print checker-specific data. virtual void printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep) = 0; + const char *NL, const char *Sep, + const LocationContext *LCtx = nullptr) = 0; /// Called by CoreEngine when the analysis worklist is either empty or the // maximum number of analysis steps have been reached. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h index 9780d0144746e..69b9858d3feb7 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h @@ -1,4 +1,4 @@ -//== SymExpr.h - Management of Symbolic Values ------------------*- C++ -*--==// +//===- SymExpr.h - Management of Symbolic Values ----------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -15,16 +15,17 @@ #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMEXPR_H #include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/Support/raw_ostream.h" +#include <cassert> namespace clang { namespace ento { class MemRegion; -/// \brief Symbolic value. These values used to capture symbolic execution of +/// Symbolic value. These values used to capture symbolic execution of /// the program. class SymExpr : public llvm::FoldingSetNode { virtual void anchor(); @@ -48,8 +49,10 @@ protected: return !T.isNull() && !T->isVoidType(); } + mutable unsigned Complexity = 0; + public: - virtual ~SymExpr() {} + virtual ~SymExpr() = default; Kind getKind() const { return K; } @@ -60,17 +63,18 @@ public: virtual QualType getType() const = 0; virtual void Profile(llvm::FoldingSetNodeID &profile) = 0; - /// \brief Iterator over symbols that the current symbol depends on. + /// Iterator over symbols that the current symbol depends on. /// /// For SymbolData, it's the symbol itself; for expressions, it's the /// expression symbol and all the operands in it. Note, SymbolDerived is /// treated as SymbolData - the iterator will NOT visit the parent region. class symbol_iterator { SmallVector<const SymExpr *, 5> itr; + void expand(); public: - symbol_iterator() {} + symbol_iterator() = default; symbol_iterator(const SymExpr *SE); symbol_iterator &operator++(); @@ -83,9 +87,9 @@ public: symbol_iterator symbol_begin() const { return symbol_iterator(this); } static symbol_iterator symbol_end() { return symbol_iterator(); } - unsigned computeComplexity() const; + virtual unsigned computeComplexity() const = 0; - /// \brief Find the region from which this symbol originates. + /// Find the region from which this symbol originates. /// /// Whenever the symbol was constructed to denote an unknown value of /// a certain memory region, return this region. This method @@ -104,26 +108,31 @@ inline raw_ostream &operator<<(raw_ostream &os, return os; } -typedef const SymExpr *SymbolRef; -typedef SmallVector<SymbolRef, 2> SymbolRefSmallVectorTy; +using SymbolRef = const SymExpr *; +using SymbolRefSmallVectorTy = SmallVector<SymbolRef, 2>; +using SymbolID = unsigned; -typedef unsigned SymbolID; -/// \brief A symbol representing data which can be stored in a memory location +/// A symbol representing data which can be stored in a memory location /// (region). class SymbolData : public SymExpr { - void anchor() override; const SymbolID Sym; + void anchor() override; + protected: SymbolData(Kind k, SymbolID sym) : SymExpr(k), Sym(sym) { assert(classof(this)); } public: - ~SymbolData() override {} + ~SymbolData() override = default; SymbolID getSymbolID() const { return Sym; } + unsigned computeComplexity() const override { + return 1; + }; + // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { Kind k = SE->getKind(); @@ -134,4 +143,4 @@ public: } // namespace ento } // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMEXPR_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 7d2e5ad8ba915..cc3ce72f9e569 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -1,4 +1,4 @@ -//== SymbolManager.h - Management of Symbolic Values ------------*- C++ -*--==// +//===- SymbolManager.h - Management of Symbolic Values ----------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -15,8 +15,8 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMBOLMANAGER_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMBOLMANAGER_H -#include "clang/AST/Decl.h" #include "clang/AST/Expr.h" +#include "clang/AST/Type.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -26,19 +26,19 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/DataTypes.h" +#include <cassert> namespace clang { - class ASTContext; - class StackFrameContext; + +class ASTContext; +class Stmt; namespace ento { - class BasicValueFactory; - class SubRegion; - class TypedValueRegion; - class VarRegion; -///\brief A symbol representing the value stored at a MemRegion. +class BasicValueFactory; +class StoreManager; + +///A symbol representing the value stored at a MemRegion. class SymbolRegionValue : public SymbolData { const TypedValueRegion *R; @@ -66,7 +66,7 @@ public: QualType getType() const override; // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymbolRegionValueKind; } }; @@ -118,7 +118,7 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymbolConjuredKind; } }; @@ -157,7 +157,7 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymbolDerivedKind; } }; @@ -190,7 +190,7 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymbolExtentKind; } }; @@ -206,11 +206,12 @@ class SymbolMetadata : public SymbolData { const LocationContext *LCtx; unsigned Count; const void *Tag; + public: SymbolMetadata(SymbolID sym, const MemRegion* r, const Stmt *s, QualType t, const LocationContext *LCtx, unsigned count, const void *tag) - : SymbolData(SymbolMetadataKind, sym), R(r), S(s), T(t), LCtx(LCtx), - Count(count), Tag(tag) { + : SymbolData(SymbolMetadataKind, sym), R(r), S(s), T(t), LCtx(LCtx), + Count(count), Tag(tag) { assert(r); assert(s); assert(isValidTypeForSymbol(t)); @@ -245,16 +246,18 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymbolMetadataKind; } }; -/// \brief Represents a cast expression. +/// Represents a cast expression. class SymbolCast : public SymExpr { const SymExpr *Operand; + /// Type of the operand. QualType FromTy; + /// The type of the result. QualType ToTy; @@ -267,6 +270,12 @@ public: // Otherwise, 'To' should also be a valid type. } + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = 1 + Operand->computeComplexity(); + return Complexity; + } + QualType getType() const override { return ToTy; } const SymExpr *getOperand() const { return Operand; } @@ -286,12 +295,12 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymbolCastKind; } }; -/// \brief Represents a symbolic expression involving a binary operator +/// Represents a symbolic expression involving a binary operator class BinarySymExpr : public SymExpr { BinaryOperator::Opcode Op; QualType T; @@ -311,13 +320,13 @@ public: BinaryOperator::Opcode getOpcode() const { return Op; } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { Kind k = SE->getKind(); return k >= BEGIN_BINARYSYMEXPRS && k <= END_BINARYSYMEXPRS; } }; -/// \brief Represents a symbolic expression like 'x' + 3. +/// Represents a symbolic expression like 'x' + 3. class SymIntExpr : public BinarySymExpr { const SymExpr *LHS; const llvm::APSInt& RHS; @@ -334,6 +343,12 @@ public: const SymExpr *getLHS() const { return LHS; } const llvm::APSInt &getRHS() const { return RHS; } + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = 1 + LHS->computeComplexity(); + return Complexity; + } + static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& rhs, QualType t) { @@ -349,12 +364,12 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymIntExprKind; } }; -/// \brief Represents a symbolic expression like 3 - 'x'. +/// Represents a symbolic expression like 3 - 'x'. class IntSymExpr : public BinarySymExpr { const llvm::APSInt& LHS; const SymExpr *RHS; @@ -371,6 +386,12 @@ public: const SymExpr *getRHS() const { return RHS; } const llvm::APSInt &getLHS() const { return LHS; } + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = 1 + RHS->computeComplexity(); + return Complexity; + } + static void Profile(llvm::FoldingSetNodeID& ID, const llvm::APSInt& lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { @@ -386,12 +407,12 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == IntSymExprKind; } }; -/// \brief Represents a symbolic expression like 'x' + 'y'. +/// Represents a symbolic expression like 'x' + 'y'. class SymSymExpr : public BinarySymExpr { const SymExpr *LHS; const SymExpr *RHS; @@ -409,6 +430,12 @@ public: void dumpToStream(raw_ostream &os) const override; + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = RHS->computeComplexity() + LHS->computeComplexity(); + return Complexity; + } + static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { ID.AddInteger((unsigned) SymSymExprKind); @@ -423,20 +450,22 @@ public: } // Implement isa<T> support. - static inline bool classof(const SymExpr *SE) { + static bool classof(const SymExpr *SE) { return SE->getKind() == SymSymExprKind; } }; class SymbolManager { - typedef llvm::FoldingSet<SymExpr> DataSetTy; - typedef llvm::DenseMap<SymbolRef, SymbolRefSmallVectorTy*> SymbolDependTy; + using DataSetTy = llvm::FoldingSet<SymExpr>; + using SymbolDependTy = llvm::DenseMap<SymbolRef, SymbolRefSmallVectorTy *>; DataSetTy DataSet; + /// Stores the extra dependencies between symbols: the data should be kept /// alive as long as the key is live. SymbolDependTy SymbolDependencies; - unsigned SymbolCounter; + + unsigned SymbolCounter = 0; llvm::BumpPtrAllocator& BPAlloc; BasicValueFactory &BV; ASTContext &Ctx; @@ -444,14 +473,12 @@ class SymbolManager { public: SymbolManager(ASTContext &ctx, BasicValueFactory &bv, llvm::BumpPtrAllocator& bpalloc) - : SymbolDependencies(16), SymbolCounter(0), - BPAlloc(bpalloc), BV(bv), Ctx(ctx) {} - + : SymbolDependencies(16), BPAlloc(bpalloc), BV(bv), Ctx(ctx) {} ~SymbolManager(); static bool canSymbolicate(QualType T); - /// \brief Make a unique symbol for MemRegion R according to its kind. + /// Make a unique symbol for MemRegion R according to its kind. const SymbolRegionValue* getRegionValueSymbol(const TypedValueRegion* R); const SymbolConjured* conjureSymbol(const Stmt *E, @@ -472,7 +499,7 @@ public: const SymbolExtent *getExtentSymbol(const SubRegion *R); - /// \brief Creates a metadata symbol associated with a specific region. + /// Creates a metadata symbol associated with a specific region. /// /// VisitCount can be used to differentiate regions corresponding to /// different loop iterations, thus, making the symbol path-dependent. @@ -504,7 +531,7 @@ public: return SE->getType(); } - /// \brief Add artificial symbol dependency. + /// Add artificial symbol dependency. /// /// The dependent symbol should stay alive as long as the primary is alive. void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent); @@ -515,16 +542,16 @@ public: BasicValueFactory &getBasicVals() { return BV; } }; -/// \brief A class responsible for cleaning up unused symbols. +/// A class responsible for cleaning up unused symbols. class SymbolReaper { enum SymbolStatus { NotProcessed, HaveMarkedDependents }; - typedef llvm::DenseSet<SymbolRef> SymbolSetTy; - typedef llvm::DenseMap<SymbolRef, SymbolStatus> SymbolMapTy; - typedef llvm::DenseSet<const MemRegion *> RegionSetTy; + using SymbolSetTy = llvm::DenseSet<SymbolRef>; + using SymbolMapTy = llvm::DenseMap<SymbolRef, SymbolStatus>; + using RegionSetTy = llvm::DenseSet<const MemRegion *>; SymbolMapTy TheLiving; SymbolSetTy MetadataInUse; @@ -539,17 +566,16 @@ class SymbolReaper { llvm::DenseMap<const MemRegion *, unsigned> includedRegionCache; public: - /// \brief Construct a reaper object, which removes everything which is not + /// Construct a reaper object, which removes everything which is not /// live before we execute statement s in the given location context. /// /// If the statement is NULL, everything is this and parent contexts is /// considered live. /// If the stack frame context is NULL, everything on stack is considered /// dead. - SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, SymbolManager& symmgr, - StoreManager &storeMgr) - : LCtx(Ctx), Loc(s), SymMgr(symmgr), - reapedStore(nullptr, storeMgr) {} + SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, + SymbolManager &symmgr, StoreManager &storeMgr) + : LCtx(Ctx), Loc(s), SymMgr(symmgr), reapedStore(nullptr, storeMgr) {} const LocationContext *getLocationContext() const { return LCtx; } @@ -558,14 +584,14 @@ public: bool isLive(const Stmt *ExprVal, const LocationContext *LCtx) const; bool isLive(const VarRegion *VR, bool includeStoreBindings = false) const; - /// \brief Unconditionally marks a symbol as live. + /// Unconditionally marks a symbol as live. /// /// This should never be /// used by checkers, only by the state infrastructure such as the store and /// environment. Checkers should instead use metadata symbols and markInUse. void markLive(SymbolRef sym); - /// \brief Marks a symbol as important to a checker. + /// Marks a symbol as important to a checker. /// /// For metadata symbols, /// this will keep the symbol alive as long as its associated region is also @@ -574,13 +600,14 @@ public: /// symbol marking has occurred, i.e. in the MarkLiveSymbols callback. void markInUse(SymbolRef sym); - /// \brief If a symbol is known to be live, marks the symbol as live. + /// If a symbol is known to be live, marks the symbol as live. /// /// Otherwise, if the symbol cannot be proven live, it is marked as dead. /// Returns true if the symbol is dead, false if live. bool maybeDead(SymbolRef sym); - typedef SymbolSetTy::const_iterator dead_iterator; + using dead_iterator = SymbolSetTy::const_iterator; + dead_iterator dead_begin() const { return TheDead.begin(); } dead_iterator dead_end() const { return TheDead.end(); } @@ -588,11 +615,12 @@ public: return !TheDead.empty(); } - typedef RegionSetTy::const_iterator region_iterator; + using region_iterator = RegionSetTy::const_iterator; + region_iterator region_begin() const { return RegionRoots.begin(); } region_iterator region_end() const { return RegionRoots.end(); } - /// \brief Returns whether or not a symbol has been confirmed dead. + /// Returns whether or not a symbol has been confirmed dead. /// /// This should only be called once all marking of dead symbols has completed. /// (For checkers, this means only in the evalDeadSymbols callback.) @@ -603,7 +631,7 @@ public: void markLive(const MemRegion *region); void markElementIndicesLive(const MemRegion *region); - /// \brief Set to the value of the symbolic store after + /// Set to the value of the symbolic store after /// StoreManager::removeDeadBindings has been called. void setReapedStore(StoreRef st) { reapedStore = st; } @@ -621,7 +649,7 @@ public: SymbolVisitor(const SymbolVisitor &) = default; SymbolVisitor(SymbolVisitor &&) {} - /// \brief A visitor method invoked by ProgramStateManager::scanReachableSymbols. + /// A visitor method invoked by ProgramStateManager::scanReachableSymbols. /// /// The method returns \c true if symbols should continue be scanned and \c /// false otherwise. @@ -629,8 +657,8 @@ public: virtual bool VisitMemRegion(const MemRegion *region) { return true; } }; -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMBOLMANAGER_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h index 7b76263f040c9..ce19b7131d42d 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h @@ -1,4 +1,4 @@ -//== TaintManager.h - Managing taint --------------------------- -*- C++ -*--=// +//===- TaintManager.h - Managing taint --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -14,9 +14,9 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTMANAGER_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTMANAGER_H -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h" #include "llvm/ADT/ImmutableMap.h" @@ -29,28 +29,37 @@ namespace ento { // FIXME: This does not use the nice trait macros because it must be accessible // from multiple translation units. struct TaintMap {}; -typedef llvm::ImmutableMap<SymbolRef, TaintTagType> TaintMapImpl; + +using TaintMapImpl = llvm::ImmutableMap<SymbolRef, TaintTagType>; + template<> struct ProgramStateTrait<TaintMap> : public ProgramStatePartialTrait<TaintMapImpl> { - static void *GDMIndex() { static int index = 0; return &index; } + static void *GDMIndex() { + static int index = 0; + return &index; + } }; /// The GDM component mapping derived symbols' parent symbols to their /// underlying regions. This is used to efficiently check whether a symbol is /// tainted when it represents a sub-region of a tainted symbol. struct DerivedSymTaint {}; -typedef llvm::ImmutableMap<SymbolRef, TaintedSubRegions> DerivedSymTaintImpl; + +using DerivedSymTaintImpl = llvm::ImmutableMap<SymbolRef, TaintedSubRegions>; + template<> struct ProgramStateTrait<DerivedSymTaint> : public ProgramStatePartialTrait<DerivedSymTaintImpl> { - static void *GDMIndex() { static int index; return &index; } + static void *GDMIndex() { + static int index; + return &index; + } }; class TaintManager { - - TaintManager() {} + TaintManager() = default; }; -} -} +} // namespace ento +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTMANAGER_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h index 0c56e7d8ea698..50c4b8194cff8 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h @@ -1,4 +1,4 @@ -//== TaintTag.h - Path-sensitive "State" for tracking values -*- C++ -*--=// +//===- TaintTag.h - Path-sensitive "State" for tracking values --*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -11,6 +11,7 @@ // of taint. // //===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTTAG_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTTAG_H @@ -19,9 +20,11 @@ namespace ento { /// The type of taint, which helps to differentiate between different types of /// taint. -typedef unsigned TaintTagType; +using TaintTagType = unsigned; + static const TaintTagType TaintTagGeneric = 0; -}} +} // namespace ento +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTTAG_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h b/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h index 4f1a60e675569..7df8f373b3663 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h @@ -80,20 +80,14 @@ public: void setBlockCounter(BlockCounter C) { CurrentCounter = C; } BlockCounter getBlockCounter() const { return CurrentCounter; } - class Visitor { - public: - Visitor() {} - virtual ~Visitor(); - virtual bool visit(const WorkListUnit &U) = 0; - }; - virtual bool visitItemsInWorkList(Visitor &V) = 0; - - static WorkList *makeDFS(); - static WorkList *makeBFS(); - static WorkList *makeBFSBlockDFSContents(); + static std::unique_ptr<WorkList> makeDFS(); + static std::unique_ptr<WorkList> makeBFS(); + static std::unique_ptr<WorkList> makeBFSBlockDFSContents(); + static std::unique_ptr<WorkList> makeUnexploredFirst(); + static std::unique_ptr<WorkList> makeUnexploredFirstPriorityQueue(); }; -} // end GR namespace +} // end ento namespace } // end clang namespace |