diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2013-04-08 18:45:10 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2013-04-08 18:45:10 +0000 |
commit | 809500fc2c13c8173a16b052304d983864e4a1e1 (patch) | |
tree | 4fc2f184c499d106f29a386c452b49e5197bf63d /include/clang/StaticAnalyzer | |
parent | be7c9ec198dcdb5bf73a35bfbb00b3333cb87909 (diff) | |
download | src-809500fc2c13c8173a16b052304d983864e4a1e1.tar.gz src-809500fc2c13c8173a16b052304d983864e4a1e1.zip |
Vendor import of clang trunk r178860:vendor/clang/clang-trunk-r178860
Notes
Notes:
svn path=/vendor/clang/dist/; revision=249261
svn path=/vendor/clang/clang-trunk-r178860/; revision=249262; tag=vendor/clang/clang-trunk-r178860
Diffstat (limited to 'include/clang/StaticAnalyzer')
32 files changed, 1049 insertions, 476 deletions
diff --git a/include/clang/StaticAnalyzer/Core/Analyses.def b/include/clang/StaticAnalyzer/Core/Analyses.def index 01a6ffd7142c..dc7945016b1a 100644 --- a/include/clang/StaticAnalyzer/Core/Analyses.def +++ b/include/clang/StaticAnalyzer/Core/Analyses.def @@ -41,22 +41,12 @@ ANALYSIS_PURGE(PurgeStmt, "statement", "Purge symbols, bindings, and constraint ANALYSIS_PURGE(PurgeBlock, "block", "Purge symbols, bindings, and constraints before every basic block") ANALYSIS_PURGE(PurgeNone, "none", "Do not purge symbols, bindings, or constraints") -#ifndef ANALYSIS_IPA -#define ANALYSIS_IPA(NAME, CMDFLAG, DESC) -#endif - -ANALYSIS_IPA(None, "none", "Perform only intra-procedural analysis") -ANALYSIS_IPA(BasicInlining, "basic-inlining", "Inline C functions and blocks when their definitions are available") -ANALYSIS_IPA(Inlining, "inlining", "Inline callees when their definitions are available") -ANALYSIS_IPA(DynamicDispatch, "dynamic", "Experimental: Enable inlining of dynamically dispatched methods") -ANALYSIS_IPA(DynamicDispatchBifurcate, "dynamic-bifurcate", "Experimental: Enable inlining of dynamically dispatched methods, bifurcate paths when exact type info is unavailable") - #ifndef ANALYSIS_INLINING_MODE #define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) #endif -ANALYSIS_INLINING_MODE(All, "all", "Analyze all functions in the order defined in the TU") -ANALYSIS_INLINING_MODE(NoRedundancy, "noredundancy", "Do not analyze a function which has been previously inlined, use call graph to order") +ANALYSIS_INLINING_MODE(All, "all", "Analyze all functions as top level") +ANALYSIS_INLINING_MODE(NoRedundancy, "noredundancy", "Do not analyze a function which has been previously inlined") #undef ANALYSIS_STORE #undef ANALYSIS_CONSTRAINTS diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index fa0754acb150..6dbdbbf89b96 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -15,12 +15,12 @@ #ifndef LLVM_CLANG_ANALYZEROPTIONS_H #define LLVM_CLANG_ANALYZEROPTIONS_H -#include <string> -#include <vector> #include "clang/Basic/LLVM.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" +#include <string> +#include <vector> namespace clang { class ASTConsumer; @@ -64,13 +64,6 @@ enum AnalysisPurgeMode { NumPurgeModes }; -/// AnalysisIPAMode - Set of inter-procedural modes. -enum AnalysisIPAMode { -#define ANALYSIS_IPA(NAME, CMDFLAG, DESC) NAME, -#include "clang/StaticAnalyzer/Core/Analyses.def" -NumIPAModes -}; - /// AnalysisInlineFunctionSelection - Set of inlining function selection heuristics. enum AnalysisInliningMode { #define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) NAME, @@ -102,8 +95,28 @@ enum CXXInlineableMemberKind { CIMK_Destructors }; +/// \brief Describes the different modes of inter-procedural analysis. +enum IPAKind { + IPAK_NotSet = 0, + + /// Perform only intra-procedural analysis. + IPAK_None = 1, + + /// Inline C functions and blocks when their definitions are available. + IPAK_BasicInlining = 2, -class AnalyzerOptions : public llvm::RefCountedBase<AnalyzerOptions> { + /// Inline callees(C, C++, ObjC) when their definitions are available. + IPAK_Inlining = 3, + + /// Enable inlining of dynamically dispatched methods. + IPAK_DynamicDispatch = 4, + + /// Enable inlining of dynamically dispatched methods, bifurcate paths when + /// exact type info is unavailable. + IPAK_DynamicDispatchBifurcate = 5 +}; + +class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> { public: typedef llvm::StringMap<std::string> ConfigTable; @@ -117,14 +130,8 @@ public: AnalysisDiagClients AnalysisDiagOpt; AnalysisPurgeMode AnalysisPurgeOpt; - // \brief The interprocedural analysis mode. - AnalysisIPAMode IPAMode; - std::string AnalyzeSpecificFunction; - /// \brief The maximum number of exploded nodes the analyzer will generate. - unsigned MaxNodes; - /// \brief The maximum number of times the analyzer visits a block. unsigned maxBlockVisitOnPath; @@ -159,39 +166,71 @@ public: unsigned InlineMaxStackDepth; /// \brief The mode of function selection used during inlining. - unsigned InlineMaxFunctionSize; - - /// \brief The mode of function selection used during inlining. AnalysisInliningMode InliningMode; private: + /// \brief 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 + }; + + /// 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; + + /// Controls the mode of inter-procedural analysis. + IPAKind IPAMode; + /// Controls which C++ member functions will be considered for inlining. CXXInlineableMemberKind CXXMemberInliningMode; /// \sa includeTemporaryDtorsInCFG - llvm::Optional<bool> IncludeTemporaryDtorsInCFG; + Optional<bool> IncludeTemporaryDtorsInCFG; /// \sa mayInlineCXXStandardLibrary - llvm::Optional<bool> InlineCXXStandardLibrary; + Optional<bool> InlineCXXStandardLibrary; /// \sa mayInlineTemplateFunctions - llvm::Optional<bool> InlineTemplateFunctions; + Optional<bool> InlineTemplateFunctions; + + /// \sa mayInlineCXXContainerCtorsAndDtors + Optional<bool> InlineCXXContainerCtorsAndDtors; /// \sa mayInlineObjCMethod - llvm::Optional<bool> ObjCInliningMode; + Optional<bool> ObjCInliningMode; // Cache of the "ipa-always-inline-size" setting. // \sa getAlwaysInlineSize - llvm::Optional<unsigned> AlwaysInlineSize; + Optional<unsigned> AlwaysInlineSize; - /// \sa shouldPruneNullReturnPaths - llvm::Optional<bool> PruneNullReturnPaths; + /// \sa shouldSuppressNullReturnPaths + Optional<bool> SuppressNullReturnPaths; + + // \sa getMaxInlinableSize + Optional<unsigned> MaxInlinableSize; /// \sa shouldAvoidSuppressingNullArgumentPaths - llvm::Optional<bool> AvoidSuppressingNullArgumentPaths; - + Optional<bool> AvoidSuppressingNullArgumentPaths; + + /// \sa shouldSuppressInlinedDefensiveChecks + Optional<bool> SuppressInlinedDefensiveChecks; + + /// \sa shouldSuppressFromCXXStandardLibrary + Optional<bool> SuppressFromCXXStandardLibrary; + /// \sa getGraphTrimInterval - llvm::Optional<unsigned> GraphTrimInterval; + Optional<unsigned> GraphTrimInterval; + + /// \sa getMaxTimesInlineLarge + Optional<unsigned> MaxTimesInlineLarge; + + /// \sa getMaxNodesPerTopLevelFunction + Optional<unsigned> MaxNodesPerTopLevelFunction; /// Interprets an option's string value as a boolean. /// @@ -200,13 +239,20 @@ private: bool getBooleanOption(StringRef Name, bool DefaultVal); /// Variant that accepts a Optional value to cache the result. - bool getBooleanOption(llvm::Optional<bool> &V, StringRef Name, - bool DefaultVal); - + bool getBooleanOption(Optional<bool> &V, StringRef Name, bool DefaultVal); + /// Interprets an option's string value as an integer value. - int getOptionAsInteger(llvm::StringRef Name, int DefaultVal); + int getOptionAsInteger(StringRef Name, int DefaultVal); public: + /// \brief 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. + IPAKind getIPAMode(); + /// Returns the option controlling which C++ member functions will be /// considered for inlining. /// @@ -238,6 +284,13 @@ public: /// accepts the values "true" and "false". bool mayInlineTemplateFunctions(); + /// Returns whether or not constructors and destructors of C++ container + /// objects may be considered for inlining. + /// + /// This is controlled by the 'c++-container-inlining' config option, which + /// accepts the values "true" and "false". + bool mayInlineCXXContainerCtorsAndDtors(); + /// Returns whether or not paths that go through null returns should be /// suppressed. /// @@ -246,12 +299,12 @@ public: /// /// This is controlled by the 'suppress-null-return-paths' config option, /// which accepts the values "true" and "false". - bool shouldPruneNullReturnPaths(); + bool shouldSuppressNullReturnPaths(); /// Returns whether a bug report should \em not be suppressed if its path /// includes a call with a null argument, even if that call has a null return. /// - /// This option has no effect when #shouldPruneNullReturnPaths() is false. + /// This option has no effect when #shouldSuppressNullReturnPaths() is false. /// /// This is a counter-heuristic to avoid false negatives. /// @@ -259,12 +312,43 @@ public: /// option, which accepts the values "true" and "false". bool shouldAvoidSuppressingNullArgumentPaths(); + /// Returns whether or not diagnostics containing inlined defensive NULL + /// checks should be suppressed. + /// + /// This is controlled by the 'suppress-inlined-defensive-checks' config + /// option, which accepts the values "true" and "false". + bool shouldSuppressInlinedDefensiveChecks(); + + /// Returns whether or not diagnostics reported within the C++ standard + /// library should be suppressed. + /// + /// This is controlled by the 'suppress-c++-stdlib' config option, + /// which accepts the values "true" and "false". + bool shouldSuppressFromCXXStandardLibrary(); + + /// Returns whether irrelevant parts of a bug report path should be pruned + /// out of the final output. + /// + /// This is controlled by the 'prune-paths' config option, which accepts the + /// values "true" and "false". + bool shouldPrunePaths(); + + /// Returns true if 'static' initializers should be in conditional logic + /// in the CFG. + bool shouldConditionalizeStaticInitializers(); + // Returns the size of the functions (in basic blocks), which should be // considered to be small enough to always inline. // // This is controlled by "ipa-always-inline-size" analyzer-config option. unsigned getAlwaysInlineSize(); - + + // Returns the bound on the number of basic blocks in an inlined function + // (50 by default). + // + // This is controlled by "-analyzer-config max-inlinable-size" option. + unsigned getMaxInlinableSize(); + /// Returns true if the analyzer engine should synthesize fake bodies /// for well-known functions. bool shouldSynthesizeBodies(); @@ -276,32 +360,45 @@ public: /// node reclamation, set the option to "0". unsigned getGraphTrimInterval(); + /// Returns the maximum times a large function could be inlined. + /// + /// This is controlled by the 'max-times-inline-large' config option. + unsigned getMaxTimesInlineLarge(); + + /// Returns the maximum number of nodes the analyzer can generate while + /// exploring a top level function (for each exploded graph). + /// 150000 is default; 0 means no limit. + /// + /// This is controlled by the 'max-nodes' config option. + unsigned getMaxNodesPerTopLevelFunction(); + public: - AnalyzerOptions() : CXXMemberInliningMode() { - AnalysisStoreOpt = RegionStoreModel; - AnalysisConstraintsOpt = RangeConstraintsModel; - AnalysisDiagOpt = PD_HTML; - AnalysisPurgeOpt = PurgeStmt; - IPAMode = DynamicDispatchBifurcate; - ShowCheckerHelp = 0; - AnalyzeAll = 0; - AnalyzerDisplayProgress = 0; - AnalyzeNestedBlocks = 0; - eagerlyAssumeBinOpBifurcation = 0; - TrimGraph = 0; - visualizeExplodedGraphWithGraphViz = 0; - visualizeExplodedGraphWithUbiGraph = 0; - UnoptimizedCFG = 0; - PrintStats = 0; - NoRetryExhausted = 0; + AnalyzerOptions() : + AnalysisStoreOpt(RegionStoreModel), + AnalysisConstraintsOpt(RangeConstraintsModel), + AnalysisDiagOpt(PD_HTML), + AnalysisPurgeOpt(PurgeStmt), + ShowCheckerHelp(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; - InlineMaxFunctionSize = 200; - InliningMode = NoRedundancy; - } + InlineMaxStackDepth(5), + InliningMode(NoRedundancy), + UserMode(UMK_NotSet), + IPAMode(IPAK_NotSet), + CXXMemberInliningMode() {} + }; -typedef llvm::IntrusiveRefCntPtr<AnalyzerOptions> AnalyzerOptionsRef; +typedef IntrusiveRefCntPtr<AnalyzerOptions> AnalyzerOptionsRef; } diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index b5a88ba9f6c6..7a87e47f74ce 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -19,12 +19,12 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/ilist.h" -#include "llvm/ADT/ilist_node.h" #include "llvm/ADT/ImmutableSet.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/ilist_node.h" namespace clang { @@ -75,6 +75,8 @@ protected: std::string Description; PathDiagnosticLocation Location; PathDiagnosticLocation UniqueingLocation; + const Decl *UniqueingDecl; + const ExplodedNode *ErrorNode; SmallVector<SourceRange, 4> Ranges; ExtraTextList ExtraText; @@ -87,14 +89,14 @@ protected: /// diagnostics to include when constructing the final path diagnostic. /// The stack is largely used by BugReporter when generating PathDiagnostics /// for multiple PathDiagnosticConsumers. - llvm::SmallVector<Symbols *, 2> interestingSymbols; + SmallVector<Symbols *, 2> interestingSymbols; /// A (stack of) set of regions that are registered with this report as being /// "interesting", and thus used to help decide which diagnostics /// to include when constructing the final path diagnostic. /// The stack is largely used by BugReporter when generating PathDiagnostics /// for multiple PathDiagnosticConsumers. - llvm::SmallVector<Regions *, 2> interestingRegions; + SmallVector<Regions *, 2> interestingRegions; /// A set of location contexts that correspoind to call sites which should be /// considered "interesting". @@ -162,9 +164,10 @@ public: /// for uniquing reports. For example, memory leaks checker, could set this to /// the allocation site, rather then the location where the bug is reported. BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode, - PathDiagnosticLocation LocationToUnique) + PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique) : BT(bt), DeclWithIssue(0), Description(desc), UniqueingLocation(LocationToUnique), + UniqueingDecl(DeclToUnique), ErrorNode(errornode), ConfigurationChangeToken(0), DoNotPrunePath(false) {} @@ -260,6 +263,16 @@ public: /// 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. + PathDiagnosticLocation getUniqueingLocation() const { + return UniqueingLocation; + } + + /// \brief 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. @@ -440,8 +453,7 @@ public: return true; } - bool RemoveUneededCalls(PathPieces &pieces, BugReport *R, - PathDiagnosticCallPiece *CallWithLoc = 0); + bool RemoveUnneededCalls(PathPieces &pieces, BugReport *R); void Register(BugType *BT); diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h index 78e35ca82b89..2e5f207f4b4c 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h @@ -55,8 +55,8 @@ public: /// /// The last parameter can be used to register a new visitor with the given /// BugReport while processing a node. - virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, + virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) = 0; @@ -99,26 +99,24 @@ class FindLastStoreBRVisitor { const MemRegion *R; SVal V; - bool satisfied; + bool Satisfied; -public: - /// \brief Convenience method to create a visitor given only the MemRegion. - /// Returns NULL if the visitor cannot be created. For example, when the - /// corresponding value is unknown. - static BugReporterVisitor *createVisitorObject(const ExplodedNode *N, - const MemRegion *R); + /// If the visitor is tracking the value directly responsible for the + /// bug, we are going to employ false positive suppression. + bool EnableNullFPSuppression; +public: /// Creates a visitor for every VarDecl inside a Stmt and registers it with /// the BugReport. - static void registerStatementVarDecls(BugReport &BR, const Stmt *S); + static void registerStatementVarDecls(BugReport &BR, const Stmt *S, + bool EnableNullFPSuppression); - FindLastStoreBRVisitor(SVal v, const MemRegion *r) - : R(r), V(v), satisfied(false) { - assert (!V.isUnknown() && "Cannot track unknown value."); - - // TODO: Does it make sense to allow undef values here? - // (If not, also see UndefCapturedBlockVarChecker)? - } + FindLastStoreBRVisitor(KnownSVal V, const MemRegion *R, + bool InEnableNullFPSuppression) + : R(R), + V(V), + Satisfied(false), + EnableNullFPSuppression(InEnableNullFPSuppression) {} void Profile(llvm::FoldingSetNodeID &ID) const; @@ -132,12 +130,14 @@ class TrackConstraintBRVisitor : public BugReporterVisitorImpl<TrackConstraintBRVisitor> { DefinedSVal Constraint; - const bool Assumption; - bool isSatisfied; + bool Assumption; + bool IsSatisfied; + bool IsZeroCheck; public: TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) - : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} + : Constraint(constraint), Assumption(assumption), IsSatisfied(false), + IsZeroCheck(!Assumption && Constraint.getAs<Loc>()) {} void Profile(llvm::FoldingSetNodeID &ID) const; @@ -149,12 +149,19 @@ public: const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR); + +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 - : public BugReporterVisitorImpl<NilReceiverBRVisitor> -{ + : public BugReporterVisitorImpl<NilReceiverBRVisitor> { public: + void Profile(llvm::FoldingSetNodeID &ID) const { static int x = 0; ID.AddPointer(&x); @@ -164,6 +171,10 @@ public: const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR); + + /// If the statement is a message send expression with nil receiver, returns + /// the receiver expression. Returns NULL otherwise. + static const Expr *getNilReceiver(const Stmt *S, const ExplodedNode *N); }; /// Visitor that tries to report interesting diagnostics from conditions. @@ -223,11 +234,38 @@ public: const ExplodedNode *N); bool patternMatch(const Expr *Ex, - llvm::raw_ostream &Out, + raw_ostream &Out, BugReporterContext &BRC, BugReport &R, const ExplodedNode *N, - llvm::Optional<bool> &prunable); + Optional<bool> &prunable); +}; + +/// \brief Suppress reports that might lead to known false positives. +/// +/// Currently this suppresses reports based on locations of bugs. +class LikelyFalsePositiveSuppressionBRVisitor + : public BugReporterVisitorImpl<LikelyFalsePositiveSuppressionBRVisitor> { +public: + static void *getTag() { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(getTag()); + } + + virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *Prev, + BugReporterContext &BRC, + BugReport &BR) { + return 0; + } + + virtual PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR); }; /// \brief When a region containing undefined value or '0' value is passed @@ -256,6 +294,38 @@ public: BugReport &BR); }; +class SuppressInlineDefensiveChecksVisitor +: public BugReporterVisitorImpl<SuppressInlineDefensiveChecksVisitor> +{ + /// 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; + + /// 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; + +public: + SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N); + + void Profile(llvm::FoldingSetNodeID &ID) const; + + /// Return the tag associated with this visitor. This tag will be used + /// to make all PathDiagnosticPieces created by this visitor. + static const char *getTag(); + + PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, + BugReport &BR); +}; + namespace bugreporter { /// Attempts to add visitors to trace a null or undefined value back to its @@ -268,14 +338,17 @@ namespace bugreporter { /// \param IsArg Whether the statement is an argument to an inlined function. /// If this is the case, \p N \em must be the CallEnter node for /// the function. +/// \param EnableNullFPSuppression Whether we should employ false positive +/// suppression (inlined defensive checks, returned null). /// /// \return Whether or not the function was able to add visitors for this /// statement. Note that returning \c true does not actually imply /// that any visitors were added. bool trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &R, - bool IsArg = false); + bool IsArg = false, + bool EnableNullFPSuppression = true); -const Stmt *GetDerefExpr(const ExplodedNode *N); +const Expr *getDerefExpr(const Stmt *S); const Stmt *GetDenomExpr(const ExplodedNode *N); const Stmt *GetRetValExpr(const ExplodedNode *N); bool isDeclRefExprToReference(const Expr *E); diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h index cb49122e4a53..644aa3159301 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_ANALYSIS_BUGTYPE #define LLVM_CLANG_ANALYSIS_BUGTYPE +#include "clang/Basic/LLVM.h" #include "llvm/ADT/FoldingSet.h" #include <string> diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h index 6dc26e670344..3f0a1b1bc144 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -14,12 +14,12 @@ #ifndef LLVM_CLANG_PATH_DIAGNOSTIC_H #define LLVM_CLANG_PATH_DIAGNOSTIC_H -#include "clang/Basic/SourceLocation.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/SourceLocation.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" -#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" #include <deque> #include <iterator> #include <string> @@ -341,7 +341,7 @@ protected: public: virtual ~PathDiagnosticPiece(); - llvm::StringRef getString() const { return str; } + StringRef getString() const { return str; } /// Tag this PathDiagnosticPiece with the given C-string. void setTag(const char *tag) { Tag = tag; } @@ -461,13 +461,13 @@ public: }; class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { - llvm::Optional<bool> IsPrunable; + Optional<bool> IsPrunable; /// If the event occurs in a different frame than the final diagnostic, /// supply a message that will be used to construct an extra hint on the /// returns from all the calls on the stack from this event to the final /// diagnostic. - llvm::OwningPtr<StackHintGenerator> CallStackHint; + OwningPtr<StackHintGenerator> CallStackHint; public: PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, @@ -670,13 +670,19 @@ class PathDiagnostic : public llvm::FoldingSetNode { std::deque<std::string> OtherDesc; PathDiagnosticLocation Loc; PathPieces pathImpl; - llvm::SmallVector<PathPieces *, 3> pathStack; + SmallVector<PathPieces *, 3> pathStack; - PathDiagnostic(); // Do not implement. + /// \brief Important bug uniqueing location. + /// The location info is useful to differentiate between bugs. + PathDiagnosticLocation UniqueingLoc; + const Decl *UniqueingDecl; + + PathDiagnostic() LLVM_DELETED_FUNCTION; public: PathDiagnostic(const Decl *DeclWithIssue, StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, - StringRef category); + StringRef category, PathDiagnosticLocation LocationToUnique, + const Decl *DeclToUnique); ~PathDiagnostic(); @@ -738,6 +744,16 @@ public: return Loc; } + /// \brief Get the location on which the report should be uniqued. + PathDiagnosticLocation getUniqueingLoc() const { + return UniqueingLoc; + } + + /// \brief 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(); diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index 9eb1248f6a71..0dbaab033d2d 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -34,11 +34,11 @@ class ASTDecl { template <typename CHECKER> static void _checkDecl(void *checker, const Decl *D, AnalysisManager& mgr, BugReporter &BR) { - ((const CHECKER *)checker)->checkASTDecl(llvm::cast<DECL>(D), mgr, BR); + ((const CHECKER *)checker)->checkASTDecl(cast<DECL>(D), mgr, BR); } static bool _handlesDecl(const Decl *D) { - return llvm::isa<DECL>(D); + return isa<DECL>(D); } public: template <typename CHECKER> @@ -86,11 +86,11 @@ template <typename STMT> class PreStmt { template <typename CHECKER> static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { - ((const CHECKER *)checker)->checkPreStmt(llvm::cast<STMT>(S), C); + ((const CHECKER *)checker)->checkPreStmt(cast<STMT>(S), C); } static bool _handlesStmt(const Stmt *S) { - return llvm::isa<STMT>(S); + return isa<STMT>(S); } public: template <typename CHECKER> @@ -105,11 +105,11 @@ template <typename STMT> class PostStmt { template <typename CHECKER> static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { - ((const CHECKER *)checker)->checkPostStmt(llvm::cast<STMT>(S), C); + ((const CHECKER *)checker)->checkPostStmt(cast<STMT>(S), C); } static bool _handlesStmt(const Stmt *S) { - return llvm::isa<STMT>(S); + return isa<STMT>(S); } public: template <typename CHECKER> @@ -227,18 +227,18 @@ public: } }; -class EndPath { +class EndFunction { template <typename CHECKER> - static void _checkEndPath(void *checker, - CheckerContext &C) { - ((const CHECKER *)checker)->checkEndPath(C); + static void _checkEndFunction(void *checker, + CheckerContext &C) { + ((const CHECKER *)checker)->checkEndFunction(C); } public: template <typename CHECKER> static void _register(CHECKER *checker, CheckerManager &mgr) { - mgr._registerForEndPath( - CheckerManager::CheckEndPathFunc(checker, _checkEndPath<CHECKER>)); + mgr._registerForEndFunction( + CheckerManager::CheckEndFunctionFunc(checker, _checkEndFunction<CHECKER>)); } }; @@ -293,7 +293,7 @@ class RegionChanges { static ProgramStateRef _checkRegionChanges(void *checker, ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) { @@ -317,6 +317,59 @@ public: } }; +class PointerEscape { + template <typename CHECKER> + static ProgramStateRef + _checkPointerEscape(void *checker, + ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool IsConst) { + if (!IsConst) + return ((const CHECKER *)checker)->checkPointerEscape(State, + Escaped, + Call, + Kind); + return State; + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPointerEscape( + CheckerManager::CheckPointerEscapeFunc(checker, + _checkPointerEscape<CHECKER>)); + } +}; + +class ConstPointerEscape { + template <typename CHECKER> + static ProgramStateRef + _checkConstPointerEscape(void *checker, + ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool IsConst) { + if (IsConst) + return ((const CHECKER *)checker)->checkConstPointerEscape(State, + Escaped, + Call, + Kind); + return State; + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPointerEscape( + CheckerManager::CheckPointerEscapeFunc(checker, + _checkConstPointerEscape<CHECKER>)); + } +}; + + template <typename EVENT> class Event { template <typename CHECKER> @@ -448,6 +501,14 @@ struct ImplicitNullDerefEvent { BugReporter *BR; }; +/// \brief A helper class which wraps a boolean value set to false by default. +struct DefaultBool { + bool val; + DefaultBool() : val(false) {} + operator bool() const { return val; } + DefaultBool &operator=(bool b) { val = b; return *this; } +}; + } // end ento namespace } // end clang namespace diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 7ae8e53784bf..6f99fc14577a 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -14,12 +14,12 @@ #ifndef LLVM_CLANG_SA_CORE_CHECKERMANAGER_H #define LLVM_CLANG_SA_CORE_CHECKERMANAGER_H +#include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LangOptions.h" -#include "llvm/ADT/SmallVector.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" -#include "clang/Analysis/ProgramPoint.h" +#include "llvm/ADT/SmallVector.h" #include <vector> namespace clang { @@ -112,6 +112,26 @@ public: RET operator()() const { return Fn(Checker); } }; +/// \brief Describes the different reasons a pointer escapes +/// during analysis. +enum PointerEscapeKind { + /// A pointer escapes due to binding its value to a location + /// that the analyzer cannot track. + PSK_EscapeOnBind, + + /// The pointer has been passed to a function call directly. + PSK_DirectEscapeOnCall, + + /// The pointer has been passed to a function indirectly. + /// For example, the pointer is accessible through an + /// argument to a function. + PSK_IndirectEscapeOnCall, + + /// The reason for pointer escape is unknown. For example, + /// a region containing this pointer is invalidated. + PSK_EscapeOther +}; + class CheckerManager { const LangOptions LangOpts; @@ -264,11 +284,11 @@ public: void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng); - /// \brief Run checkers for end of path. - void runCheckersForEndPath(NodeBuilderContext &BC, - ExplodedNodeSet &Dst, - ExplodedNode *Pred, - ExprEngine &Eng); + /// \brief Run checkers on end of function. + void runCheckersForEndFunction(NodeBuilderContext &BC, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, + ExprEngine &Eng); /// \brief Run checkers for branch condition. void runCheckersForBranchCondition(const Stmt *condition, @@ -310,14 +330,34 @@ public: /// by a call. ProgramStateRef runCheckersForRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call); + /// \brief 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 + /// result of assigning a pointer into a global or when it's passed to a + /// function call the analyzer cannot model. + /// + /// \param State The state at the point of escape. + /// \param Escaped The list of escaped symbols. + /// \param Call The corresponding CallEvent, if the symbols escape as + /// parameters to the given call. + /// \param IsConst Specifies if the pointer is const. + /// \returns Checkers can modify the state by returning a new one. + ProgramStateRef + runCheckersForPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool IsConst = false); + /// \brief Run checkers for handling assumptions on symbolic values. ProgramStateRef runCheckersForEvalAssume(ProgramStateRef state, - SVal Cond, bool Assumption); + SVal Cond, bool Assumption); /// \brief Run checkers for evaluating a call. /// @@ -382,7 +422,7 @@ public: CheckEndAnalysisFunc; typedef CheckerFn<void (CheckerContext &)> - CheckEndPathFunc; + CheckEndFunctionFunc; typedef CheckerFn<void (const Stmt *, CheckerContext &)> CheckBranchConditionFunc; @@ -393,13 +433,20 @@ public: typedef CheckerFn<void (ProgramStateRef,SymbolReaper &)> CheckLiveSymbolsFunc; typedef CheckerFn<ProgramStateRef (ProgramStateRef, - const StoreManager::InvalidatedSymbols *symbols, + const InvalidatedSymbols *symbols, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call)> CheckRegionChangesFunc; typedef CheckerFn<bool (ProgramStateRef)> WantsRegionChangeUpdateFunc; + + typedef CheckerFn<ProgramStateRef (ProgramStateRef, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool IsConst)> + CheckPointerEscapeFunc; typedef CheckerFn<ProgramStateRef (ProgramStateRef, const SVal &cond, bool assumption)> @@ -430,7 +477,7 @@ public: void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn); - void _registerForEndPath(CheckEndPathFunc checkfn); + void _registerForEndFunction(CheckEndFunctionFunc checkfn); void _registerForBranchCondition(CheckBranchConditionFunc checkfn); @@ -441,6 +488,10 @@ public: void _registerForRegionChanges(CheckRegionChangesFunc checkfn, WantsRegionChangeUpdateFunc wantUpdateFn); + void _registerForPointerEscape(CheckPointerEscapeFunc checkfn); + + void _registerForConstPointerEscape(CheckPointerEscapeFunc checkfn); + void _registerForEvalAssume(EvalAssumeFunc checkfn); void _registerForEvalCall(EvalCallFunc checkfn); @@ -552,7 +603,7 @@ private: std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers; - std::vector<CheckEndPathFunc> EndPathCheckers; + std::vector<CheckEndFunctionFunc> EndFunctionCheckers; std::vector<CheckBranchConditionFunc> BranchConditionCheckers; @@ -566,6 +617,8 @@ private: }; std::vector<RegionChangesCheckerInfo> RegionChangesCheckers; + std::vector<CheckPointerEscapeFunc> PointerEscapeCheckers; + std::vector<EvalAssumeFunc> EvalAssumeCheckers; std::vector<EvalCallFunc> EvalCallCheckers; diff --git a/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h b/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h index 6ce5b3c5095e..e981871ae4e0 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h +++ b/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_STATICANALYZER_CORE_CHECKEROPTINFO_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" namespace clang { namespace ento { diff --git a/include/clang/StaticAnalyzer/Core/CheckerRegistry.h b/include/clang/StaticAnalyzer/Core/CheckerRegistry.h index 1452d45073aa..4557aa40ea80 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerRegistry.h +++ b/include/clang/StaticAnalyzer/Core/CheckerRegistry.h @@ -10,8 +10,8 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H #define LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H -#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include <vector> // FIXME: move this information to an HTML file in docs/. diff --git a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h index 3aab648dc574..b856de7dc612 100644 --- a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h +++ b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h @@ -19,6 +19,7 @@ namespace clang { +class AnalyzerOptions; class Preprocessor; namespace ento { @@ -26,21 +27,18 @@ namespace ento { class PathDiagnosticConsumer; typedef std::vector<PathDiagnosticConsumer*> PathDiagnosticConsumers; -void createHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP); +#define CREATE_CONSUMER(NAME)\ +void create ## NAME ## DiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,\ + PathDiagnosticConsumers &C,\ + const std::string& prefix,\ + const Preprocessor &PP); -void createPlistDiagnosticConsumer(PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP); +CREATE_CONSUMER(HTML) +CREATE_CONSUMER(Plist) +CREATE_CONSUMER(PlistMultiFile) +CREATE_CONSUMER(TextPath) -void createPlistMultiFileDiagnosticConsumer(PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP); - -void createTextPathDiagnosticConsumer(PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP); +#undef CREATE_CONSUMER } // end 'ento' namespace } // end 'clang' namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h index 27f3677bba22..9502900f7e35 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h @@ -81,9 +81,12 @@ public: /// Tests whether a given value is losslessly representable using this type. /// - /// Note that signedness conversions will be rejected, even with the same bit - /// pattern. For example, -1s8 is not in range for 'unsigned char' (u8). - RangeTestResultKind testInRange(const llvm::APSInt &Val) const LLVM_READONLY; + /// \param Val The value to test. + /// \param AllowMixedSign Whether or not to allow signedness conversions. + /// This determines whether -1s8 is considered in range + /// for 'unsigned char' (u8). + RangeTestResultKind testInRange(const llvm::APSInt &Val, + bool AllowMixedSign) const LLVM_READONLY; bool operator==(const APSIntType &Other) const { return BitWidth == Other.BitWidth && IsUnsigned == Other.IsUnsigned; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h index 9038ae5276a7..458c8966e79e 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -100,7 +100,7 @@ public: } bool shouldInlineCall() const { - return options.IPAMode != None; + return options.getIPAMode() != IPAK_None; } CFG *getCFG(Decl const *D) { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index fb393548b1af..1135b511441d 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -16,9 +16,10 @@ #ifndef LLVM_CLANG_GR_BASICVALUEFACTORY_H #define LLVM_CLANG_GR_BASICVALUEFACTORY_H +#include "clang/AST/ASTContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" namespace clang { namespace ento { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index a6a91e2b66df..f990b8dcd0ec 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -16,11 +16,11 @@ #ifndef LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_CALL #define LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_CALL -#include "clang/Basic/SourceManager.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/Analysis/AnalysisContext.h" +#include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/PointerIntPair.h" @@ -162,11 +162,11 @@ protected: } - typedef SmallVectorImpl<const MemRegion *> RegionList; + typedef SmallVectorImpl<SVal> ValueList; /// \brief Used to specify non-argument regions that will be invalidated as a /// result of this call. - virtual void getExtraInvalidatedRegions(RegionList &Regions) const {} + virtual void getExtraInvalidatedValues(ValueList &Values) const {} public: virtual ~CallEvent() {} @@ -181,7 +181,7 @@ public: } /// \brief The state in which the call is being evaluated. - ProgramStateRef getState() const { + const ProgramStateRef &getState() const { return State; } @@ -228,6 +228,11 @@ public: return false; } + /// \brief Returns true if this is a call to a variadic function or method. + virtual bool isVariadic() const { + return false; + } + /// \brief Returns a source range for the entire call, suitable for /// outputting in diagnostics. virtual SourceRange getSourceRange() const { @@ -331,7 +336,9 @@ public: /// of some kind. static bool isCallStmt(const Stmt *S); - /// \brief Returns the result type of a function, method declaration. + /// \brief 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); // Iterator access to formal parameters and their types. @@ -416,6 +423,10 @@ public: return RuntimeDefinition(); } + virtual bool isVariadic() const { + return getDecl()->isVariadic(); + } + virtual bool argumentsMayEscape() const; virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, @@ -493,7 +504,7 @@ protected: BlockCall(const BlockCall &Other) : SimpleCall(Other) {} virtual void cloneTo(void *Dest) const { new (Dest) BlockCall(*this); } - virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + virtual void getExtraInvalidatedValues(ValueList &Values) const; public: /// \brief Returns the region associated with this instance of the block. @@ -516,6 +527,10 @@ public: return RuntimeDefinition(getBlockDecl()); } + virtual bool isVariadic() const { + return getBlockDecl()->isVariadic(); + } + virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const; @@ -533,7 +548,7 @@ public: /// it is written. class CXXInstanceCall : public AnyFunctionCall { protected: - virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + virtual void getExtraInvalidatedValues(ValueList &Values) const; CXXInstanceCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) @@ -716,7 +731,7 @@ protected: CXXConstructorCall(const CXXConstructorCall &Other) : AnyFunctionCall(Other){} virtual void cloneTo(void *Dest) const { new (Dest) CXXConstructorCall(*this); } - virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + virtual void getExtraInvalidatedValues(ValueList &Values) const; public: virtual const CXXConstructExpr *getOriginExpr() const { @@ -815,7 +830,7 @@ protected: ObjCMethodCall(const ObjCMethodCall &Other) : CallEvent(Other) {} virtual void cloneTo(void *Dest) const { new (Dest) ObjCMethodCall(*this); } - virtual void getExtraInvalidatedRegions(RegionList &Regions) const; + virtual void getExtraInvalidatedValues(ValueList &Values) const; /// Check if the selector may have multiple definitions (may have overrides). virtual bool canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, @@ -834,6 +849,9 @@ public: virtual const Expr *getArgExpr(unsigned Index) const { return getOriginExpr()->getArg(Index); } + virtual bool isVariadic() const { + return getDecl()->isVariadic(); + } bool isInstanceMessage() const { return getOriginExpr()->isInstanceMessage(); @@ -1024,7 +1042,7 @@ namespace llvm { typedef const T *SimpleType; static SimpleType - getSimplifiedValue(const clang::ento::CallEventRef<T>& Val) { + getSimplifiedValue(clang::ento::CallEventRef<T> Val) { return Val.getPtr(); } }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 4558cd9c9480..cda1366a4399 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -119,7 +119,7 @@ public: /// 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; } - ProgramStateRef getState() const { return Pred->getState(); } + const ProgramStateRef &getState() const { return Pred->getState(); } /// \brief Check if the checker changed the state of the execution; ex: added /// a new transition or a bug report. @@ -185,7 +185,7 @@ public: /// example, for finding variables that the given symbol was assigned to. static const MemRegion *getLocationRegionIfPostStore(const ExplodedNode *N) { ProgramPoint L = N->getLocation(); - if (const PostStore *PSL = dyn_cast<PostStore>(&L)) + if (Optional<PostStore> PSL = L.getAs<PostStore>()) return reinterpret_cast<const MemRegion*>(PSL->getLocationValue()); return 0; } @@ -303,14 +303,6 @@ private: } }; -/// \brief A helper class which wraps a boolean value set to false by default. -struct DefaultBool { - bool Val; - DefaultBool() : Val(false) {} - operator bool() const { return Val; } - DefaultBool &operator=(bool b) { Val = b; return *this; } -}; - } // 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 4a78849024ae..1e76ea6ed647 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -14,8 +14,8 @@ #ifndef LLVM_CLANG_GR_CONSTRAINT_MANAGER_H #define LLVM_CLANG_GR_CONSTRAINT_MANAGER_H -#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/Support/SaveAndRestore.h" namespace llvm { @@ -28,7 +28,7 @@ namespace ento { class SubEngine; class ConditionTruthVal { - llvm::Optional<bool> Val; + Optional<bool> Val; public: /// Construct a ConditionTruthVal indicating the constraint is constrained /// to either true or false, depending on the boolean value provided. @@ -78,9 +78,13 @@ public: // If StTrue is infeasible, asserting the falseness of Cond is unnecessary // because the existing constraints already establish this. if (!StTrue) { - // FIXME: This is fairly expensive and should be disabled even in - // Release+Asserts builds. +#ifndef __OPTIMIZE__ + // This check is expensive and should be disabled even in Release+Asserts + // builds. + // FIXME: __OPTIMIZE__ is a GNU extension that Clang implements but MSVC + // does not. Is there a good equivalent there? assert(assume(State, Cond, false) && "System is over constrained."); +#endif return ProgramStatePair((ProgramStateRef)NULL, State); } @@ -118,7 +122,7 @@ public: /// Convenience method to query the state to see if a symbol is null or /// not null, or if neither assumption can be made. ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym) { - llvm::SaveAndRestore<bool> DisableNotify(NotifyAssumeClients, false); + SaveAndRestore<bool> DisableNotify(NotifyAssumeClients, false); return checkNull(State, Sym); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index b6686409e5aa..a2e211edea8b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -17,10 +17,10 @@ #include "clang/AST/Expr.h" #include "clang/Analysis/AnalysisContext.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/WorkList.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" #include "llvm/ADT/OwningPtr.h" namespace clang { @@ -96,6 +96,10 @@ private: void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, ExplodedNode *Pred); + /// Handle conditional logic for running static initializers. + void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred); + private: CoreEngine(const CoreEngine &) LLVM_DELETED_FUNCTION; void operator=(const CoreEngine &) LLVM_DELETED_FUNCTION; @@ -463,7 +467,7 @@ public: bool operator!=(const iterator &X) const { return I != X.I; } const LabelDecl *getLabel() const { - return llvm::cast<LabelStmt>((*I)->getLabel())->getDecl(); + return cast<LabelStmt>((*I)->getLabel())->getDecl(); } const CFGBlock *getBlock() const { @@ -510,7 +514,7 @@ public: bool operator==(const iterator &X) const { return I == X.I; } const CaseStmt *getCase() const { - return llvm::cast<CaseStmt>((*I)->getLabel()); + return cast<CaseStmt>((*I)->getLabel()); } const CFGBlock *getBlock() const { @@ -522,7 +526,7 @@ public: iterator end() { return iterator(Src->succ_rend()); } const SwitchStmt *getSwitch() const { - return llvm::cast<SwitchStmt>(Src->getTerminator()); + return cast<SwitchStmt>(Src->getTerminator()); } ExplodedNode *generateCaseStmtNode(const iterator &I, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h index eb9bd85fe64d..f3a582da040f 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h @@ -33,9 +33,6 @@ class SValBuilder; /// other things. class EnvironmentEntry : public std::pair<const Stmt*, const StackFrameContext *> { - friend class EnvironmentManager; - EnvironmentEntry makeLocation() const; - public: EnvironmentEntry(const Stmt *s, const LocationContext *L); @@ -118,13 +115,6 @@ public: /// Bind a symbolic value to the given environment entry. Environment bindExpr(Environment Env, const EnvironmentEntry &E, SVal V, bool Invalidate); - - /// Bind the location 'location' and value 'V' to the specified - /// environment entry. - Environment bindExprAndLocation(Environment Env, - const EnvironmentEntry &E, - SVal location, - SVal V); Environment removeDeadBindings(Environment Env, SymbolReaper &SymReaper, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index b112e66d30d3..5211916407c8 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -19,19 +19,19 @@ #ifndef LLVM_CLANG_GR_EXPLODEDGRAPH #define LLVM_CLANG_GR_EXPLODEDGRAPH -#include "clang/Analysis/ProgramPoint.h" -#include "clang/Analysis/AnalysisContext.h" #include "clang/AST/Decl.h" -#include "llvm/ADT/SmallVector.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Analysis/Support/BumpVector.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Allocator.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/GraphTraits.h" -#include "llvm/ADT/DepthFirstIterator.h" #include "llvm/Support/Casting.h" -#include "clang/Analysis/Support/BumpVector.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include <vector> namespace clang { @@ -152,10 +152,12 @@ public: return *getLocationContext()->getAnalysis<T>(); } - ProgramStateRef getState() const { return State; } + const ProgramStateRef &getState() const { return State; } template <typename T> - const T* getLocationAs() const { return llvm::dyn_cast<T>(&Location); } + Optional<T> getLocationAs() const LLVM_LVALUE_FUNCTION { + return Location.getAs<T>(); + } static void Profile(llvm::FoldingSetNodeID &ID, const ProgramPoint &Loc, @@ -167,7 +169,8 @@ public: } void Profile(llvm::FoldingSetNodeID& ID) const { - Profile(ID, getLocation(), getState(), isSink()); + // We avoid copy constructors by not using accessors. + Profile(ID, Location, State, isSink()); } /// addPredeccessor - Adds a predecessor to the current node, and @@ -236,18 +239,8 @@ private: void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } }; -// FIXME: Is this class necessary? -class InterExplodedGraphMap { - virtual void anchor(); - llvm::DenseMap<const ExplodedNode*, ExplodedNode*> M; - friend class ExplodedGraph; - -public: - ExplodedNode *getMappedNode(const ExplodedNode *N) const; - - InterExplodedGraphMap() {} - virtual ~InterExplodedGraphMap() {} -}; +typedef llvm::DenseMap<const ExplodedNode *, const ExplodedNode *> + InterExplodedGraphMap; class ExplodedGraph { protected: @@ -365,14 +358,19 @@ public: typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> NodeMap; - std::pair<ExplodedGraph*, InterExplodedGraphMap*> - Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, - llvm::DenseMap<const void*, const void*> *InverseMap = 0) const; - - ExplodedGraph* TrimInternal(const ExplodedNode* const * NBeg, - const ExplodedNode* const * NEnd, - InterExplodedGraphMap *M, - llvm::DenseMap<const void*, const void*> *InverseMap) const; + /// Creates a trimmed version of the graph that only contains paths leading + /// to the given nodes. + /// + /// \param Nodes The nodes which must appear in the final graph. Presumably + /// these are end-of-path nodes (i.e. they have no successors). + /// \param[out] ForwardMap A optional map from nodes in this graph to nodes in + /// the returned graph. + /// \param[out] InverseMap An optional map from nodes in the returned graph to + /// nodes in this graph. + /// \returns The trimmed graph + ExplodedGraph *trim(ArrayRef<const NodeTy *> Nodes, + InterExplodedGraphMap *ForwardMap = 0, + InterExplodedGraphMap *InverseMap = 0) const; /// Enable tracking of recently allocated nodes for potential reclamation /// when calling reclaimRecentlyAllocatedNodes(). @@ -384,6 +382,10 @@ public: /// was called. void reclaimRecentlyAllocatedNodes(); + /// \brief Returns true if nodes for the given expression kind are always + /// kept around. + static bool isInterestingLValueExpr(const Expr *Ex); + private: bool shouldCollect(const ExplodedNode *node); void collectNode(ExplodedNode *node); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 78b254222e9e..33e4431eb4be 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -16,15 +16,15 @@ #ifndef LLVM_CLANG_GR_EXPRENGINE #define LLVM_CLANG_GR_EXPRENGINE +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" #include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/AST/Expr.h" -#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" namespace clang { @@ -44,8 +44,19 @@ namespace ento { class AnalysisManager; class CallEvent; class SimpleCall; +class CXXConstructorCall; class ExprEngine : public SubEngine { +public: + /// The modes of inlining, which override the default analysis-wide settings. + enum InliningModes { + /// Follow the default settings for inlining callees. + Inline_Regular = 0, + /// Do minimal inlining of callees. + Inline_Minimal = 0x1 + }; + +private: AnalysisManager &AMgr; AnalysisDeclContextManager &AnalysisDeclContexts; @@ -64,15 +75,6 @@ class ExprEngine : public SubEngine { /// svalBuilder - SValBuilder object that creates SVals from expressions. SValBuilder &svalBuilder; - /// EntryNode - The immediate predecessor node. - ExplodedNode *EntryNode; - - /// CleanedState - The state for EntryNode "cleaned" of all dead - /// variables and symbols (as determined by a liveness analysis). - ProgramStateRef CleanedState; - - /// currStmt - The current block-level statement. - const Stmt *currStmt; unsigned int currStmtIdx; const NodeBuilderContext *currBldrCtx; @@ -92,10 +94,14 @@ class ExprEngine : public SubEngine { /// AnalysisConsumer. It can be null. SetOfConstDecls *VisitedCallees; + /// The flag, which specifies the mode of inlining for the engine. + InliningModes HowToInline; + public: ExprEngine(AnalysisManager &mgr, bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, - FunctionSummariesTy *FS); + FunctionSummariesTy *FS, + InliningModes HowToInlineIn); ~ExprEngine(); @@ -140,11 +146,12 @@ public: void enqueueEndOfPath(ExplodedNodeSet &S); void GenerateCallExitNode(ExplodedNode *N); - /// ViewGraph - Visualize the ExplodedGraph created by executing the - /// simulation. + /// Visualize the ExplodedGraph created by executing the simulation. void ViewGraph(bool trim = false); - void ViewGraph(ExplodedNode** Beg, ExplodedNode** End); + /// Visualize a trimmed ExplodedGraph that only contains paths to the given + /// nodes. + void ViewGraph(ArrayRef<const ExplodedNode*> Nodes); /// getInitialState - Return the initial state used for the root vertex /// in the ExplodedGraph. @@ -154,26 +161,33 @@ public: const ExplodedGraph& getGraph() const { return G; } /// \brief Run the analyzer's garbage collection - remove dead symbols and - /// bindings. + /// bindings from the state. /// - /// \param Node - The predecessor node, from which the processing should - /// start. - /// \param Out - The returned set of output nodes. - /// \param ReferenceStmt - Run garbage collection using the symbols, - /// which are live before the given statement. - /// \param LC - The location context of the ReferenceStmt. - /// \param DiagnosticStmt - the statement used to associate the diagnostic - /// message, if any warnings should occur while removing the dead (leaks - /// are usually reported here). - /// \param K - In some cases it is possible to use PreStmt kind. (Do - /// not use it unless you know what you are doing.) - /// If the ReferenceStmt is NULL, everything is this and parent contexts is - /// considered live. - /// If the stack frame context is NULL, everything on stack is considered - /// dead. + /// Checkers can participate in this process with two callbacks: + /// \c checkLiveSymbols and \c checkDeadSymbols. See the CheckerDocumentation + /// class for more information. + /// + /// \param Node The predecessor node, from which the processing should start. + /// \param Out The returned set of output nodes. + /// \param ReferenceStmt The statement which is about to be processed. + /// Everything needed for this statement should be considered live. + /// A null statement means that everything in child LocationContexts + /// is dead. + /// \param LC The location context of the \p ReferenceStmt. A null location + /// context means that we have reached the end of analysis and that + /// all statements and local variables should be considered dead. + /// \param DiagnosticStmt Used as a location for any warnings that should + /// occur while removing the dead (e.g. leaks). By default, the + /// \p ReferenceStmt is used. + /// \param K Denotes whether this is a pre- or post-statement purge. This + /// must only be ProgramPoint::PostStmtPurgeDeadSymbolsKind if an + /// entire location context is being cleared, in which case the + /// \p ReferenceStmt must either be a ReturnStmt or \c NULL. Otherwise, + /// it must be ProgramPoint::PreStmtPurgeDeadSymbolsKind (the default) + /// and \p ReferenceStmt must be valid (non-null). void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out, - const Stmt *ReferenceStmt, const StackFrameContext *LC, - const Stmt *DiagnosticStmt, + const Stmt *ReferenceStmt, const LocationContext *LC, + const Stmt *DiagnosticStmt = 0, ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind); /// processCFGElement - Called by CoreEngine. Used to generate new successor @@ -210,6 +224,15 @@ public: const CFGBlock *DstT, const CFGBlock *DstF); + /// Called by CoreEngine. Used to processing branching behavior + /// at static initalizers. + void processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF); + /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void processIndirectGoto(IndirectGotoNodeBuilder& builder); @@ -218,8 +241,8 @@ public: /// nodes by processing the 'effects' of a switch statement. void processSwitch(SwitchNodeBuilder& builder); - /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path - /// nodes when the control reaches the end of a function. + /// Called by CoreEngine. Used to generate end-of-path + /// nodes when the control reaches the end of a function. void processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred); @@ -250,7 +273,7 @@ public: /// to the store. Used to update checkers that track region values. ProgramStateRef processRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call); @@ -416,11 +439,11 @@ public: geteagerlyAssumeBinOpBifurcationTags(); SVal evalMinus(SVal X) { - return X.isValid() ? svalBuilder.evalMinus(cast<NonLoc>(X)) : X; + return X.isValid() ? svalBuilder.evalMinus(X.castAs<NonLoc>()) : X; } SVal evalComplement(SVal X) { - return X.isValid() ? svalBuilder.evalComplement(cast<NonLoc>(X)) : X; + return X.isValid() ? svalBuilder.evalComplement(X.castAs<NonLoc>()) : X; } public: @@ -432,7 +455,8 @@ public: SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc L, SVal R, QualType T) { - return R.isValid() ? svalBuilder.evalBinOpNN(state,op,L, cast<NonLoc>(R), T) : R; + return R.isValid() ? svalBuilder.evalBinOpNN(state, op, L, + R.castAs<NonLoc>(), T) : R; } SVal evalBinOp(ProgramStateRef ST, BinaryOperator::Opcode Op, @@ -447,6 +471,20 @@ protected: SVal location, SVal Val, bool atDeclInit = false, const ProgramPoint *PP = 0); + /// Call PointerEscape callback when a value escapes as a result of bind. + ProgramStateRef processPointerEscapedOnBind(ProgramStateRef State, + SVal Loc, SVal Val); + /// Call PointerEscape callback when a value escapes as a result of + /// region invalidation. + /// \param[in] IsConst Specifies that the pointer is const. + ProgramStateRef notifyCheckersOfPointerEscape( + ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call, + bool IsConst); + public: // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. @@ -506,7 +544,10 @@ private: void examineStackFrames(const Decl *D, const LocationContext *LCtx, bool &IsRecursive, unsigned &StackDepth); - bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred); + /// Checks our policies and decides weither the given call should be inlined. + bool shouldInlineCall(const CallEvent &Call, const Decl *D, + const ExplodedNode *Pred); + bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); @@ -522,6 +563,22 @@ private: ExplodedNode *Pred); bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC); + + /// Models a trivial copy or move constructor or trivial assignment operator + /// call with a simple bind. + void performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &Call); + + /// If the value of the given expression is a NonLoc, copy it into a new + /// temporary object region, and replace the value of the expression with + /// that. + /// + /// If \p ResultE is provided, the new region will be bound to this expression + /// instead of \p E. + ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State, + const LocationContext *LC, + const Expr *E, + const Expr *ResultE = 0); }; /// Traits for storing the call processing policy inside GDM. @@ -531,7 +588,7 @@ private: struct ReplayWithoutInlining{}; template <> struct ProgramStateTrait<ReplayWithoutInlining> : - public ProgramStatePartialTrait<void*> { + public ProgramStatePartialTrait<const void*> { static void *GDMIndex() { static int index = 0; return &index; } }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h index cf4a6929a3aa..169af939f08e 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h @@ -7,94 +7,126 @@ // //===----------------------------------------------------------------------===// // -// This file defines a summary of a function gathered/used by static analyzes. +// This file defines a summary of a function gathered/used by static analysis. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_GR_FUNCTIONSUMMARY_H #define LLVM_CLANG_GR_FUNCTIONSUMMARY_H -#include <deque> -#include "clang/AST/Decl.h" +#include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallBitVector.h" +#include <deque> namespace clang { +class Decl; + namespace ento { typedef std::deque<Decl*> SetOfDecls; typedef llvm::DenseSet<const Decl*> SetOfConstDecls; class FunctionSummariesTy { - struct FunctionSummary { - /// True if this function has reached a max block count while inlined from - /// at least one call site. - bool MayReachMaxBlockCount; + class FunctionSummary { + public: + /// Marks the IDs of the basic blocks visited during the analyzes. + llvm::SmallBitVector VisitedBasicBlocks; /// Total number of blocks in the function. - unsigned TotalBasicBlocks; + unsigned TotalBasicBlocks : 30; - /// Marks the IDs of the basic blocks visited during the analyzes. - llvm::BitVector VisitedBasicBlocks; + /// True if this function has been checked against the rules for which + /// functions may be inlined. + unsigned InlineChecked : 1; + + /// True if this function may be inlined. + unsigned MayInline : 1; + + /// The number of times the function has been inlined. + unsigned TimesInlined : 32; FunctionSummary() : - MayReachMaxBlockCount(false), TotalBasicBlocks(0), - VisitedBasicBlocks(0) {} + InlineChecked(0), + TimesInlined(0) {} }; - typedef llvm::DenseMap<const Decl*, FunctionSummary*> MapTy; + typedef llvm::DenseMap<const Decl *, FunctionSummary> MapTy; MapTy Map; public: - ~FunctionSummariesTy(); - MapTy::iterator findOrInsertSummary(const Decl *D) { MapTy::iterator I = Map.find(D); if (I != Map.end()) return I; - FunctionSummary *DS = new FunctionSummary(); - I = Map.insert(std::pair<const Decl*, FunctionSummary*>(D, DS)).first; + + typedef std::pair<const Decl *, FunctionSummary> KVPair; + I = Map.insert(KVPair(D, FunctionSummary())).first; assert(I != Map.end()); return I; } - void markReachedMaxBlockCount(const Decl* D) { + void markMayInline(const Decl *D) { MapTy::iterator I = findOrInsertSummary(D); - I->second->MayReachMaxBlockCount = true; + I->second.InlineChecked = 1; + I->second.MayInline = 1; } - bool hasReachedMaxBlockCount(const Decl* D) { - MapTy::const_iterator I = Map.find(D); - if (I != Map.end()) - return I->second->MayReachMaxBlockCount; - return false; + void markShouldNotInline(const Decl *D) { + MapTy::iterator I = findOrInsertSummary(D); + I->second.InlineChecked = 1; + I->second.MayInline = 0; + } + + void markReachedMaxBlockCount(const Decl *D) { + markShouldNotInline(D); + } + + Optional<bool> mayInline(const Decl *D) { + MapTy::const_iterator I = Map.find(D); + if (I != Map.end() && I->second.InlineChecked) + return I->second.MayInline; + return None; } void markVisitedBasicBlock(unsigned ID, const Decl* D, unsigned TotalIDs) { MapTy::iterator I = findOrInsertSummary(D); - llvm::BitVector &Blocks = I->second->VisitedBasicBlocks; + llvm::SmallBitVector &Blocks = I->second.VisitedBasicBlocks; assert(ID < TotalIDs); if (TotalIDs > Blocks.size()) { Blocks.resize(TotalIDs); - I->second->TotalBasicBlocks = TotalIDs; + I->second.TotalBasicBlocks = TotalIDs; } - Blocks[ID] = true; + Blocks.set(ID); } unsigned getNumVisitedBasicBlocks(const Decl* D) { MapTy::const_iterator I = Map.find(D); - if (I != Map.end()) - return I->second->VisitedBasicBlocks.count(); + if (I != Map.end()) + return I->second.VisitedBasicBlocks.count(); return 0; } + unsigned getNumTimesInlined(const Decl* D) { + MapTy::const_iterator I = Map.find(D); + if (I != Map.end()) + return I->second.TimesInlined; + return 0; + } + + void bumpNumTimesInlined(const Decl* D) { + MapTy::iterator I = findOrInsertSummary(D); + I->second.TimesInlined++; + } + /// Get the percentage of the reachable blocks. unsigned getPercentBlocksReachable(const Decl *D) { MapTy::const_iterator I = Map.find(D); if (I != Map.end()) - return ((I->second->VisitedBasicBlocks.count() * 100) / - I->second->TotalBasicBlocks); + return ((I->second.VisitedBasicBlocks.count() * 100) / + I->second.TotalBasicBlocks); return 0; } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 34fbc3ca9e83..af2f365ead96 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -22,8 +22,8 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "llvm/Support/ErrorHandling.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/ErrorHandling.h" #include <string> namespace llvm { @@ -642,26 +642,20 @@ public: explicit referenced_vars_iterator(const MemRegion * const *r, const MemRegion * const *originalR) : R(r), OriginalR(originalR) {} - - operator const MemRegion * const *() const { - return R; - } - - const MemRegion *getCapturedRegion() const { - return *R; - } - const MemRegion *getOriginalRegion() const { - return *OriginalR; - } - const VarRegion* operator*() const { + const VarRegion *getCapturedRegion() const { return cast<VarRegion>(*R); } - + const VarRegion *getOriginalRegion() const { + return cast<VarRegion>(*OriginalR); + } + bool operator==(const referenced_vars_iterator &I) const { + assert((R == 0) == (I.R == 0)); return I.R == R; } bool operator!=(const referenced_vars_iterator &I) const { + assert((R == 0) == (I.R == 0)); return I.R != R; } referenced_vars_iterator &operator++() { @@ -670,6 +664,10 @@ public: return *this; } }; + + /// Return the original region for a captured region, if + /// one exists. + const VarRegion *getOriginalRegion(const VarRegion *VR) const; referenced_vars_iterator referenced_vars_begin() const; referenced_vars_iterator referenced_vars_end() const; @@ -686,6 +684,8 @@ public: } private: void LazyInitializeReferencedVars(); + std::pair<const VarRegion *, const VarRegion *> + getCaptureRegions(const VarDecl *VD); }; /// SymbolicRegion - A special, "non-concrete" region. Unlike other region @@ -952,6 +952,9 @@ public: const ObjCIvarDecl *getDecl() const; QualType getValueType() const; + bool canPrintPretty() const; + void printPretty(raw_ostream &os) const; + void dumpToStream(raw_ostream &os) const; static bool classof(const MemRegion* R) { @@ -993,8 +996,8 @@ class ElementRegion : public TypedValueRegion { ElementRegion(QualType elementType, NonLoc Idx, const MemRegion* sReg) : TypedValueRegion(sReg, ElementRegionKind), ElementType(elementType), Index(Idx) { - assert((!isa<nonloc::ConcreteInt>(&Idx) || - cast<nonloc::ConcreteInt>(&Idx)->getValue().isSigned()) && + assert((!Idx.getAs<nonloc::ConcreteInt>() || + Idx.castAs<nonloc::ConcreteInt>().getValue().isSigned()) && "The index must be signed"); } @@ -1057,16 +1060,18 @@ public: class CXXBaseObjectRegion : public TypedValueRegion { friend class MemRegionManager; - const CXXRecordDecl *decl; + llvm::PointerIntPair<const CXXRecordDecl *, 1, bool> Data; - CXXBaseObjectRegion(const CXXRecordDecl *d, const MemRegion *sReg) - : TypedValueRegion(sReg, CXXBaseObjectRegionKind), decl(d) {} + CXXBaseObjectRegion(const CXXRecordDecl *RD, bool IsVirtual, + const MemRegion *SReg) + : TypedValueRegion(SReg, CXXBaseObjectRegionKind), Data(RD, IsVirtual) {} - static void ProfileRegion(llvm::FoldingSetNodeID &ID, - const CXXRecordDecl *decl, const MemRegion *sReg); + static void ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD, + bool IsVirtual, const MemRegion *SReg); public: - const CXXRecordDecl *getDecl() const { return decl; } + const CXXRecordDecl *getDecl() const { return Data.getPointer(); } + bool isVirtual() const { return Data.getInt(); } QualType getValueType() const; @@ -1216,15 +1221,21 @@ public: const CXXTempObjectRegion *getCXXTempObjectRegion(Expr const *Ex, LocationContext const *LC); - const CXXBaseObjectRegion *getCXXBaseObjectRegion(const CXXRecordDecl *decl, - const MemRegion *superRegion); + /// Create a CXXBaseObjectRegion with the given base class for region + /// \p Super. + /// + /// The type of \p Super is assumed be a class deriving from \p BaseClass. + const CXXBaseObjectRegion * + getCXXBaseObjectRegion(const CXXRecordDecl *BaseClass, const MemRegion *Super, + bool IsVirtual); /// Create a CXXBaseObjectRegion with the same CXXRecordDecl but a different /// super region. const CXXBaseObjectRegion * getCXXBaseObjectRegionWithSuper(const CXXBaseObjectRegion *baseReg, const MemRegion *superRegion) { - return getCXXBaseObjectRegion(baseReg->getDecl(), superRegion); + return getCXXBaseObjectRegion(baseReg->getDecl(), superRegion, + baseReg->isVirtual()); } const FunctionTextRegion *getFunctionTextRegion(const NamedDecl *FD); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index 86c94deab5e8..6ea7211090ba 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -18,13 +18,13 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h" -#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/PointerIntPair.h" namespace llvm { class APSInt; @@ -170,19 +170,30 @@ public: // If no new state is feasible, NULL is returned. // + /// Assumes that the value of \p cond is zero (if \p assumption is "false") + /// or non-zero (if \p assumption is "true"). + /// + /// 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; - /// This method assumes both "true" and "false" for 'cond', and - /// returns both corresponding states. It's shorthand for doing - /// 'assume' twice. - std::pair<ProgramStateRef , ProgramStateRef > + /// 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> assume(DefinedOrUnknownSVal cond) const; ProgramStateRef assumeInBound(DefinedOrUnknownSVal idx, DefinedOrUnknownSVal upperBound, bool assumption, QualType IndexType = QualType()) const; - + + /// \brief Check if the given SVal is constrained to zero or is a zero + /// constant. + ConditionTruthVal isNull(SVal V) const; + /// Utility method for getting regions. const VarRegion* getRegion(const VarDecl *D, const LocationContext *LC) const; @@ -203,12 +214,6 @@ public: ProgramStateRef BindExpr(const Stmt *S, const LocationContext *LCtx, SVal V, bool Invalidate = true) const; - /// Create a new state by binding the value 'V' and location 'locaton' to the - /// statement 'S' in the state's environment. - ProgramStateRef bindExprAndLocation(const Stmt *S, - const LocationContext *LCtx, - SVal location, SVal V) const; - ProgramStateRef bindLoc(Loc location, SVal V, bool notifyChanges = true) const; @@ -219,14 +224,38 @@ public: ProgramStateRef killBinding(Loc LV) const; - /// invalidateRegions - Returns the state with bindings for the given regions - /// cleared from the store. The regions are provided as a continuous array - /// from Begin to End. Optionally invalidates global regions as well. - ProgramStateRef invalidateRegions(ArrayRef<const MemRegion *> Regions, - const Expr *E, unsigned BlockCount, - const LocationContext *LCtx, - StoreManager::InvalidatedSymbols *IS = 0, - const CallEvent *Call = 0) const; + /// \brief Returns the state with bindings for the given regions + /// cleared from the store. + /// + /// Optionally invalidates global regions as well. + /// + /// \param Regions the set of regions to be invalidated. + /// \param E the expression that caused the invalidation. + /// \param BlockCount The number of times the current basic block has been + // visited. + /// \param CausesPointerEscape the flag is set to true when + /// the invalidation entails escape of a symbol (representing a + /// pointer). For example, due to it being passed as an argument in a + /// call. + /// \param IS the set of invalidated symbols. + /// \param Call if non-null, the invalidated regions represent parameters to + /// the call and should be considered directly invalidated. + /// \param ConstRegions the set of regions whose contents are accessible, + /// even though the regions themselves should not be invalidated. + ProgramStateRef + invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, + unsigned BlockCount, const LocationContext *LCtx, + bool CausesPointerEscape, InvalidatedSymbols *IS = 0, + const CallEvent *Call = 0, + ArrayRef<const MemRegion *> ConstRegions = + ArrayRef<const MemRegion *>()) const; + + ProgramStateRef + invalidateRegions(ArrayRef<SVal> Regions, const Expr *E, + unsigned BlockCount, const LocationContext *LCtx, + bool CausesPointerEscape, InvalidatedSymbols *IS = 0, + const CallEvent *Call = 0, + ArrayRef<SVal> ConstRegions = ArrayRef<SVal>()) const; /// enterStackFrame - Returns the state for entry to the given stack frame, /// preserving the current state. @@ -396,13 +425,17 @@ public: private: friend void ProgramStateRetain(const ProgramState *state); friend void ProgramStateRelease(const ProgramState *state); - - ProgramStateRef - invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, + + /// \sa invalidateValues() + /// \sa invalidateRegions() + ProgramStateRef + invalidateRegionsImpl(ArrayRef<SVal> Values, const Expr *E, unsigned BlockCount, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols &IS, - const CallEvent *Call) const; + bool ResultsInSymbolEscape, + InvalidatedSymbols &IS, + const CallEvent *Call, + ArrayRef<SVal> ConstValues) const; }; //===----------------------------------------------------------------------===// @@ -611,22 +644,24 @@ inline ProgramStateRef ProgramState::assume(DefinedOrUnknownSVal Cond, bool Assumption) const { if (Cond.isUnknown()) return this; - - return getStateManager().ConstraintMgr->assume(this, cast<DefinedSVal>(Cond), - Assumption); + + return getStateManager().ConstraintMgr + ->assume(this, Cond.castAs<DefinedSVal>(), Assumption); } inline std::pair<ProgramStateRef , ProgramStateRef > ProgramState::assume(DefinedOrUnknownSVal Cond) const { if (Cond.isUnknown()) return std::make_pair(this, this); - - return getStateManager().ConstraintMgr->assumeDual(this, - cast<DefinedSVal>(Cond)); + + return getStateManager().ConstraintMgr + ->assumeDual(this, Cond.castAs<DefinedSVal>()); } inline ProgramStateRef ProgramState::bindLoc(SVal LV, SVal V) const { - return !isa<Loc>(LV) ? this : bindLoc(cast<Loc>(LV), V); + if (Optional<Loc> L = LV.getAs<Loc>()) + return bindLoc(*L, V); + return this; } inline Loc ProgramState::getLValue(const VarDecl *VD, @@ -660,7 +695,7 @@ inline SVal ProgramState::getLValue(const IndirectFieldDecl *D, } inline SVal ProgramState::getLValue(QualType ElementType, SVal Idx, SVal Base) const{ - if (NonLoc *N = dyn_cast<NonLoc>(&Idx)) + if (Optional<NonLoc> N = Idx.getAs<NonLoc>()) return getStateManager().StoreMgr->getLValueElement(ElementType, *N, Base); return UnknownVal(); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h index ea2a8525ba47..eb52ae47bdf2 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h @@ -18,6 +18,8 @@ #ifndef LLVM_CLANG_GR_PROGRAMSTATETRAIT_H #define LLVM_CLANG_GR_PROGRAMSTATETRAIT_H +#include "llvm/Support/DataTypes.h" + namespace llvm { class BumpPtrAllocator; template <typename K, typename D, typename I> class ImmutableMap; @@ -165,7 +167,7 @@ namespace ento { } static inline void *MakeVoidPtr(data_type D) { - return (void*) D.getInternalPointer(); + return const_cast<llvm::ImmutableListImpl<T> *>(D.getInternalPointer()); } static inline context_type MakeContext(void *p) { @@ -221,7 +223,20 @@ namespace ento { } }; -} // end GR namespace + // Partial specialization for const void *. + template <> struct ProgramStatePartialTrait<const void *> { + typedef const void *data_type; + + static inline data_type MakeData(void * const *p) { + return p ? *p : data_type(); + } + + static inline void *MakeVoidPtr(data_type d) { + return const_cast<void *>(d); + } + }; + +} // end ento namespace } // end clang namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index 5d72e73a3d94..f7e49a3c7539 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -17,11 +17,10 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" namespace clang { @@ -124,7 +123,7 @@ public: ProgramStateManager &getStateManager() { return StateMgr; } QualType getConditionType() const { - return getContext().IntTy; + return Context.getLangOpts().CPlusPlus ? Context.BoolTy : Context.IntTy; } QualType getArrayIndexType() const { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index c2134cf04826..1c5519e9e785 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -16,8 +16,8 @@ #define LLVM_CLANG_GR_RVALUE_H #include "clang/Basic/LLVM.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/ImmutableList.h" //==------------------------------------------------------------------------==// @@ -33,7 +33,7 @@ class LazyCompoundValData; class ProgramState; class BasicValueFactory; class MemRegion; -class TypedRegion; +class TypedValueRegion; class MemRegionManager; class ProgramStateManager; class SValBuilder; @@ -69,6 +69,29 @@ protected: public: explicit SVal() : Data(0), Kind(0) {} + /// \brief Convert to the specified SVal type, asserting that this SVal is of + /// the desired type. + template<typename T> + T castAs() const { + assert(T::isKind(*this)); + T t; + SVal& sv = t; + sv = *this; + return t; + } + + /// \brief Convert to the specified SVal type, returning None if this SVal is + /// not of the desired type. + template<typename T> + Optional<T> getAs() const { + if (!T::isKind(*this)) + return None; + T t; + SVal& sv = t; + sv = *this; + return t; + } + /// BufferTy - A temporary buffer to hold a set of SVals. typedef SmallVector<SVal,5> BufferTy; @@ -161,29 +184,32 @@ class UndefinedVal : public SVal { public: UndefinedVal() : SVal(UndefinedKind) {} - static inline bool classof(const SVal* V) { - return V->getBaseKind() == UndefinedKind; +private: + friend class SVal; + static bool isKind(const SVal& V) { + return V.getBaseKind() == UndefinedKind; } }; class DefinedOrUnknownSVal : public SVal { private: - // Do not implement. We want calling these methods to be a compiler - // error since they are tautologically false. - bool isUndef() const; - bool isValid() const; + // We want calling these methods to be a compiler error since they are + // tautologically false. + bool isUndef() const LLVM_DELETED_FUNCTION; + bool isValid() const LLVM_DELETED_FUNCTION; protected: + DefinedOrUnknownSVal() {} explicit DefinedOrUnknownSVal(const void *d, bool isLoc, unsigned ValKind) : SVal(d, isLoc, ValKind) {} explicit DefinedOrUnknownSVal(BaseKind k, void *D = NULL) : SVal(k, D) {} -public: - // Implement isa<T> support. - static inline bool classof(const SVal *V) { - return !V->isUndef(); +private: + friend class SVal; + static bool isKind(const SVal& V) { + return !V.isUndef(); } }; @@ -191,61 +217,79 @@ class UnknownVal : public DefinedOrUnknownSVal { public: explicit UnknownVal() : DefinedOrUnknownSVal(UnknownKind) {} - static inline bool classof(const SVal *V) { - return V->getBaseKind() == UnknownKind; +private: + friend class SVal; + static bool isKind(const SVal &V) { + return V.getBaseKind() == UnknownKind; } }; class DefinedSVal : public DefinedOrUnknownSVal { private: - // Do not implement. We want calling these methods to be a compiler - // error since they are tautologically true/false. - bool isUnknown() const; - bool isUnknownOrUndef() const; - bool isValid() const; + // We want calling these methods to be a compiler error since they are + // tautologically true/false. + bool isUnknown() const LLVM_DELETED_FUNCTION; + bool isUnknownOrUndef() const LLVM_DELETED_FUNCTION; + bool isValid() const LLVM_DELETED_FUNCTION; protected: + DefinedSVal() {} explicit DefinedSVal(const void *d, bool isLoc, unsigned ValKind) : DefinedOrUnknownSVal(d, isLoc, ValKind) {} -public: - // Implement isa<T> support. - static inline bool classof(const SVal *V) { - return !V->isUnknownOrUndef(); +private: + friend class SVal; + static bool isKind(const SVal& V) { + return !V.isUnknownOrUndef(); + } +}; + + +/// \brief Represents an SVal that is guaranteed to not be UnknownVal. +class KnownSVal : public SVal { + KnownSVal() {} + friend class SVal; + static bool isKind(const SVal &V) { + return !V.isUnknown(); } +public: + KnownSVal(const DefinedSVal &V) : SVal(V) {} + KnownSVal(const UndefinedVal &V) : SVal(V) {} }; class NonLoc : public DefinedSVal { protected: + NonLoc() {} explicit NonLoc(unsigned SubKind, const void *d) : DefinedSVal(d, false, SubKind) {} public: void dumpToStream(raw_ostream &Out) const; - // Implement isa<T> support. - static inline bool classof(const SVal* V) { - return V->getBaseKind() == NonLocKind; +private: + friend class SVal; + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind; } }; class Loc : public DefinedSVal { protected: + Loc() {} explicit Loc(unsigned SubKind, const void *D) : DefinedSVal(const_cast<void*>(D), true, SubKind) {} public: void dumpToStream(raw_ostream &Out) const; - Loc(const Loc& X) : DefinedSVal(X.Data, true, X.getSubKind()) {} - - // Implement isa<T> support. - static inline bool classof(const SVal* V) { - return V->getBaseKind() == LocKind; - } - static inline bool isLocType(QualType T) { return T->isAnyPointerType() || T->isBlockPointerType() || T->isReferenceType(); } + +private: + friend class SVal; + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind; + } }; //==------------------------------------------------------------------------==// @@ -266,17 +310,20 @@ public: return (const SymExpr*) Data; } - bool isExpression() { + bool isExpression() const { return !isa<SymbolData>(getSymbol()); } - static inline bool classof(const SVal* V) { - return V->getBaseKind() == NonLocKind && - V->getSubKind() == SymbolValKind; +private: + friend class SVal; + SymbolVal() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == SymbolValKind; } - static inline bool classof(const NonLoc* V) { - return V->getSubKind() == SymbolValKind; + static bool isKind(const NonLoc& V) { + return V.getSubKind() == SymbolValKind; } }; @@ -297,38 +344,40 @@ public: ConcreteInt evalMinus(SValBuilder &svalBuilder) const; - // Implement isa<T> support. - static inline bool classof(const SVal* V) { - return V->getBaseKind() == NonLocKind && - V->getSubKind() == ConcreteIntKind; +private: + friend class SVal; + ConcreteInt() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == ConcreteIntKind; } - static inline bool classof(const NonLoc* V) { - return V->getSubKind() == ConcreteIntKind; + static bool isKind(const NonLoc& V) { + return V.getSubKind() == ConcreteIntKind; } }; class LocAsInteger : public NonLoc { friend class ento::SValBuilder; - explicit LocAsInteger(const std::pair<SVal, uintptr_t>& data) : - NonLoc(LocAsIntegerKind, &data) { - assert (isa<Loc>(data.first)); - } + explicit LocAsInteger(const std::pair<SVal, uintptr_t> &data) + : NonLoc(LocAsIntegerKind, &data) { + assert (data.first.getAs<Loc>()); + } public: Loc getLoc() const { const std::pair<SVal, uintptr_t> *D = static_cast<const std::pair<SVal, uintptr_t> *>(Data); - return cast<Loc>(D->first); + return D->first.castAs<Loc>(); } - const Loc& getPersistentLoc() const { + Loc getPersistentLoc() const { const std::pair<SVal, uintptr_t> *D = static_cast<const std::pair<SVal, uintptr_t> *>(Data); const SVal& V = D->first; - return cast<Loc>(V); + return V.castAs<Loc>(); } unsigned getNumBits() const { @@ -337,14 +386,16 @@ public: return D->second; } - // Implement isa<T> support. - static inline bool classof(const SVal* V) { - return V->getBaseKind() == NonLocKind && - V->getSubKind() == LocAsIntegerKind; +private: + friend class SVal; + LocAsInteger() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == LocAsIntegerKind; } - static inline bool classof(const NonLoc* V) { - return V->getSubKind() == LocAsIntegerKind; + static bool isKind(const NonLoc& V) { + return V.getSubKind() == LocAsIntegerKind; } }; @@ -362,12 +413,15 @@ public: iterator begin() const; iterator end() const; - static bool classof(const SVal* V) { - return V->getBaseKind() == NonLocKind && V->getSubKind() == CompoundValKind; +private: + friend class SVal; + CompoundVal() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && V.getSubKind() == CompoundValKind; } - static bool classof(const NonLoc* V) { - return V->getSubKind() == CompoundValKind; + static bool isKind(const NonLoc& V) { + return V.getSubKind() == CompoundValKind; } }; @@ -381,14 +435,17 @@ public: return static_cast<const LazyCompoundValData*>(Data); } const void *getStore() const; - const TypedRegion *getRegion() const; + const TypedValueRegion *getRegion() const; - static bool classof(const SVal *V) { - return V->getBaseKind() == NonLocKind && - V->getSubKind() == LazyCompoundValKind; +private: + friend class SVal; + LazyCompoundVal() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == LazyCompoundValKind; } - static bool classof(const NonLoc *V) { - return V->getSubKind() == LazyCompoundValKind; + static bool isKind(const NonLoc& V) { + return V.getSubKind() == LazyCompoundValKind; } }; @@ -410,12 +467,15 @@ public: return static_cast<const LabelDecl*>(Data); } - static inline bool classof(const SVal* V) { - return V->getBaseKind() == LocKind && V->getSubKind() == GotoLabelKind; +private: + friend class SVal; + GotoLabel() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind && V.getSubKind() == GotoLabelKind; } - static inline bool classof(const Loc* V) { - return V->getSubKind() == GotoLabelKind; + static bool isKind(const Loc& V) { + return V.getSubKind() == GotoLabelKind; } }; @@ -434,7 +494,7 @@ public: template <typename REGION> const REGION* getRegionAs() const { - return llvm::dyn_cast<REGION>(getRegion()); + return dyn_cast<REGION>(getRegion()); } inline bool operator==(const MemRegionVal& R) const { @@ -445,14 +505,16 @@ public: return getRegion() != R.getRegion(); } - // Implement isa<T> support. - static inline bool classof(const SVal* V) { - return V->getBaseKind() == LocKind && - V->getSubKind() == MemRegionKind; +private: + friend class SVal; + MemRegionVal() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind && + V.getSubKind() == MemRegionKind; } - static inline bool classof(const Loc* V) { - return V->getSubKind() == MemRegionKind; + static bool isKind(const Loc& V) { + return V.getSubKind() == MemRegionKind; } }; @@ -468,19 +530,22 @@ public: SVal evalBinOp(BasicValueFactory& BasicVals, BinaryOperator::Opcode Op, const ConcreteInt& R) const; - // Implement isa<T> support. - static inline bool classof(const SVal* V) { - return V->getBaseKind() == LocKind && - V->getSubKind() == ConcreteIntKind; +private: + friend class SVal; + ConcreteInt() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind && + V.getSubKind() == ConcreteIntKind; } - static inline bool classof(const Loc* V) { - return V->getSubKind() == ConcreteIntKind; + static bool isKind(const Loc& V) { + return V.getSubKind() == ConcreteIntKind; } }; } // end ento::loc namespace -} // end GR namespace + +} // end ento namespace } // end clang namespace @@ -491,6 +556,11 @@ static inline raw_ostream &operator<<(raw_ostream &os, return os; } +template <typename T> struct isPodLike; +template <> struct isPodLike<clang::ento::SVal> { + static const bool value = true; +}; + } // end llvm namespace #endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index 979546b6ed47..bbfd5797fff0 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -14,9 +14,9 @@ #ifndef LLVM_CLANG_GR_STORE_H #define LLVM_CLANG_GR_STORE_H -#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" @@ -35,6 +35,8 @@ class ProgramState; class ProgramStateManager; class ScanReachableSymbols; +typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols; + class StoreManager { protected: SValBuilder &svalBuilder; @@ -134,7 +136,8 @@ public: SVal evalDerivedToBase(SVal Derived, const CXXBasePath &CastPath); /// Evaluates a derived-to-base cast through a single level of derivation. - SVal evalDerivedToBase(SVal Derived, QualType DerivedPtrType); + SVal evalDerivedToBase(SVal Derived, QualType DerivedPtrType, + bool IsVirtual); /// \brief Evaluates C++ dynamic_cast cast. /// The callback may result in the following 3 scenarios: @@ -168,7 +171,6 @@ public: /// associated with the object is recycled. virtual void decrementReferenceCount(Store store) {} - typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols; typedef SmallVector<const MemRegion *, 8> InvalidatedRegions; /// invalidateRegions - Clears out the specified regions from the store, @@ -176,26 +178,40 @@ public: /// invalidate additional regions that may have changed based on accessing /// the given regions. Optionally, invalidates non-static globals as well. /// \param[in] store The initial store - /// \param[in] Regions The regions to invalidate. + /// \param[in] Values The values to invalidate. + /// \param[in] ConstValues The values to invalidate; these are known to be + /// const, so only regions accesible from them should be invalidated. /// \param[in] E The current statement being evaluated. Used to conjure /// symbols to mark the values of invalidated regions. /// \param[in] Count The current block count. Used to conjure /// symbols to mark the values of invalidated regions. - /// \param[in,out] IS A set to fill with any symbols that are no longer - /// accessible. Pass \c NULL if this information will not be used. /// \param[in] Call The call expression which will be used to determine which /// globals should get invalidated. + /// \param[in,out] IS A set to fill with any symbols that are no longer + /// accessible. Pass \c NULL if this information will not be used. + /// \param[in,out] ConstIS A set to fill with any symbols corresponding to + /// the ConstValues. + /// \param[in,out] InvalidatedTopLevel A vector to fill with regions + //// explicitely being invalidated. Pass \c NULL if this + /// information will not be used. + /// \param[in,out] InvalidatedTopLevelConst A vector to fill with const + //// regions explicitely being invalidated. Pass \c NULL if this + /// information will not be used. /// \param[in,out] Invalidated A vector to fill with any regions being /// invalidated. This should include any regions explicitly invalidated /// even if they do not currently have bindings. Pass \c NULL if this /// information will not be used. virtual StoreRef invalidateRegions(Store store, - ArrayRef<const MemRegion *> Regions, - const Expr *E, unsigned Count, - const LocationContext *LCtx, - InvalidatedSymbols &IS, - const CallEvent *Call, - InvalidatedRegions *Invalidated) = 0; + ArrayRef<SVal> Values, + ArrayRef<SVal> ConstValues, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + const CallEvent *Call, + InvalidatedSymbols &IS, + InvalidatedSymbols &ConstIS, + InvalidatedRegions *InvalidatedTopLevel, + InvalidatedRegions *InvalidatedTopLevelConst, + InvalidatedRegions *Invalidated) = 0; /// enterStackFrame - Let the StoreManager to do something when execution /// engine is about to execute into a callee. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index 1e710778d9be..d4100634a785 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -72,6 +72,15 @@ public: const CFGBlock *DstT, const CFGBlock *DstF) = 0; + /// Called by CoreEngine. Used to processing branching behavior + /// at static initalizers. + virtual void processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) = 0; + /// Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0; @@ -104,7 +113,7 @@ public: /// made to the store. Used to update checkers that track region values. virtual ProgramStateRef processRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) = 0; @@ -116,6 +125,17 @@ public: return processRegionChanges(state, 0, MR, MR, 0); } + virtual ProgramStateRef + processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val) = 0; + + virtual ProgramStateRef + notifyCheckersOfPointerEscape(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call, + bool IsConst = false) = 0; + /// printState - Called by ProgramStateManager to print checker-specific data. virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) = 0; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 873f773b459d..56afca24f6b4 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -20,10 +20,10 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" -#include "llvm/Support/DataTypes.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/DataTypes.h" namespace llvm { class BumpPtrAllocator; @@ -96,7 +96,7 @@ public: }; typedef const SymExpr* SymbolRef; -typedef llvm::SmallVector<SymbolRef, 2> SymbolRefSmallVectorTy; +typedef SmallVector<SymbolRef, 2> SymbolRefSmallVectorTy; typedef unsigned SymbolID; /// \brief A symbol representing data which can be stored in a memory location diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h index c274cea8413e..4c58d4b1d261 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h @@ -14,7 +14,11 @@ #ifndef LLVM_CLANG_TAINTMANAGER_H #define LLVM_CLANG_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/TaintTag.h" +#include "llvm/ADT/ImmutableMap.h" namespace clang { namespace ento { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h b/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h index 51aa753f11e9..d12a1514898f 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h @@ -16,7 +16,8 @@ #define LLVM_CLANG_GR_WORKLIST #include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" -#include <cstddef> +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include <cassert> namespace clang { @@ -24,9 +25,6 @@ class CFGBlock; namespace ento { -class ExplodedNode; -class ExplodedNodeImpl; - class WorkListUnit { ExplodedNode *node; BlockCounter counter; |