diff options
author | dim <dim@FreeBSD.org> | 2012-12-02 13:20:44 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2012-12-02 13:20:44 +0000 |
commit | 056abd2059c65a3e908193aeae16fad98017437c (patch) | |
tree | 2732d02d7d51218d6eed98ac7fcfc5b8794896b5 /include/clang/StaticAnalyzer | |
parent | cc73504950eb7b5dff2dded9bedd67bc36d64641 (diff) | |
download | FreeBSD-src-056abd2059c65a3e908193aeae16fad98017437c.zip FreeBSD-src-056abd2059c65a3e908193aeae16fad98017437c.tar.gz |
Vendor import of clang release_32 branch r168974 (effectively, 3.2 RC2):
http://llvm.org/svn/llvm-project/cfe/branches/release_32@168974
Diffstat (limited to 'include/clang/StaticAnalyzer')
28 files changed, 1293 insertions, 538 deletions
diff --git a/include/clang/StaticAnalyzer/Checkers/DereferenceChecker.h b/include/clang/StaticAnalyzer/Checkers/DereferenceChecker.h deleted file mode 100644 index f9cce9c..0000000 --- a/include/clang/StaticAnalyzer/Checkers/DereferenceChecker.h +++ /dev/null @@ -1,35 +0,0 @@ -//== NullDerefChecker.h - Null dereference checker --------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines NullDerefChecker and UndefDerefChecker, two builtin checks -// in ExprEngine that check for null and undefined pointers at loads -// and stores. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_GR_DEREFCHECKER -#define LLVM_CLANG_GR_DEREFCHECKER - -#include <utility> - -namespace clang { - -namespace ento { - -class ExprEngine; -class ExplodedNode; - -std::pair<ExplodedNode * const *, ExplodedNode * const *> -GetImplicitNullDereferences(ExprEngine &Eng); - -} // end GR namespace - -} // end clang namespace - -#endif diff --git a/include/clang/StaticAnalyzer/Core/Analyses.def b/include/clang/StaticAnalyzer/Core/Analyses.def new file mode 100644 index 0000000..01a6ffd --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/Analyses.def @@ -0,0 +1,67 @@ +//===-- Analyses.def - Metadata about Static Analyses -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the set of static analyses used by AnalysisConsumer. +// +//===----------------------------------------------------------------------===// + +#ifndef ANALYSIS_STORE +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) +#endif + +ANALYSIS_STORE(RegionStore, "region", "Use region-based analyzer store", CreateRegionStoreManager) + +#ifndef ANALYSIS_CONSTRAINTS +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) +#endif + +ANALYSIS_CONSTRAINTS(RangeConstraints, "range", "Use constraint tracking of concrete value ranges", CreateRangeConstraintManager) + +#ifndef ANALYSIS_DIAGNOSTICS +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) +#endif + +ANALYSIS_DIAGNOSTICS(HTML, "html", "Output analysis results using HTML", createHTMLDiagnosticConsumer, false) +ANALYSIS_DIAGNOSTICS(PLIST, "plist", "Output analysis results using Plists", createPlistDiagnosticConsumer, true) +ANALYSIS_DIAGNOSTICS(PLIST_MULTI_FILE, "plist-multi-file", "Output analysis results using Plists (allowing for mult-file bugs)", createPlistMultiFileDiagnosticConsumer, true) +ANALYSIS_DIAGNOSTICS(PLIST_HTML, "plist-html", "Output analysis results using HTML wrapped with Plists", createPlistHTMLDiagnosticConsumer, true) +ANALYSIS_DIAGNOSTICS(TEXT, "text", "Text output of analysis results", createTextPathDiagnosticConsumer, true) + +#ifndef ANALYSIS_PURGE +#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) +#endif + +ANALYSIS_PURGE(PurgeStmt, "statement", "Purge symbols, bindings, and constraints before every statement") +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") + +#undef ANALYSIS_STORE +#undef ANALYSIS_CONSTRAINTS +#undef ANALYSIS_DIAGNOSTICS +#undef ANALYSIS_PURGE +#undef ANALYSIS_INLINING_MODE +#undef ANALYSIS_IPA + diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h new file mode 100644 index 0000000..fa0754a --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -0,0 +1,308 @@ +//===--- AnalyzerOptions.h - Analysis Engine Options ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This header defines various options for the static analyzer that are set +// by the frontend and are consulted throughout the analyzer. +// +//===----------------------------------------------------------------------===// + +#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/StringMap.h" + +namespace clang { +class ASTConsumer; +class DiagnosticsEngine; +class Preprocessor; +class LangOptions; + +/// Analysis - Set of available source code analyses. +enum Analyses { +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE) NAME, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumAnalyses +}; + +/// AnalysisStores - Set of available analysis store models. +enum AnalysisStores { +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) NAME##Model, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumStores +}; + +/// AnalysisConstraints - Set of available constraint models. +enum AnalysisConstraints { +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) NAME##Model, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumConstraints +}; + +/// AnalysisDiagClients - Set of available diagnostic clients for rendering +/// analysis results. +enum AnalysisDiagClients { +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN, AUTOCREAT) PD_##NAME, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NUM_ANALYSIS_DIAG_CLIENTS +}; + +/// AnalysisPurgeModes - Set of available strategies for dead symbol removal. +enum AnalysisPurgeMode { +#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) NAME, +#include "clang/StaticAnalyzer/Core/Analyses.def" +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, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumInliningModes +}; + +/// \brief Describes the different kinds of C++ member functions which can be +/// considered for inlining by the analyzer. +/// +/// These options are cumulative; enabling one kind of member function will +/// enable all kinds with lower enum values. +enum CXXInlineableMemberKind { + // Uninitialized = 0, + + /// A dummy mode in which no C++ inlining is enabled. + CIMK_None = 1, + + /// Refers to regular member function and operator calls. + CIMK_MemberFunctions, + + /// Refers to constructors (implicit or explicit). + /// + /// Note that a constructor will not be inlined if the corresponding + /// destructor is non-trivial. + CIMK_Constructors, + + /// Refers to destructors (implicit or explicit). + CIMK_Destructors +}; + + +class AnalyzerOptions : public llvm::RefCountedBase<AnalyzerOptions> { +public: + typedef llvm::StringMap<std::string> ConfigTable; + + /// \brief Pair of checker name and enable/disable. + std::vector<std::pair<std::string, bool> > CheckersControlList; + + /// \brief A key-value table of use-specified configuration values. + ConfigTable Config; + AnalysisStores AnalysisStoreOpt; + AnalysisConstraints AnalysisConstraintsOpt; + 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; + + + unsigned ShowCheckerHelp : 1; + unsigned AnalyzeAll : 1; + unsigned AnalyzerDisplayProgress : 1; + unsigned AnalyzeNestedBlocks : 1; + + /// \brief The flag regulates if we should eagerly assume evaluations of + /// conditionals, thus, bifurcating the path. + /// + /// This flag indicates how the engine should handle expressions such as: 'x = + /// (y != 0)'. When this flag is true then the subexpression 'y != 0' will be + /// eagerly assumed to be true or false, thus evaluating it to the integers 0 + /// or 1 respectively. The upside is that this can increase analysis + /// precision until we have a better way to lazily evaluate such logic. The + /// downside is that it eagerly bifurcates paths. + unsigned eagerlyAssumeBinOpBifurcation : 1; + + unsigned TrimGraph : 1; + unsigned visualizeExplodedGraphWithGraphViz : 1; + unsigned visualizeExplodedGraphWithUbiGraph : 1; + unsigned UnoptimizedCFG : 1; + unsigned PrintStats : 1; + + /// \brief Do not re-analyze paths leading to exhausted nodes with a different + /// strategy. We get better code coverage when retry is enabled. + unsigned NoRetryExhausted : 1; + + /// \brief The inlining stack depth limit. + unsigned InlineMaxStackDepth; + + /// \brief The mode of function selection used during inlining. + unsigned InlineMaxFunctionSize; + + /// \brief The mode of function selection used during inlining. + AnalysisInliningMode InliningMode; + +private: + /// Controls which C++ member functions will be considered for inlining. + CXXInlineableMemberKind CXXMemberInliningMode; + + /// \sa includeTemporaryDtorsInCFG + llvm::Optional<bool> IncludeTemporaryDtorsInCFG; + + /// \sa mayInlineCXXStandardLibrary + llvm::Optional<bool> InlineCXXStandardLibrary; + + /// \sa mayInlineTemplateFunctions + llvm::Optional<bool> InlineTemplateFunctions; + + /// \sa mayInlineObjCMethod + llvm::Optional<bool> ObjCInliningMode; + + // Cache of the "ipa-always-inline-size" setting. + // \sa getAlwaysInlineSize + llvm::Optional<unsigned> AlwaysInlineSize; + + /// \sa shouldPruneNullReturnPaths + llvm::Optional<bool> PruneNullReturnPaths; + + /// \sa shouldAvoidSuppressingNullArgumentPaths + llvm::Optional<bool> AvoidSuppressingNullArgumentPaths; + + /// \sa getGraphTrimInterval + llvm::Optional<unsigned> GraphTrimInterval; + + /// Interprets an option's string value as a boolean. + /// + /// Accepts the strings "true" and "false". + /// If an option value is not provided, returns the given \p DefaultVal. + 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); + + /// Interprets an option's string value as an integer value. + int getOptionAsInteger(llvm::StringRef Name, int DefaultVal); + +public: + /// Returns the option controlling which C++ member functions will be + /// considered for inlining. + /// + /// This is controlled by the 'c++-inlining' config option. + /// + /// \sa CXXMemberInliningMode + bool mayInlineCXXMemberFunction(CXXInlineableMemberKind K); + + /// Returns true if ObjectiveC inlining is enabled, false otherwise. + bool mayInlineObjCMethod(); + + /// Returns whether or not the destructors for C++ temporary objects should + /// be included in the CFG. + /// + /// This is controlled by the 'cfg-temporary-dtors' config option, which + /// accepts the values "true" and "false". + bool includeTemporaryDtorsInCFG(); + + /// Returns whether or not C++ standard library functions may be considered + /// for inlining. + /// + /// This is controlled by the 'c++-stdlib-inlining' config option, which + /// accepts the values "true" and "false". + bool mayInlineCXXStandardLibrary(); + + /// Returns whether or not templated functions may be considered for inlining. + /// + /// This is controlled by the 'c++-template-inlining' config option, which + /// accepts the values "true" and "false". + bool mayInlineTemplateFunctions(); + + /// Returns whether or not paths that go through null returns should be + /// suppressed. + /// + /// This is a heuristic for avoiding bug reports with paths that go through + /// inlined functions that are more defensive than their callers. + /// + /// This is controlled by the 'suppress-null-return-paths' config option, + /// which accepts the values "true" and "false". + bool shouldPruneNullReturnPaths(); + + /// 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 is a counter-heuristic to avoid false negatives. + /// + /// This is controlled by the 'avoid-suppressing-null-argument-paths' config + /// option, which accepts the values "true" and "false". + bool shouldAvoidSuppressingNullArgumentPaths(); + + // 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 true if the analyzer engine should synthesize fake bodies + /// for well-known functions. + bool shouldSynthesizeBodies(); + + /// Returns how often nodes in the ExplodedGraph should be recycled to save + /// memory. + /// + /// This is controlled by the 'graph-trim-interval' config option. To disable + /// node reclamation, set the option to "0". + unsigned getGraphTrimInterval(); + +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; + // Cap the stack depth at 4 calls (5 stack frames, base + 4 calls). + InlineMaxStackDepth = 5; + InlineMaxFunctionSize = 200; + InliningMode = NoRedundancy; + } +}; + +typedef llvm::IntrusiveRefCntPtr<AnalyzerOptions> AnalyzerOptionsRef; + +} + +#endif diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index 48393a3..b5a88ba 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -24,6 +24,7 @@ #include "llvm/ADT/ilist_node.h" #include "llvm/ADT/ImmutableSet.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallSet.h" namespace clang { @@ -95,6 +96,10 @@ protected: /// for multiple PathDiagnosticConsumers. llvm::SmallVector<Regions *, 2> interestingRegions; + /// A set of location contexts that correspoind to call sites which should be + /// considered "interesting". + llvm::SmallSet<const LocationContext *, 2> InterestingLocationContexts; + /// A set of custom visitors which generate "event" diagnostics at /// interesting points in the path. VisitorList Callbacks; @@ -111,6 +116,19 @@ protected: /// when reporting an issue. bool DoNotPrunePath; + /// Used to track unique reasons why a bug report might be invalid. + /// + /// \sa markInvalid + /// \sa removeInvalidation + typedef std::pair<const void *, const void *> InvalidationRecord; + + /// If non-empty, this bug report is likely a false positive and should not be + /// shown to the user. + /// + /// \sa markInvalid + /// \sa removeInvalidation + llvm::SmallSet<InvalidationRecord, 4> Invalidations; + private: // Used internally by BugReporter. Symbols &getInterestingSymbols(); @@ -147,7 +165,8 @@ public: PathDiagnosticLocation LocationToUnique) : BT(bt), DeclWithIssue(0), Description(desc), UniqueingLocation(LocationToUnique), - ErrorNode(errornode), ConfigurationChangeToken(0) {} + ErrorNode(errornode), ConfigurationChangeToken(0), + DoNotPrunePath(false) {} virtual ~BugReport(); @@ -158,8 +177,10 @@ public: const StringRef getDescription() const { return Description; } - const StringRef getShortDescription() const { - return ShortDescription.empty() ? Description : ShortDescription; + const StringRef getShortDescription(bool UseFallback = true) const { + if (ShortDescription.empty() && UseFallback) + return Description; + return ShortDescription; } /// Indicates whether or not any path pruning should take place @@ -172,14 +193,44 @@ public: void markInteresting(SymbolRef sym); void markInteresting(const MemRegion *R); void markInteresting(SVal V); + void markInteresting(const LocationContext *LC); bool isInteresting(SymbolRef sym); bool isInteresting(const MemRegion *R); bool isInteresting(SVal V); + bool isInteresting(const LocationContext *LC); unsigned getConfigurationChangeToken() const { return ConfigurationChangeToken; } + + /// Returns whether or not this report should be considered valid. + /// + /// Invalid reports are those that have been classified as likely false + /// positives after the fact. + bool isValid() const { + return Invalidations.empty(); + } + + /// Marks the current report as invalid, meaning that it is probably a false + /// positive and should not be reported to the user. + /// + /// The \p Tag and \p Data arguments are intended to be opaque identifiers for + /// this particular invalidation, where \p Tag represents the visitor + /// responsible for invalidation, and \p Data represents the reason this + /// visitor decided to invalidate the bug report. + /// + /// \sa removeInvalidation + void markInvalid(const void *Tag, const void *Data) { + Invalidations.insert(std::make_pair(Tag, Data)); + } + + /// Reverses the effects of a previous invalidation. + /// + /// \sa markInvalid + void removeInvalidation(const void *Tag, const void *Data) { + Invalidations.erase(std::make_pair(Tag, Data)); + } /// Return the canonical declaration, be it a method or class, where /// this issue semantically occurred. @@ -342,6 +393,11 @@ private: /// A vector of BugReports for tracking the allocated pointers and cleanup. std::vector<BugReportEquivClass *> EQClassesVector; + /// A map from PathDiagnosticPiece to the LocationContext of the inlined + /// function call it represents. + llvm::DenseMap<const PathDiagnosticCallPiece*, + const LocationContext*> LocationContextMap; + protected: BugReporter(BugReporterData& d, Kind k) : BugTypes(F.getEmptySet()), kind(k), D(d) {} @@ -378,9 +434,14 @@ public: SourceManager& getSourceManager() { return D.getSourceManager(); } - virtual void GeneratePathDiagnostic(PathDiagnostic& pathDiagnostic, + virtual bool generatePathDiagnostic(PathDiagnostic& pathDiagnostic, PathDiagnosticConsumer &PC, - ArrayRef<BugReport *> &bugReports) {} + ArrayRef<BugReport *> &bugReports) { + return true; + } + + bool RemoveUneededCalls(PathPieces &pieces, BugReport *R, + PathDiagnosticCallPiece *CallWithLoc = 0); void Register(BugType *BT); @@ -389,7 +450,7 @@ public: /// The reports are usually generated by the checkers. Further, they are /// folded based on the profile value, which is done to coalesce similar /// reports. - void EmitReport(BugReport *R); + void emitReport(BugReport *R); void EmitBasicReport(const Decl *DeclWithIssue, StringRef BugName, StringRef BugCategory, @@ -409,8 +470,10 @@ public: EmitBasicReport(DeclWithIssue, BugName, Category, BugStr, Loc, &R, 1); } - static bool classof(const BugReporter* R) { return true; } - + void addCallPieceLocationContextPair(const PathDiagnosticCallPiece *C, + const LocationContext *LC) { + LocationContextMap[C] = LC; + } private: llvm::StringMap<BugType *> StrBugTypes; @@ -440,7 +503,15 @@ public: /// engine. ProgramStateManager &getStateManager(); - virtual void GeneratePathDiagnostic(PathDiagnostic &pathDiagnostic, + /// Generates a path corresponding to one of the given bug reports. + /// + /// Which report is used for path generation is not specified. The + /// bug reporter will try to pick the shortest path, but this is not + /// guaranteed. + /// + /// \return True if the report was valid and a path was generated, + /// false if the reports should be considered invalid. + virtual bool generatePathDiagnostic(PathDiagnostic &PD, PathDiagnosticConsumer &PC, ArrayRef<BugReport*> &bugReports); diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h index f53c15f..78e35ca 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h @@ -100,7 +100,6 @@ class FindLastStoreBRVisitor const MemRegion *R; SVal V; bool satisfied; - const ExplodedNode *StoreSite; public: /// \brief Convenience method to create a visitor given only the MemRegion. @@ -114,7 +113,7 @@ public: static void registerStatementVarDecls(BugReport &BR, const Stmt *S); FindLastStoreBRVisitor(SVal v, const MemRegion *r) - : R(r), V(v), satisfied(false), StoreSite(0) { + : R(r), V(v), satisfied(false) { assert (!V.isUnknown() && "Cannot track unknown value."); // TODO: Does it make sense to allow undef values here? @@ -142,6 +141,10 @@ public: 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 *N, const ExplodedNode *PrevN, BugReporterContext &BRC, @@ -171,6 +174,9 @@ public: ID.AddPointer(&x); } + /// Return the tag associated with this visitor. This tag will be used + /// to make all PathDiagnosticPieces created by this visitor. + static const char *getTag(); virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N, const ExplodedNode *Prev, @@ -223,15 +229,57 @@ public: const ExplodedNode *N, llvm::Optional<bool> &prunable); }; - + +/// \brief When a region containing undefined value or '0' value is passed +/// as an argument in a call, marks the call as interesting. +/// +/// As a result, BugReporter will not prune the path through the function even +/// if the region's contents are not modified/accessed by the call. +class UndefOrNullArgVisitor + : public BugReporterVisitorImpl<UndefOrNullArgVisitor> { + + /// The interesting memory region this visitor is tracking. + const MemRegion *R; + +public: + UndefOrNullArgVisitor(const MemRegion *InR) : R(InR) {} + + virtual void Profile(llvm::FoldingSetNodeID &ID) const { + static int Tag = 0; + ID.AddPointer(&Tag); + ID.AddPointer(R); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR); +}; + namespace bugreporter { -void addTrackNullOrUndefValueVisitor(const ExplodedNode *N, const Stmt *S, - BugReport *R); +/// Attempts to add visitors to trace a null or undefined value back to its +/// point of origin, whether it is a symbol constrained to null or an explicit +/// assignment. +/// +/// \param N A node "downstream" from the evaluation of the statement. +/// \param S The statement whose value is null or undefined. +/// \param R The bug report to which visitors should be attached. +/// \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. +/// +/// \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); const Stmt *GetDerefExpr(const ExplodedNode *N); const Stmt *GetDenomExpr(const ExplodedNode *N); const Stmt *GetRetValExpr(const ExplodedNode *N); +bool isDeclRefExprToReference(const Expr *E); + } // end namespace clang } // end namespace ento diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h index 973cfb1..6dc26e6 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -52,7 +52,31 @@ class PathDiagnostic; class PathDiagnosticConsumer { public: - typedef std::vector<std::pair<StringRef, std::string> > FilesMade; + class PDFileEntry : public llvm::FoldingSetNode { + public: + PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} + + typedef std::vector<std::pair<StringRef, StringRef> > ConsumerFiles; + + /// \brief A vector of <consumer,file> pairs. + ConsumerFiles files; + + /// \brief A precomputed hash tag used for uniquing PDFileEntry objects. + const llvm::FoldingSetNodeID NodeID; + + /// \brief Used for profiling in the FoldingSet. + void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } + }; + + struct FilesMade : public llvm::FoldingSet<PDFileEntry> { + llvm::BumpPtrAllocator Alloc; + + void addDiagnostic(const PathDiagnostic &PD, + StringRef ConsumerName, + StringRef fileName); + + PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); + }; private: virtual void anchor(); @@ -73,7 +97,6 @@ public: virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } virtual bool supportsLogicalOpControlFlow() const { return false; } virtual bool supportsAllBlockEdges() const { return false; } - virtual bool useVerboseDescription() const { return true; } /// Return true if the PathDiagnosticConsumer supports individual /// PathDiagnostics that span multiple files. @@ -114,8 +137,6 @@ private: Kind kind) : K(kind), S(0), D(0), SM(&sm), Loc(genLocation(L)), Range(genRange()) { - assert(Loc.isValid()); - assert(Range.isValid()); } FullSourceLoc @@ -134,12 +155,14 @@ public: PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, LocationOrAnalysisDeclContext lac) - : K(StmtK), S(s), D(0), SM(&sm), + : K(s->getLocStart().isValid() ? StmtK : SingleLocK), + S(K == StmtK ? s : 0), + D(0), SM(&sm), Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { - assert(S); - assert(Loc.isValid()); - assert(Range.isValid()); + assert(K == SingleLocK || S); + assert(K == SingleLocK || Loc.isValid()); + assert(K == SingleLocK || Range.isValid()); } /// Create a location corresponding to the given declaration. @@ -297,12 +320,18 @@ private: const std::string str; const Kind kind; const DisplayHint Hint; + + /// A constant string that can be used to tag the PathDiagnosticPiece, + /// typically with the identification of the creator. The actual pointer + /// value is meant to be an identifier; the string itself is useful for + /// debugging. + StringRef Tag; + std::vector<SourceRange> ranges; - // Do not implement: - PathDiagnosticPiece(); - PathDiagnosticPiece(const PathDiagnosticPiece &P); - PathDiagnosticPiece& operator=(const PathDiagnosticPiece &P); + PathDiagnosticPiece() LLVM_DELETED_FUNCTION; + PathDiagnosticPiece(const PathDiagnosticPiece &P) LLVM_DELETED_FUNCTION; + void operator=(const PathDiagnosticPiece &P) LLVM_DELETED_FUNCTION; protected: PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); @@ -312,8 +341,18 @@ protected: public: virtual ~PathDiagnosticPiece(); - const std::string& getString() const { return str; } + llvm::StringRef getString() const { return str; } + /// Tag this PathDiagnosticPiece with the given C-string. + void setTag(const char *tag) { Tag = tag; } + + /// Return the opaque tag (if any) on the PathDiagnosticPiece. + const void *getTag() const { return Tag.data(); } + + /// Return the string representation of the tag. This is useful + /// for debugging. + StringRef getTagStr() const { return Tag; } + /// getDisplayHint - Return a hint indicating where the diagnostic should /// be displayed by the PathDiagnosticConsumer. DisplayHint getDisplayHint() const { return Hint; } @@ -338,10 +377,6 @@ public: /// Return the SourceRanges associated with this PathDiagnosticPiece. ArrayRef<SourceRange> getRanges() const { return ranges; } - static inline bool classof(const PathDiagnosticPiece *P) { - return true; - } - virtual void Profile(llvm::FoldingSetNodeID &ID) const; }; @@ -377,6 +412,10 @@ public: virtual void flattenLocations() { Pos.flatten(); } virtual void Profile(llvm::FoldingSetNodeID &ID) const; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Event || P->getKind() == Macro; + } }; /// \brief Interface for classes constructing Stack hints. @@ -410,10 +449,6 @@ public: /// 'getMessageForX()' methods to construct a specific message. virtual std::string getMessage(const ExplodedNode *N); - /// Prints the ordinal form of the given integer, - /// only valid for ValNo : ValNo > 0. - void printOrdinal(unsigned ValNo, llvm::raw_svector_ostream &Out); - /// Produces the message of the following form: /// 'Msg via Nth parameter' virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); @@ -629,14 +664,22 @@ public: class PathDiagnostic : public llvm::FoldingSetNode { const Decl *DeclWithIssue; std::string BugType; - std::string Desc; + std::string VerboseDesc; + std::string ShortDesc; std::string Category; std::deque<std::string> OtherDesc; + PathDiagnosticLocation Loc; PathPieces pathImpl; llvm::SmallVector<PathPieces *, 3> pathStack; PathDiagnostic(); // Do not implement. public: + PathDiagnostic(const Decl *DeclWithIssue, StringRef bugtype, + StringRef verboseDesc, StringRef shortDesc, + StringRef category); + + ~PathDiagnostic(); + const PathPieces &path; /// Return the path currently used by builders for constructing the @@ -659,16 +702,24 @@ public: void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } bool isWithinCall() const { return !pathStack.empty(); } - - // PathDiagnostic(); - PathDiagnostic(const Decl *DeclWithIssue, - StringRef bugtype, - StringRef desc, - StringRef category); - ~PathDiagnostic(); + void setEndOfPath(PathDiagnosticPiece *EndPiece) { + assert(!Loc.isValid() && "End location already set!"); + Loc = EndPiece->getLocation(); + assert(Loc.isValid() && "Invalid location for end-of-path piece"); + getActivePath().push_back(EndPiece); + } - StringRef getDescription() const { return Desc; } + void resetPath() { + pathStack.clear(); + pathImpl.clear(); + Loc = PathDiagnosticLocation(); + } + + StringRef getVerboseDescription() const { return VerboseDesc; } + StringRef getShortDescription() const { + return ShortDesc.empty() ? VerboseDesc : ShortDesc; + } StringRef getBugType() const { return BugType; } StringRef getCategory() const { return Category; } @@ -682,15 +733,27 @@ public: meta_iterator meta_end() const { return OtherDesc.end(); } void addMeta(StringRef s) { OtherDesc.push_back(s); } - PathDiagnosticLocation getLocation() const; + PathDiagnosticLocation getLocation() const { + assert(Loc.isValid() && "No end-of-path location set yet!"); + return Loc; + } void flattenLocations() { + Loc.flatten(); for (PathPieces::iterator I = pathImpl.begin(), E = pathImpl.end(); I != E; ++I) (*I)->flattenLocations(); } - + + /// Profiles the diagnostic, independent of the path it references. + /// + /// This can be used to merge diagnostics that refer to the same issue + /// along different paths. void Profile(llvm::FoldingSetNodeID &ID) const; - + + /// Profiles the diagnostic, including its path. + /// + /// Two diagnostics with the same issue along different paths will generate + /// different profiles. void FullProfile(llvm::FoldingSetNodeID &ID) const; }; diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index 3214d96..9eb1248 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -366,23 +366,6 @@ public: } }; -class InlineCall { - template <typename CHECKER> - static bool _inlineCall(void *checker, const CallExpr *CE, - ExprEngine &Eng, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - return ((const CHECKER *)checker)->inlineCall(CE, Eng, Pred, Dst); - } - -public: - template <typename CHECKER> - static void _register(CHECKER *checker, CheckerManager &mgr) { - mgr._registerForInlineCall( - CheckerManager::InlineCallFunc(checker, _inlineCall<CHECKER>)); - } -}; - } // end eval namespace class CheckerBase : public ProgramPointTag { diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index e11b6d5..7ae8e53 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -258,7 +258,7 @@ public: const ExplodedNodeSet &Src, SVal location, SVal val, const Stmt *S, ExprEngine &Eng, - ProgramPoint::Kind PointKind); + const ProgramPoint &PP); /// \brief Run checkers for end of analysis. void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR, @@ -267,6 +267,7 @@ public: /// \brief Run checkers for end of path. void runCheckersForEndPath(NodeBuilderContext &BC, ExplodedNodeSet &Dst, + ExplodedNode *Pred, ExprEngine &Eng); /// \brief Run checkers for branch condition. @@ -407,11 +408,6 @@ public: typedef CheckerFn<bool (const CallExpr *, CheckerContext &)> EvalCallFunc; - typedef CheckerFn<bool (const CallExpr *, ExprEngine &Eng, - ExplodedNode *Pred, - ExplodedNodeSet &Dst)> - InlineCallFunc; - typedef CheckerFn<void (const TranslationUnitDecl *, AnalysisManager&, BugReporter &)> CheckEndOfTranslationUnit; @@ -449,8 +445,6 @@ public: void _registerForEvalCall(EvalCallFunc checkfn); - void _registerForInlineCall(InlineCallFunc checkfn); - void _registerForEndOfTranslationUnit(CheckEndOfTranslationUnit checkfn); //===----------------------------------------------------------------------===// @@ -576,8 +570,6 @@ private: std::vector<EvalCallFunc> EvalCallCheckers; - std::vector<InlineCallFunc> InlineCallCheckers; - std::vector<CheckEndOfTranslationUnit> EndOfTranslationUnitCheckers; struct EventInfo { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h index e1ff17b..27f3677 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h @@ -66,6 +66,10 @@ public: return llvm::APSInt::getMaxValue(BitWidth, IsUnsigned); } + llvm::APSInt getValue(uint64_t RawValue) const LLVM_READONLY { + return (llvm::APSInt(BitWidth, IsUnsigned) = RawValue); + } + /// Used to classify whether a value is representable using this type. /// /// \see testInRange diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h index 876196b..9038ae5 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -16,7 +16,7 @@ #define LLVM_CLANG_GR_ANALYSISMANAGER_H #include "clang/Analysis/AnalysisContext.h" -#include "clang/Frontend/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" @@ -41,64 +41,16 @@ class AnalysisManager : public BugReporterData { CheckerManager *CheckerMgr; - /// \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 MaxVisit; - - bool VisualizeEGDot; - bool VisualizeEGUbi; - AnalysisPurgeMode PurgeDead; - - /// \brief The flag regulates if we should eagerly assume evaluations of - /// conditionals, thus, bifurcating the path. - /// - /// EagerlyAssume - A flag indicating how the engine should handle - /// expressions such as: 'x = (y != 0)'. When this flag is true then - /// the subexpression 'y != 0' will be eagerly assumed to be true or false, - /// thus evaluating it to the integers 0 or 1 respectively. The upside - /// is that this can increase analysis precision until we have a better way - /// to lazily evaluate such logic. The downside is that it eagerly - /// bifurcates paths. - bool EagerlyAssume; - bool TrimGraph; - bool EagerlyTrimEGraph; - -public: - // \brief inter-procedural analysis mode. - AnalysisIPAMode IPAMode; - - // Settings for inlining tuning. - /// \brief The inlining stack depth limit. - unsigned InlineMaxStackDepth; - /// \brief The max number of basic blocks in a function being inlined. - unsigned InlineMaxFunctionSize; - /// \brief The mode of function selection used during inlining. - AnalysisInliningMode InliningMode; - - /// \brief Do not re-analyze paths leading to exhausted nodes with a different - /// strategy. We get better code coverage when retry is enabled. - bool NoRetryExhausted; - public: + AnalyzerOptions &options; + AnalysisManager(ASTContext &ctx,DiagnosticsEngine &diags, const LangOptions &lang, const PathDiagnosticConsumers &Consumers, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, CheckerManager *checkerMgr, - unsigned maxnodes, unsigned maxvisit, - bool vizdot, bool vizubi, AnalysisPurgeMode purge, - bool eager, bool trim, - bool useUnoptimizedCFG, - bool addImplicitDtors, - bool eagerlyTrimEGraph, - AnalysisIPAMode ipa, - unsigned inlineMaxStack, - unsigned inlineMaxFunctionSize, - AnalysisInliningMode inliningMode, - bool NoRetry); + AnalyzerOptions &Options); ~AnalysisManager(); @@ -142,27 +94,14 @@ public: void FlushDiagnostics(); - unsigned getMaxNodes() const { return MaxNodes; } - - unsigned getMaxVisit() const { return MaxVisit; } - - bool shouldVisualizeGraphviz() const { return VisualizeEGDot; } - - bool shouldVisualizeUbigraph() const { return VisualizeEGUbi; } - bool shouldVisualize() const { - return VisualizeEGDot || VisualizeEGUbi; + return options.visualizeExplodedGraphWithGraphViz || + options.visualizeExplodedGraphWithUbiGraph; } - bool shouldEagerlyTrimExplodedGraph() const { return EagerlyTrimEGraph; } - - bool shouldTrimGraph() const { return TrimGraph; } - - AnalysisPurgeMode getPurgeMode() const { return PurgeDead; } - - bool shouldEagerlyAssume() const { return EagerlyAssume; } - - bool shouldInlineCall() const { return (IPAMode != None); } + bool shouldInlineCall() const { + return options.IPAMode != None; + } CFG *getCFG(Decl const *D) { return AnaCtxMgr.getContext(D)->getCFG(); @@ -180,7 +119,6 @@ public: AnalysisDeclContext *getAnalysisDeclContext(const Decl *D) { return AnaCtxMgr.getContext(D); } - }; } // enAnaCtxMgrspace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index b4a9de7..fb39354 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -73,6 +73,10 @@ class BasicValueFactory { llvm::FoldingSet<CompoundValData> CompoundValDataSet; llvm::FoldingSet<LazyCompoundValData> LazyCompoundValDataSet; + // This is private because external clients should use the factory + // method that takes a QualType. + const llvm::APSInt& getValue(uint64_t X, unsigned BitWidth, bool isUnsigned); + public: BasicValueFactory(ASTContext &ctx, llvm::BumpPtrAllocator& Alloc) : Ctx(ctx), BPAlloc(Alloc), PersistentSVals(0), PersistentSValPairs(0), @@ -84,7 +88,6 @@ public: const llvm::APSInt& getValue(const llvm::APSInt& X); const llvm::APSInt& getValue(const llvm::APInt& X, bool isUnsigned); - const llvm::APSInt& getValue(uint64_t X, unsigned BitWidth, bool isUnsigned); const llvm::APSInt& getValue(uint64_t X, QualType T); /// Returns the type of the APSInt used to store values of the given QualType. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index f6c5830..a6a91e2 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -20,6 +20,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/Analysis/AnalysisContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/PointerIntPair.h" @@ -68,15 +69,22 @@ public: } }; +/// \class RuntimeDefinition /// \brief Defines the runtime definition of the called function. +/// +/// Encapsulates the information we have about which Decl will be used +/// when the call is executed on the given path. When dealing with dynamic +/// dispatch, the information is based on DynamicTypeInfo and might not be +/// precise. class RuntimeDefinition { - /// The Declaration of the function which will be called at runtime. - /// 0 if not available. + /// The Declaration of the function which could be called at runtime. + /// NULL if not available. const Decl *D; /// The region representing an object (ObjC/C++) on which the method is /// called. With dynamic dispatch, the method definition depends on the - /// runtime type of this object. 0 when there is no dynamic dispatch. + /// runtime type of this object. NULL when the DynamicTypeInfo is + /// precise. const MemRegion *R; public: @@ -84,8 +92,15 @@ public: RuntimeDefinition(const Decl *InD): D(InD), R(0) {} RuntimeDefinition(const Decl *InD, const MemRegion *InR): D(InD), R(InR) {} const Decl *getDecl() { return D; } - const MemRegion *getDispatchRegion() { return R; } + + /// \brief Check if the definition we have is precise. + /// If not, it is possible that the call dispatches to another definition at + /// execution time. bool mayHaveOtherDefinitions() { return R != 0; } + + /// When other definitions are possible, returns the region whose runtime type + /// determines the method definition. + const MemRegion *getDispatchRegion() { return R; } }; /// \brief Represents an abstract call to a function or method along a @@ -106,8 +121,7 @@ private: const LocationContext *LCtx; llvm::PointerUnion<const Expr *, const Decl *> Origin; - // DO NOT IMPLEMENT - CallEvent &operator=(const CallEvent &); + void operator=(const CallEvent &) LLVM_DELETED_FUNCTION; protected: // This is user data for subclasses. @@ -139,16 +153,6 @@ protected: : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin), Data(Original.Data), Location(Original.Location), RefCount(0) {} - - ProgramStateRef getState() const { - return State; - } - - const LocationContext *getLocationContext() const { - return LCtx; - } - - /// Copies this CallEvent, with vtable intact, into a new block of memory. virtual void cloneTo(void *Dest) const = 0; @@ -164,8 +168,6 @@ protected: /// result of this call. virtual void getExtraInvalidatedRegions(RegionList &Regions) const {} - virtual QualType getDeclaredResultType() const = 0; - public: virtual ~CallEvent() {} @@ -178,6 +180,16 @@ public: return Origin.dyn_cast<const Decl *>(); } + /// \brief The state in which the call is being evaluated. + ProgramStateRef getState() const { + return State; + } + + /// \brief The context in which the call is being evaluated. + const LocationContext *getLocationContext() const { + return LCtx; + } + /// \brief Returns the definition of the function or method that will be /// called. virtual RuntimeDefinition getRuntimeDefinition() const = 0; @@ -237,6 +249,12 @@ public: /// \brief Returns the result type, adjusted for references. QualType getResultType() const; + /// \brief Returns the return value of the call. + /// + /// This should only be called if the CallEvent was created using a state in + /// which the return value has already been bound to the origin expression. + SVal getReturnValue() const; + /// \brief Returns true if any of the arguments appear to represent callbacks. bool hasNonZeroCallbackArg() const; @@ -249,6 +267,38 @@ public: return hasNonZeroCallbackArg(); } + /// \brief Returns true if the callee is an externally-visible function in the + /// top-level namespace, such as \c malloc. + /// + /// You can use this call to determine that a particular function really is + /// a library function and not, say, a C++ member function with the same name. + /// + /// If a name is provided, the function must additionally match the given + /// name. + /// + /// Note that this deliberately excludes C++ library functions in the \c std + /// namespace, but will include C library functions accessed through the + /// \c std namespace. This also does not check if the function is declared + /// as 'extern "C"', or if it uses C++ name mangling. + // FIXME: Add a helper for checking namespaces. + // FIXME: Move this down to AnyFunctionCall once checkers have more + // precise callbacks. + bool isGlobalCFunction(StringRef SpecificName = StringRef()) const; + + /// \brief Returns the name of the callee, if its name is a simple identifier. + /// + /// Note that this will fail for Objective-C methods, blocks, and C++ + /// overloaded operators. The former is named by a Selector rather than a + /// simple identifier, and the latter two do not have names. + // FIXME: Move this down to AnyFunctionCall once checkers have more + // precise callbacks. + const IdentifierInfo *getCalleeIdentifier() const { + const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(getDecl()); + if (!ND) + return 0; + return ND->getIdentifier(); + } + /// \brief Returns an appropriate ProgramPoint for this call. ProgramPoint getProgramPoint(bool IsPreVisit = false, const ProgramPointTag *Tag = 0) const; @@ -277,12 +327,12 @@ public: return cloneWithState<CallEvent>(NewState); } - /// \brief Returns true if this is a statement that can be considered for - /// inlining. - /// - /// FIXME: This should go away once CallEvents are cheap and easy to - /// construct from ExplodedNodes. - static bool mayBeInlined(const Stmt *S); + /// \brief Returns true if this is a statement is a function or method call + /// of some kind. + static bool isCallStmt(const Stmt *S); + + /// \brief Returns the result type of a function, method declaration. + static QualType getDeclaredResultType(const Decl *D); // Iterator access to formal parameters and their types. private: @@ -329,8 +379,6 @@ public: // For debugging purposes only void dump(raw_ostream &Out) const; LLVM_ATTRIBUTE_USED void dump() const; - - static bool classof(const CallEvent *) { return true; } }; @@ -346,8 +394,6 @@ protected: : CallEvent(D, St, LCtx) {} AnyFunctionCall(const AnyFunctionCall &Other) : CallEvent(Other) {} - virtual QualType getDeclaredResultType() const; - public: // This function is overridden by subclasses, but they must return // a FunctionDecl. @@ -357,9 +403,16 @@ public: virtual RuntimeDefinition getRuntimeDefinition() const { const FunctionDecl *FD = getDecl(); - // Note that hasBody() will fill FD with the definition FunctionDecl. - if (FD && FD->hasBody(FD)) - return RuntimeDefinition(FD); + // Note that the AnalysisDeclContext will have the FunctionDecl with + // the definition (if one exists). + if (FD) { + AnalysisDeclContext *AD = + getLocationContext()->getAnalysisDeclContext()-> + getManager()->getContext(FD); + if (AD->getBody()) + return RuntimeDefinition(AD->getDecl()); + } + return RuntimeDefinition(); } @@ -442,8 +495,6 @@ protected: virtual void getExtraInvalidatedRegions(RegionList &Regions) const; - virtual QualType getDeclaredResultType() const; - public: /// \brief Returns the region associated with this instance of the block. /// @@ -499,13 +550,7 @@ public: virtual const Expr *getCXXThisExpr() const { return 0; } /// \brief Returns the value of the implicit 'this' object. - virtual SVal getCXXThisVal() const { - const Expr *Base = getCXXThisExpr(); - // FIXME: This doesn't handle an overloaded ->* operator. - if (!Base) - return UnknownVal(); - return getSVal(Base); - } + virtual SVal getCXXThisVal() const; virtual const FunctionDecl *getDecl() const; @@ -550,6 +595,8 @@ public: } virtual const Expr *getCXXThisExpr() const; + + virtual RuntimeDefinition getRuntimeDefinition() const; virtual Kind getKind() const { return CE_CXXMember; } @@ -605,6 +652,8 @@ class CXXDestructorCall : public CXXInstanceCall { friend class CallEventManager; protected: + typedef llvm::PointerIntPair<const MemRegion *, 1, bool> DtorDataTy; + /// Creates an implicit destructor. /// /// \param DD The destructor that will be called. @@ -613,10 +662,10 @@ protected: /// \param St The path-sensitive state at this point in the program. /// \param LCtx The location context at this point in the program. CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, - const MemRegion *Target, ProgramStateRef St, - const LocationContext *LCtx) + const MemRegion *Target, bool IsBaseDestructor, + ProgramStateRef St, const LocationContext *LCtx) : CXXInstanceCall(DD, St, LCtx) { - Data = Target; + Data = DtorDataTy(Target, IsBaseDestructor).getOpaqueValue(); Location = Trigger->getLocEnd(); } @@ -627,9 +676,16 @@ public: virtual SourceRange getSourceRange() const { return Location; } virtual unsigned getNumArgs() const { return 0; } + virtual RuntimeDefinition getRuntimeDefinition() const; + /// \brief Returns the value of the implicit 'this' object. virtual SVal getCXXThisVal() const; + /// Returns true if this is a call to a base class destructor. + bool isBaseDestructor() const { + return DtorDataTy::getFromOpaqueValue(Data).getInt(); + } + virtual Kind getKind() const { return CE_CXXDestructor; } static bool classof(const CallEvent *CA) { @@ -651,10 +707,10 @@ protected: /// a new symbolic region will be used. /// \param St The path-sensitive state at this point in the program. /// \param LCtx The location context at this point in the program. - CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *target, + CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *Target, ProgramStateRef St, const LocationContext *LCtx) : AnyFunctionCall(CE, St, LCtx) { - Data = target; + Data = Target; } CXXConstructorCall(const CXXConstructorCall &Other) : AnyFunctionCall(Other){} @@ -761,8 +817,6 @@ protected: virtual void getExtraInvalidatedRegions(RegionList &Regions) const; - virtual QualType getDeclaredResultType() const; - /// Check if the selector may have multiple definitions (may have overrides). virtual bool canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, Selector Sel) const; @@ -796,6 +850,9 @@ public: /// \brief Returns the value of the receiver at the time of this call. SVal getReceiverSVal() const; + /// \brief Return the value of 'self' if available. + SVal getSelfSVal() const; + /// \brief Get the interface for the receiver. /// /// This works whether this is an instance message or a class message. @@ -804,6 +861,9 @@ public: return getOriginExpr()->getReceiverInterface(); } + /// \brief Checks if the receiver refers to 'self' or 'super'. + bool isReceiverSelfOrSuper() const; + /// Returns how the message was written in the source (property access, /// subscript, or explicit message send). ObjCMessageKind getMessageKind() const; @@ -879,6 +939,13 @@ class CallEventManager { return new (allocate()) T(A1, A2, A3, St, LCtx); } + template <typename T, typename Arg1, typename Arg2, typename Arg3, + typename Arg4> + T *create(Arg1 A1, Arg2 A2, Arg3 A3, Arg4 A4, ProgramStateRef St, + const LocationContext *LCtx) { + return new (allocate()) T(A1, A2, A3, A4, St, LCtx); + } + public: CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {} @@ -905,9 +972,9 @@ public: CallEventRef<CXXDestructorCall> getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, - const MemRegion *Target, ProgramStateRef State, - const LocationContext *LCtx) { - return create<CXXDestructorCall>(DD, Trigger, Target, State, LCtx); + const MemRegion *Target, bool IsBase, + ProgramStateRef State, const LocationContext *LCtx) { + return create<CXXDestructorCall>(DD, Trigger, Target, IsBase, State, LCtx); } CallEventRef<CXXAllocatorCall> diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 8c8e82c..4558cd9 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -16,10 +16,57 @@ #define LLVM_CLANG_SA_CORE_PATHSENSITIVE_CHECKERCONTEXT #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" namespace clang { namespace ento { + /// Declares an immutable map of type \p NameTy, suitable for placement into + /// the ProgramState. This is implementing using llvm::ImmutableMap. + /// + /// \code + /// State = State->set<Name>(K, V); + /// const Value *V = State->get<Name>(K); // Returns NULL if not in the map. + /// State = State->remove<Name>(K); + /// NameTy Map = State->get<Name>(); + /// \endcode + /// + /// The macro should not be used inside namespaces, or for traits that must + /// be accessible from more than one translation unit. + #define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value) \ + REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, \ + CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value)) + + /// Declares an immutable set of type \p NameTy, suitable for placement into + /// the ProgramState. This is implementing using llvm::ImmutableSet. + /// + /// \code + /// State = State->add<Name>(E); + /// State = State->remove<Name>(E); + /// bool Present = State->contains<Name>(E); + /// NameTy Set = State->get<Name>(); + /// \endcode + /// + /// The macro should not be used inside namespaces, or for traits that must + /// be accessible from more than one translation unit. + #define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem) \ + REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableSet<Elem>) + + /// Declares an immutable list of type \p NameTy, suitable for placement into + /// the ProgramState. This is implementing using llvm::ImmutableList. + /// + /// \code + /// State = State->add<Name>(E); // Adds to the /end/ of the list. + /// bool Present = State->contains<Name>(E); + /// NameTy List = State->get<Name>(); + /// \endcode + /// + /// The macro should not be used inside namespaces, or for traits that must + /// be accessible from more than one translation unit. + #define REGISTER_LIST_WITH_PROGRAMSTATE(Name, Elem) \ + REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableList<Elem>) + + class CheckerContext { ExprEngine &Eng; /// The current exploded(symbolic execution) graph node. @@ -64,6 +111,10 @@ public: return Eng.getStoreManager(); } + const AnalyzerOptions::ConfigTable &getConfig() const { + return Eng.getAnalysisManager().options.Config; + } + /// \brief Returns the previous node in the exploded graph, which includes /// the state of the program before the checker ran. Note, checkers should /// not retain the node in their state since the nodes might get invalidated. @@ -76,8 +127,8 @@ public: /// \brief Returns the number of times the current block has been visited /// along the analyzed path. - unsigned getCurrentBlockCount() const { - return NB.getContext().getCurrentBlockCount(); + unsigned blockCount() const { + return NB.getContext().blockCount(); } ASTContext &getASTContext() { @@ -96,6 +147,9 @@ public: return Pred->getStackFrame(); } + /// Return true if the current LocationContext has no caller context. + bool inTopFrame() const { return getLocationContext()->inTopFrame(); } + BugReporter &getBugReporter() { return Eng.getBugReporter(); } @@ -144,20 +198,15 @@ public: /// \brief Generates a new transition in the program state graph /// (ExplodedGraph). Uses the default CheckerContext predecessor node. /// - /// @param State The state of the generated node. + /// @param State The state of the generated node. If not specified, the state + /// will not be changed, but the new node will have the checker's tag. /// @param Tag The tag is used to uniquely identify the creation site. If no /// tag is specified, a default tag, unique to the given checker, /// will be used. Tags are used to prevent states generated at /// different sites from caching out. - ExplodedNode *addTransition(ProgramStateRef State, + ExplodedNode *addTransition(ProgramStateRef State = 0, const ProgramPointTag *Tag = 0) { - return addTransitionImpl(State, false, 0, Tag); - } - - /// \brief Generates a default transition (containing checker tag but no - /// checker state changes). - ExplodedNode *addTransition() { - return addTransition(getState()); + return addTransitionImpl(State ? State : getState(), false, 0, Tag); } /// \brief Generates a new transition with the given predecessor. @@ -167,25 +216,24 @@ public: /// @param Pred The transition will be generated from the specified Pred node /// to the newly generated node. /// @param Tag The tag to uniquely identify the creation site. - /// @param IsSink Mark the new node as sink, which will stop exploration of - /// the given path. ExplodedNode *addTransition(ProgramStateRef State, - ExplodedNode *Pred, - const ProgramPointTag *Tag = 0, - bool IsSink = false) { - return addTransitionImpl(State, IsSink, Pred, Tag); + ExplodedNode *Pred, + const ProgramPointTag *Tag = 0) { + return addTransitionImpl(State, false, Pred, Tag); } - /// \brief Generate a sink node. Generating sink stops exploration of the + /// \brief Generate a sink node. Generating a sink stops exploration of the /// given path. - ExplodedNode *generateSink(ProgramStateRef state = 0) { - return addTransitionImpl(state ? state : getState(), true); + ExplodedNode *generateSink(ProgramStateRef State = 0, + ExplodedNode *Pred = 0, + const ProgramPointTag *Tag = 0) { + return addTransitionImpl(State ? State : getState(), true, Pred, Tag); } /// \brief Emit the diagnostics report. - void EmitReport(BugReport *R) { + void emitReport(BugReport *R) { Changed = true; - Eng.getBugReporter().EmitReport(R); + Eng.getBugReporter().emitReport(R); } /// \brief Get the declaration of the called function (path-sensitive). @@ -194,17 +242,33 @@ public: /// \brief Get the name of the called function (path-sensitive). StringRef getCalleeName(const FunctionDecl *FunDecl) const; + /// \brief Get the identifier of the called function (path-sensitive). + const IdentifierInfo *getCalleeIdentifier(const CallExpr *CE) const { + const FunctionDecl *FunDecl = getCalleeDecl(CE); + if (FunDecl) + return FunDecl->getIdentifier(); + else + return 0; + } + /// \brief Get the name of the called function (path-sensitive). StringRef getCalleeName(const CallExpr *CE) const { const FunctionDecl *FunDecl = getCalleeDecl(CE); return getCalleeName(FunDecl); } - /// Given a function declaration and a name checks if this is a C lib - /// function with the given name. - bool isCLibraryFunction(const FunctionDecl *FD, StringRef Name); - static bool isCLibraryFunction(const FunctionDecl *FD, StringRef Name, - ASTContext &Context); + /// \brief Returns true if the callee is an externally-visible function in the + /// top-level namespace, such as \c malloc. + /// + /// If a name is provided, the function must additionally match the given + /// name. + /// + /// Note that this deliberately excludes C++ library functions in the \c std + /// namespace, but will include C library functions accessed through the + /// \c std namespace. This also does not check if the function is declared + /// as 'extern "C"', or if it uses C++ name mangling. + static bool isCLibraryFunction(const FunctionDecl *FD, + StringRef Name = StringRef()); /// \brief Depending on wither the location corresponds to a macro, return /// either the macro name or the token spelling. @@ -226,9 +290,15 @@ private: return Pred; Changed = true; - ExplodedNode *node = NB.generateNode(Tag ? Location.withTag(Tag) : Location, - State, - P ? P : Pred, MarkAsSink); + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + if (!P) + P = Pred; + + ExplodedNode *node; + if (MarkAsSink) + node = NB.generateSink(LocalLoc, State, P); + else + node = NB.generateNode(LocalLoc, State, P); return node; } }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h index 631858d..4a78849 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -16,6 +16,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/Support/SaveAndRestore.h" namespace llvm { class APSInt; @@ -26,29 +27,83 @@ namespace ento { class SubEngine; +class ConditionTruthVal { + llvm::Optional<bool> Val; +public: + /// Construct a ConditionTruthVal indicating the constraint is constrained + /// to either true or false, depending on the boolean value provided. + ConditionTruthVal(bool constraint) : Val(constraint) {} + + /// Construct a ConstraintVal indicating the constraint is underconstrained. + ConditionTruthVal() {} + + /// Return true if the constraint is perfectly constrained to 'true'. + bool isConstrainedTrue() const { + return Val.hasValue() && Val.getValue(); + } + + /// Return true if the constraint is perfectly constrained to 'false'. + bool isConstrainedFalse() const { + return Val.hasValue() && !Val.getValue(); + } + + /// Return true if the constrained is perfectly constrained. + bool isConstrained() const { + return Val.hasValue(); + } + + /// Return true if the constrained is underconstrained and we do not know + /// if the constraint is true of value. + bool isUnderconstrained() const { + return !Val.hasValue(); + } +}; + class ConstraintManager { public: + ConstraintManager() : NotifyAssumeClients(true) {} + virtual ~ConstraintManager(); virtual ProgramStateRef assume(ProgramStateRef state, - DefinedSVal Cond, - bool Assumption) = 0; - - std::pair<ProgramStateRef, ProgramStateRef > - assumeDual(ProgramStateRef state, DefinedSVal Cond) - { - std::pair<ProgramStateRef, ProgramStateRef > res = - std::make_pair(assume(state, Cond, true), assume(state, Cond, false)); - - assert(!(!res.first && !res.second) && "System is over constrained."); - return res; + DefinedSVal Cond, + bool Assumption) = 0; + + typedef std::pair<ProgramStateRef, ProgramStateRef> ProgramStatePair; + + /// Returns a pair of states (StTrue, StFalse) where the given condition is + /// assumed to be true or false, respectively. + ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond) { + ProgramStateRef StTrue = assume(State, Cond, true); + + // 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. + assert(assume(State, Cond, false) && "System is over constrained."); + return ProgramStatePair((ProgramStateRef)NULL, State); + } + + ProgramStateRef StFalse = assume(State, Cond, false); + if (!StFalse) { + // We are careful to return the original state, /not/ StTrue, + // because we want to avoid having callers generate a new node + // in the ExplodedGraph. + return ProgramStatePair(State, (ProgramStateRef)NULL); + } + + return ProgramStatePair(StTrue, StFalse); } + /// \brief If a symbol is perfectly constrained to a constant, attempt + /// to return the concrete value. + /// + /// Note that a ConstraintManager is not obligated to return a concretized + /// value for a symbol, even if it is perfectly constrained. virtual const llvm::APSInt* getSymVal(ProgramStateRef state, - SymbolRef sym) const = 0; - - virtual bool isEqual(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt& V) const = 0; + SymbolRef sym) const { + return 0; + } virtual ProgramStateRef removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper) = 0; @@ -59,20 +114,38 @@ public: const char *sep) = 0; virtual void EndPath(ProgramStateRef state) {} + + /// 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); + + return checkNull(State, Sym); + } protected: + /// A flag to indicate that clients should be notified of assumptions. + /// By default this is the case, but sometimes this needs to be restricted + /// to avoid infinite recursions within the ConstraintManager. + /// + /// Note that this flag allows the ConstraintManager to be re-entrant, + /// but not thread-safe. + bool NotifyAssumeClients; + /// canReasonAbout - Not all ConstraintManagers can accurately reason about /// all SVal values. This method returns true if the ConstraintManager can /// reasonably handle a given SVal value. This is typically queried by /// ExprEngine to determine if the value should be replaced with a /// conjured symbolic value in order to recover some precision. virtual bool canReasonAbout(SVal X) const = 0; + + /// Returns whether or not a symbol is known to be null ("true"), known to be + /// non-null ("false"), or may be either ("underconstrained"). + virtual ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym); }; -ConstraintManager* CreateBasicConstraintManager(ProgramStateManager& statemgr, - SubEngine &subengine); ConstraintManager* CreateRangeConstraintManager(ProgramStateManager& statemgr, - SubEngine &subengine); + SubEngine *subengine); } // end GR namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index e75cdd8..b668640 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -80,10 +80,6 @@ private: /// usually because it could not reason about something. BlocksAborted blocksAborted; - /// The functions which have been analyzed through inlining. This is owned by - /// AnalysisConsumer. It can be null. - SetOfConstDecls *AnalyzedCallees; - /// The information about functions shared by the whole translation unit. /// (This data is owned by AnalysisConsumer.) FunctionSummariesTy *FunctionSummaries; @@ -101,19 +97,18 @@ private: ExplodedNode *Pred); private: - CoreEngine(const CoreEngine&); // Do not implement. - CoreEngine& operator=(const CoreEngine&); + CoreEngine(const CoreEngine &) LLVM_DELETED_FUNCTION; + void operator=(const CoreEngine &) LLVM_DELETED_FUNCTION; ExplodedNode *generateCallExitBeginNode(ExplodedNode *N); public: /// Construct a CoreEngine object to analyze the provided CFG. - CoreEngine(SubEngine& subengine, SetOfConstDecls *VisitedCallees, + CoreEngine(SubEngine& subengine, FunctionSummariesTy *FS) : SubEng(subengine), G(new ExplodedGraph()), WList(WorkList::makeDFS()), BCounterFactory(G->getAllocator()), - AnalyzedCallees(VisitedCallees), FunctionSummaries(FS){} /// getGraph - Returns the exploded graph. @@ -185,20 +180,18 @@ public: struct NodeBuilderContext { const CoreEngine &Eng; const CFGBlock *Block; - ExplodedNode *Pred; + const LocationContext *LC; NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N) - : Eng(E), Block(B), Pred(N) { assert(B); assert(!N->isSink()); } - - ExplodedNode *getPred() const { return Pred; } + : Eng(E), Block(B), LC(N->getLocationContext()) { assert(B); } /// \brief Return the CFGBlock associated with this builder. const CFGBlock *getBlock() const { return Block; } /// \brief Returns the number of times the current basic block has been /// visited on the exploded graph path. - unsigned getCurrentBlockCount() const { + unsigned blockCount() const { return Eng.WList->getBlockCounter().getNumVisited( - Pred->getLocationContext()->getCurrentStackFrame(), + LC->getCurrentStackFrame(), Block->getBlockID()); } }; @@ -265,14 +258,21 @@ public: virtual ~NodeBuilder() {} /// \brief Generates a node in the ExplodedGraph. + ExplodedNode *generateNode(const ProgramPoint &PP, + ProgramStateRef State, + ExplodedNode *Pred) { + return generateNodeImpl(PP, State, Pred, false); + } + + /// \brief Generates a sink in the ExplodedGraph. /// /// When a node is marked as sink, the exploration from the node is stopped - - /// the node becomes the last node on the path. - ExplodedNode *generateNode(const ProgramPoint &PP, + /// the node becomes the last node on the path and certain kinds of bugs are + /// suppressed. + ExplodedNode *generateSink(const ProgramPoint &PP, ProgramStateRef State, - ExplodedNode *Pred, - bool MarkAsSink = false) { - return generateNodeImpl(PP, State, Pred, MarkAsSink); + ExplodedNode *Pred) { + return generateNodeImpl(PP, State, Pred, true); } const ExplodedNodeSet &getResults() { @@ -317,13 +317,18 @@ public: NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet, const NodeBuilderContext &Ctx, ProgramPoint &L) : NodeBuilder(Pred, DstSet, Ctx), Location(L) {} + ExplodedNode *generateNode(ProgramStateRef State, ExplodedNode *Pred, - const ProgramPointTag *Tag = 0, - bool MarkAsSink = false) { - ProgramPoint LocalLoc = (Tag ? Location.withTag(Tag): Location); + const ProgramPointTag *Tag = 0) { + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + return NodeBuilder::generateNode(LocalLoc, State, Pred); + } - ExplodedNode *N = generateNodeImpl(LocalLoc, State, Pred, MarkAsSink); + ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, + const ProgramPointTag *Tag = 0) { + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + ExplodedNode *N = NodeBuilder::generateSink(LocalLoc, State, Pred); if (N && N->isSink()) sinksGenerated.push_back(N); return N; @@ -336,7 +341,7 @@ public: /// \class StmtNodeBuilder /// \brief This builder class is useful for generating nodes that resulted from -/// visiting a statement. The main difference from it's parent NodeBuilder is +/// visiting a statement. The main difference from its parent NodeBuilder is /// that it creates a statement specific ProgramPoint. class StmtNodeBuilder: public NodeBuilder { NodeBuilder *EnclosingBldr; @@ -363,22 +368,27 @@ public: virtual ~StmtNodeBuilder(); + using NodeBuilder::generateNode; + using NodeBuilder::generateSink; + ExplodedNode *generateNode(const Stmt *S, ExplodedNode *Pred, ProgramStateRef St, - bool MarkAsSink = false, const ProgramPointTag *tag = 0, ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, Pred->getLocationContext(), tag); - return generateNodeImpl(L, St, Pred, MarkAsSink); + return NodeBuilder::generateNode(L, St, Pred); } - ExplodedNode *generateNode(const ProgramPoint &PP, + ExplodedNode *generateSink(const Stmt *S, ExplodedNode *Pred, - ProgramStateRef State, - bool MarkAsSink = false) { - return generateNodeImpl(PP, State, Pred, MarkAsSink); + ProgramStateRef St, + const ProgramPointTag *tag = 0, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), tag); + return NodeBuilder::generateSink(L, St, Pred); } }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h new file mode 100644 index 0000000..5ac97db --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h @@ -0,0 +1,52 @@ +//== DynamicTypeInfo.h - Runtime type information ----------------*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SA_CORE_DYNAMICTYPEINFO_H +#define LLVM_CLANG_SA_CORE_DYNAMICTYPEINFO_H + +#include "clang/AST/Type.h" + +namespace clang { +namespace ento { + +/// \brief Stores the currently inferred strictest bound on the runtime type +/// of a region in a given state along the analysis path. +class DynamicTypeInfo { +private: + QualType T; + bool CanBeASubClass; + +public: + + DynamicTypeInfo() : T(QualType()) {} + DynamicTypeInfo(QualType WithType, bool CanBeSub = true) + : T(WithType), CanBeASubClass(CanBeSub) {} + + /// \brief Return false if no dynamic type info is available. + bool isValid() const { return !T.isNull(); } + + /// \brief Returns the currently inferred upper bound on the runtime type. + QualType getType() const { return T; } + + /// \brief Returns false if the type information is precise (the type T is + /// the only type in the lattice), true otherwise. + bool canBeASubClass() const { return CanBeASubClass; } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(T); + ID.AddInteger((unsigned)CanBeASubClass); + } + bool operator==(const DynamicTypeInfo &X) const { + return T == X.T && CanBeASubClass == X.CanBeASubClass; + } +}; + +} // end ento +} // end clang + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h index b80213e..eb9bd85 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h @@ -33,10 +33,11 @@ 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) - : std::pair<const Stmt*, - const StackFrameContext*>(s, L ? L->getCurrentStackFrame():0) {} + EnvironmentEntry(const Stmt *s, const LocationContext *L); const Stmt *getStmt() const { return first; } const LocationContext *getLocationContext() const { return second; } @@ -76,9 +77,7 @@ public: /// Fetches the current binding of the expression in the /// Environment. - SVal getSVal(const EnvironmentEntry &E, - SValBuilder &svalBuilder, - bool useOnlyDirectBindings = false) const; + SVal getSVal(const EnvironmentEntry &E, SValBuilder &svalBuilder) const; /// Profile - Profile the contents of an Environment object for use /// in a FoldingSet. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index 1052d94..b112e66 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -60,45 +60,50 @@ class ExplodedNode : public llvm::FoldingSetNode { friend class SwitchNodeBuilder; friend class EndOfFunctionNodeBuilder; + /// Efficiently stores a list of ExplodedNodes, or an optional flag. + /// + /// NodeGroup provides opaque storage for a list of ExplodedNodes, optimizing + /// for the case when there is only one node in the group. This is a fairly + /// common case in an ExplodedGraph, where most nodes have only one + /// predecessor and many have only one successor. It can also be used to + /// store a flag rather than a node list, which ExplodedNode uses to mark + /// whether a node is a sink. If the flag is set, the group is implicitly + /// empty and no nodes may be added. class NodeGroup { - enum { Size1 = 0x0, SizeOther = 0x1, AuxFlag = 0x2, Mask = 0x3 }; + // Conceptually a discriminated union. If the low bit is set, the node is + // a sink. If the low bit is not set, the pointer refers to the storage + // for the nodes in the group. + // This is not a PointerIntPair in order to keep the storage type opaque. uintptr_t P; - - unsigned getKind() const { - return P & 0x1; - } - - void *getPtr() const { - assert (!getFlag()); - return reinterpret_cast<void*>(P & ~Mask); - } - - ExplodedNode *getNode() const { - return reinterpret_cast<ExplodedNode*>(getPtr()); - } public: - NodeGroup() : P(0) {} + NodeGroup(bool Flag = false) : P(Flag) { + assert(getFlag() == Flag); + } - ExplodedNode **begin() const; + ExplodedNode * const *begin() const; - ExplodedNode **end() const; + ExplodedNode * const *end() const; unsigned size() const; - bool empty() const { return (P & ~Mask) == 0; } + bool empty() const { return P == 0 || getFlag() != 0; } + /// Adds a node to the list. + /// + /// The group must not have been created with its flag set. void addNode(ExplodedNode *N, ExplodedGraph &G); + /// Replaces the single node in this group with a new node. + /// + /// Note that this should only be used when you know the group was not + /// created with its flag set, and that the group is empty or contains + /// only a single node. void replaceNode(ExplodedNode *node); - void setFlag() { - assert(P == 0); - P = AuxFlag; - } - + /// Returns whether this group was created with its flag set. bool getFlag() const { - return P & AuxFlag ? true : false; + return (P & 1); } }; @@ -119,9 +124,8 @@ public: explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, bool IsSink) - : Location(loc), State(state) { - if (IsSink) - Succs.setFlag(); + : Location(loc), State(state), Succs(IsSink) { + assert(isSink() == IsSink); } ~ExplodedNode() {} @@ -190,9 +194,9 @@ public: } // Iterators over successor and predecessor vertices. - typedef ExplodedNode** succ_iterator; + typedef ExplodedNode* const * succ_iterator; typedef const ExplodedNode* const * const_succ_iterator; - typedef ExplodedNode** pred_iterator; + typedef ExplodedNode* const * pred_iterator; typedef const ExplodedNode* const * const_pred_iterator; pred_iterator pred_begin() { return Preds.begin(); } @@ -278,11 +282,13 @@ protected: /// A list of nodes that can be reused. NodeVector FreeNodes; - /// A flag that indicates whether nodes should be recycled. - bool reclaimNodes; + /// Determines how often nodes are reclaimed. + /// + /// If this is 0, nodes will never be reclaimed. + unsigned ReclaimNodeInterval; /// Counter to determine when to reclaim nodes. - unsigned reclaimCounter; + unsigned ReclaimCounter; public: @@ -370,7 +376,9 @@ public: /// Enable tracking of recently allocated nodes for potential reclamation /// when calling reclaimRecentlyAllocatedNodes(). - void enableNodeReclamation() { reclaimNodes = true; } + void enableNodeReclamation(unsigned Interval) { + ReclaimCounter = ReclaimNodeInterval = Interval; + } /// Reclaim "uninteresting" nodes created since the last time this method /// was called. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 4addb9d..78b2542 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -16,6 +16,7 @@ #ifndef LLVM_CLANG_GR_EXPRENGINE #define LLVM_CLANG_GR_EXPRENGINE +#include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" @@ -70,17 +71,14 @@ class ExprEngine : public SubEngine { /// variables and symbols (as determined by a liveness analysis). ProgramStateRef CleanedState; - /// currentStmt - The current block-level statement. - const Stmt *currentStmt; - unsigned int currentStmtIdx; - const NodeBuilderContext *currentBuilderContext; - - /// Obj-C Class Identifiers. - IdentifierInfo* NSExceptionII; - - /// Obj-C Selectors. - Selector* NSExceptionInstanceRaiseSelectors; - Selector RaiseSel; + /// currStmt - The current block-level statement. + const Stmt *currStmt; + unsigned int currStmtIdx; + const NodeBuilderContext *currBldrCtx; + + /// Helper object to determine if an Objective-C message expression + /// implicitly never returns. + ObjCNoReturn ObjCNoRet; /// Whether or not GC is enabled in this analysis. bool ObjCGCEnabled; @@ -90,9 +88,13 @@ class ExprEngine : public SubEngine { /// destructor is called before the rest of the ExprEngine is destroyed. GRBugReporter BR; + /// The functions which have been analyzed through inlining. This is owned by + /// AnalysisConsumer. It can be null. + SetOfConstDecls *VisitedCallees; + public: ExprEngine(AnalysisManager &mgr, bool gcEnabled, - SetOfConstDecls *VisitedCallees, + SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS); ~ExprEngine(); @@ -126,8 +128,8 @@ public: BugReporter& getBugReporter() { return BR; } const NodeBuilderContext &getBuilderContext() { - assert(currentBuilderContext); - return *currentBuilderContext; + assert(currBldrCtx); + return *currBldrCtx; } bool isObjCGCEnabled() { return ObjCGCEnabled; } @@ -165,8 +167,12 @@ public: /// 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. void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out, - const Stmt *ReferenceStmt, const LocationContext *LC, + const Stmt *ReferenceStmt, const StackFrameContext *LC, const Stmt *DiagnosticStmt, ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind); @@ -192,7 +198,8 @@ public: /// Called by CoreEngine when processing the entrance of a CFGBlock. virtual void processCFGBlockEntrance(const BlockEdge &L, - NodeBuilderWithSinks &nodeBuilder); + NodeBuilderWithSinks &nodeBuilder, + ExplodedNode *Pred); /// ProcessBranch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a branch condition. @@ -213,7 +220,13 @@ public: /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. - void processEndOfFunction(NodeBuilderContext& BC); + void processEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred); + + /// Remove dead bindings/symbols before exiting a function. + void removeDeadOnEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst); /// Generate the entry node of the callee. void processCallEnter(CallEnter CE, ExplodedNode *Pred); @@ -258,9 +271,6 @@ public: BasicValueFactory& getBasicVals() { return StateMgr.getBasicVals(); } - const BasicValueFactory& getBasicVals() const { - return StateMgr.getBasicVals(); - } // FIXME: Remove when we migrate over to just using ValueManager. SymbolManager& getSymbolManager() { return SymMgr; } @@ -283,13 +293,14 @@ public: ExplodedNode *Pred, ExplodedNodeSet &Dst); - /// VisitAsmStmt - Transfer function logic for inline asm. - void VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst); + /// VisitGCCAsmStmt - Transfer function logic for inline asm. + void VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst); /// VisitMSAsmStmt - Transfer function logic for MS inline asm. void VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst); - + /// VisitBlockExpr - Transfer function logic for BlockExprs. void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, ExplodedNodeSet &Dst); @@ -380,8 +391,8 @@ public: void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); - void VisitCXXDestructor(QualType ObjectType, - const MemRegion *Dest, const Stmt *S, + void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, + const Stmt *S, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, @@ -395,14 +406,14 @@ public: ExplodedNode *Pred, ExplodedNodeSet &Dst); - /// evalEagerlyAssume - Given the nodes in 'Src', eagerly assume symbolic + /// evalEagerlyAssumeBinOpBifurcation - Given the nodes in 'Src', eagerly assume symbolic /// expressions of the form 'x != 0' and generate new nodes (stored in Dst) /// with those assumptions. - void evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + void evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, const Expr *Ex); std::pair<const ProgramPointTag *, const ProgramPointTag*> - getEagerlyAssumeTags(); + geteagerlyAssumeBinOpBifurcationTags(); SVal evalMinus(SVal X) { return X.isValid() ? svalBuilder.evalMinus(cast<NonLoc>(X)) : X; @@ -433,7 +444,8 @@ protected: /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore, VisitDeclStmt, and others. void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, - SVal location, SVal Val, bool atDeclInit = false); + SVal location, SVal Val, bool atDeclInit = false, + const ProgramPoint *PP = 0); public: // FIXME: 'tag' should be removed, and a LocationContext should be used @@ -490,6 +502,10 @@ private: ProgramStateRef St, SVal location, const ProgramPointTag *tag, bool isLoad); + /// Count the stack depth and determine if the call is recursive. + void examineStackFrames(const Decl *D, const LocationContext *LCtx, + bool &IsRecursive, unsigned &StackDepth); + bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred); bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); @@ -510,6 +526,8 @@ private: /// Traits for storing the call processing policy inside GDM. /// The GDM stores the corresponding CallExpr pointer. +// FIXME: This does not use the nice trait macros because it must be accessible +// from multiple translation units. struct ReplayWithoutInlining{}; template <> struct ProgramStateTrait<ReplayWithoutInlining> : diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 8044ed8..34fbc3c 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -99,11 +99,11 @@ public: // Untyped regions. SymbolicRegionKind, AllocaRegionKind, - BlockDataRegionKind, // Typed regions. BEG_TYPED_REGIONS, FunctionTextRegionKind = BEG_TYPED_REGIONS, BlockTextRegionKind, + BlockDataRegionKind, BEG_TYPED_VALUE_REGIONS, CompoundLiteralRegionKind = BEG_TYPED_VALUE_REGIONS, CXXThisRegionKind, @@ -140,6 +140,9 @@ public: const MemRegion *getBaseRegion() const; + /// Check if the region is a subregion of the given region. + virtual bool isSubRegionOf(const MemRegion *R) const; + const MemRegion *StripCasts(bool StripBaseCasts = true) const; bool hasGlobalsOrParametersStorage() const; @@ -171,8 +174,6 @@ public: template<typename RegionTy> const RegionTy* getAs() const; virtual bool isBoundable() const { return false; } - - static bool classof(const MemRegion*) { return true; } }; /// MemSpaceRegion - A memory region that represents a "memory space"; @@ -416,7 +417,7 @@ public: MemRegionManager* getMemRegionManager() const; - bool isSubRegionOf(const MemRegion* R) const; + virtual bool isSubRegionOf(const MemRegion* R) const; static bool classof(const MemRegion* R) { return R->getKind() > END_MEMSPACES; @@ -530,16 +531,28 @@ public: /// FunctionTextRegion - A region that represents code texts of function. class FunctionTextRegion : public CodeTextRegion { - const FunctionDecl *FD; + const NamedDecl *FD; public: - FunctionTextRegion(const FunctionDecl *fd, const MemRegion* sreg) - : CodeTextRegion(sreg, FunctionTextRegionKind), FD(fd) {} + FunctionTextRegion(const NamedDecl *fd, const MemRegion* sreg) + : CodeTextRegion(sreg, FunctionTextRegionKind), FD(fd) { + assert(isa<ObjCMethodDecl>(fd) || isa<FunctionDecl>(fd)); + } QualType getLocationType() const { - return getContext().getPointerType(FD->getType()); + const ASTContext &Ctx = getContext(); + if (const FunctionDecl *D = dyn_cast<FunctionDecl>(FD)) { + return Ctx.getPointerType(D->getType()); + } + + assert(isa<ObjCMethodDecl>(FD)); + assert(false && "Getting the type of ObjCMethod is not supported yet"); + + // TODO: We might want to return a different type here (ex: id (*ty)(...)) + // depending on how it is used. + return QualType(); } - - const FunctionDecl *getDecl() const { + + const NamedDecl *getDecl() const { return FD; } @@ -547,7 +560,7 @@ public: void Profile(llvm::FoldingSetNodeID& ID) const; - static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FunctionDecl *FD, + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const NamedDecl *FD, const MemRegion*); static bool classof(const MemRegion* R) { @@ -603,7 +616,7 @@ public: /// which correspond to "code+data". The distinction is important, because /// like a closure a block captures the values of externally referenced /// variables. -class BlockDataRegion : public SubRegion { +class BlockDataRegion : public TypedRegion { friend class MemRegionManager; const BlockTextRegion *BC; const LocationContext *LC; // Can be null */ @@ -612,13 +625,15 @@ class BlockDataRegion : public SubRegion { BlockDataRegion(const BlockTextRegion *bc, const LocationContext *lc, const MemRegion *sreg) - : SubRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), + : TypedRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), ReferencedVars(0), OriginalVars(0) {} public: const BlockTextRegion *getCodeRegion() const { return BC; } const BlockDecl *getDecl() const { return BC->getDecl(); } + + QualType getLocationType() const { return BC->getLocationType(); } class referenced_vars_iterator { const MemRegion * const *R; @@ -1212,7 +1227,7 @@ public: return getCXXBaseObjectRegion(baseReg->getDecl(), superRegion); } - const FunctionTextRegion *getFunctionTextRegion(const FunctionDecl *FD); + const FunctionTextRegion *getFunctionTextRegion(const NamedDecl *FD); const BlockTextRegion *getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, AnalysisDeclContext *AC); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index b0c51dd..86c94de 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines SymbolRef, ExprBindKey, and ProgramState*. +// This file defines the state of the program along the analysisa path. // //===----------------------------------------------------------------------===// @@ -16,6 +16,7 @@ #include "clang/Basic/LLVM.h" #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" @@ -39,7 +40,7 @@ class CallEvent; class CallEventManager; typedef ConstraintManager* (*ConstraintManagerCreator)(ProgramStateManager&, - SubEngine&); + SubEngine*); typedef StoreManager* (*StoreManagerCreator)(ProgramStateManager&); //===----------------------------------------------------------------------===// @@ -56,32 +57,6 @@ template <typename T> struct ProgramStateTrait { } }; -/// \class Stores the dynamic type information. -/// Information about type of an object at runtime. This is used by dynamic -/// dispatch implementation. -class DynamicTypeInfo { - QualType T; - bool CanBeASubClass; - -public: - DynamicTypeInfo() : T(QualType()) {} - DynamicTypeInfo(QualType WithType, bool CanBeSub = true) - : T(WithType), CanBeASubClass(CanBeSub) {} - - bool isValid() const { return !T.isNull(); } - - QualType getType() const { return T; } - bool canBeASubClass() const { return CanBeASubClass; } - - void Profile(llvm::FoldingSetNodeID &ID) const { - T.Profile(ID); - ID.AddInteger((unsigned)CanBeASubClass); - } - bool operator==(const DynamicTypeInfo &X) const { - return T == X.T && CanBeASubClass == X.CanBeASubClass; - } -}; - /// \class ProgramState /// ProgramState - This class encapsulates: /// @@ -100,7 +75,7 @@ public: typedef llvm::ImmutableMap<void*, void*> GenericDataMap; private: - void operator=(const ProgramState& R) const; // Do not implement. + void operator=(const ProgramState& R) LLVM_DELETED_FUNCTION; friend class ProgramStateManager; friend class ExplodedGraph; @@ -130,7 +105,12 @@ public: ~ProgramState(); /// Return the ProgramStateManager associated with this state. - ProgramStateManager &getStateManager() const { return *stateMgr; } + ProgramStateManager &getStateManager() const { + return *stateMgr; + } + + /// Return the ConstraintManager. + ConstraintManager &getConstraintManager() const; /// getEnvironment - Return the environment associated with this state. /// The environment is the mapping from expressions to values. @@ -210,11 +190,13 @@ public: // Binding and retrieving values to/from the environment and symbolic store. //==---------------------------------------------------------------------==// - /// BindCompoundLiteral - Return the state that has the bindings currently - /// in this state plus the bindings for the CompoundLiteral. + /// \brief Create a new state with the specified CompoundLiteral binding. + /// \param CL the compound literal expression (the binding key) + /// \param LC the LocationContext of the binding + /// \param V the value to bind. ProgramStateRef bindCompoundLiteral(const CompoundLiteralExpr *CL, - const LocationContext *LC, - SVal V) const; + const LocationContext *LC, + SVal V) const; /// Create a new state by binding the value 'V' to the statement 'S' in the /// state's environment. @@ -226,18 +208,16 @@ public: ProgramStateRef bindExprAndLocation(const Stmt *S, const LocationContext *LCtx, SVal location, SVal V) const; - - ProgramStateRef bindDecl(const VarRegion *VR, SVal V) const; - ProgramStateRef bindDeclWithNoInit(const VarRegion *VR) const; - - ProgramStateRef bindLoc(Loc location, SVal V) const; + ProgramStateRef bindLoc(Loc location, + SVal V, + bool notifyChanges = true) const; ProgramStateRef bindLoc(SVal location, SVal V) const; ProgramStateRef bindDefault(SVal loc, SVal V) const; - ProgramStateRef unbindLoc(Loc LV) const; + 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 @@ -271,11 +251,8 @@ public: /// Get the lvalue for an array index. SVal getLValue(QualType ElementType, SVal Idx, SVal Base) const; - const llvm::APSInt *getSymVal(SymbolRef sym) const; - /// Returns the SVal bound to the statement 'S' in the state's environment. - SVal getSVal(const Stmt *S, const LocationContext *LCtx, - bool useOnlyDirectBindings = false) const; + SVal getSVal(const Stmt *S, const LocationContext *LCtx) const; SVal getSValAsScalarOrLoc(const Stmt *Ex, const LocationContext *LCtx) const; @@ -469,7 +446,7 @@ public: StoreManagerCreator CreateStoreManager, ConstraintManagerCreator CreateConstraintManager, llvm::BumpPtrAllocator& alloc, - SubEngine &subeng); + SubEngine *subeng); ~ProgramStateManager(); @@ -481,9 +458,6 @@ public: BasicValueFactory &getBasicVals() { return svalBuilder->getBasicValueFactory(); } - const BasicValueFactory& getBasicVals() const { - return svalBuilder->getBasicValueFactory(); - } SValBuilder &getSValBuilder() { return *svalBuilder; @@ -515,10 +489,6 @@ public: const StackFrameContext *LCtx, SymbolReaper& SymReaper); - /// Marshal a new state for the callee in another translation unit. - /// 'state' is owned by the caller's engine. - ProgramStateRef MarshalState(ProgramStateRef state, const StackFrameContext *L); - public: SVal ArrayToPointer(Loc Array) { @@ -617,10 +587,6 @@ public: return ProgramStateTrait<T>::MakeContext(p); } - const llvm::APSInt* getSymVal(ProgramStateRef St, SymbolRef sym) { - return ConstraintMgr->getSymVal(St, sym); - } - void EndPath(ProgramStateRef St) { ConstraintMgr->EndPath(St); } @@ -631,6 +597,10 @@ public: // Out-of-line method definitions for ProgramState. //===----------------------------------------------------------------------===// +inline ConstraintManager &ProgramState::getConstraintManager() const { + return stateMgr->getConstraintManager(); +} + inline const VarRegion* ProgramState::getRegion(const VarDecl *D, const LocationContext *LC) const { @@ -695,15 +665,10 @@ inline SVal ProgramState::getLValue(QualType ElementType, SVal Idx, SVal Base) c return UnknownVal(); } -inline const llvm::APSInt *ProgramState::getSymVal(SymbolRef sym) const { - return getStateManager().getSymVal(this, sym); -} - -inline SVal ProgramState::getSVal(const Stmt *Ex, const LocationContext *LCtx, - bool useOnlyDirectBindings) const{ +inline SVal ProgramState::getSVal(const Stmt *Ex, + const LocationContext *LCtx) const{ return Env.getSVal(EnvironmentEntry(Ex, LCtx), - *getStateManager().svalBuilder, - useOnlyDirectBindings); + *getStateManager().svalBuilder); } inline SVal @@ -821,7 +786,7 @@ public: bool scan(const SymExpr *sym); }; -} // end GR namespace +} // end ento namespace } // end clang namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h index 1c7bedb..ea2a852 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h @@ -31,6 +31,26 @@ namespace clang { namespace ento { template <typename T> struct ProgramStatePartialTrait; + /// Declares a program state trait for type \p Type called \p Name, and + /// introduce a typedef named \c NameTy. + /// The macro should not be used inside namespaces, or for traits that must + /// be accessible from more than one translation unit. + #define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type) \ + namespace { \ + class Name {}; \ + typedef Type Name ## Ty; \ + } \ + namespace clang { \ + namespace ento { \ + template <> \ + struct ProgramStateTrait<Name> \ + : public ProgramStatePartialTrait<Name ## Ty> { \ + static void *GDMIndex() { static int Index; return &Index; } \ + }; \ + } \ + } + + // Partial-specialization for ImmutableMap. template <typename Key, typename Data, typename Info> @@ -71,6 +91,15 @@ namespace ento { } }; + /// Helper for registering a map trait. + /// + /// If the map type were written directly in the invocation of + /// REGISTER_TRAIT_WITH_PROGRAMSTATE, the comma in the template arguments + /// would be treated as a macro argument separator, which is wrong. + /// This allows the user to specify a map type in a way that the preprocessor + /// can deal with. + #define CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value) llvm::ImmutableMap<Key, Value> + // Partial-specialization for ImmutableSet. @@ -113,6 +142,7 @@ namespace ento { } }; + // Partial-specialization for ImmutableList. template <typename T> @@ -150,6 +180,7 @@ namespace ento { delete (typename data_type::Factory*) Ctx; } }; + // Partial specialization for bool. template <> struct ProgramStatePartialTrait<bool> { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index 83c3a56..5d72e73 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -72,7 +72,7 @@ public: virtual ~SValBuilder() {} bool haveSameType(const SymExpr *Sym1, const SymExpr *Sym2) { - return haveSameType(Sym1->getType(Context), Sym2->getType(Context)); + return haveSameType(Sym1->getType(), Sym2->getType()); } bool haveSameType(QualType Ty1, QualType Ty2) { @@ -142,19 +142,19 @@ public: // Forwarding methods to SymbolManager. - const SymbolConjured* getConjuredSymbol(const Stmt *stmt, - const LocationContext *LCtx, - QualType type, - unsigned visitCount, - const void *symbolTag = 0) { - return SymMgr.getConjuredSymbol(stmt, LCtx, type, visitCount, symbolTag); + const SymbolConjured* conjureSymbol(const Stmt *stmt, + const LocationContext *LCtx, + QualType type, + unsigned visitCount, + const void *symbolTag = 0) { + return SymMgr.conjureSymbol(stmt, LCtx, type, visitCount, symbolTag); } - const SymbolConjured* getConjuredSymbol(const Expr *expr, - const LocationContext *LCtx, - unsigned visitCount, - const void *symbolTag = 0) { - return SymMgr.getConjuredSymbol(expr, LCtx, visitCount, symbolTag); + const SymbolConjured* conjureSymbol(const Expr *expr, + const LocationContext *LCtx, + unsigned visitCount, + const void *symbolTag = 0) { + return SymMgr.conjureSymbol(expr, LCtx, visitCount, symbolTag); } /// Construct an SVal representing '0' for the specified type. @@ -169,20 +169,20 @@ public: /// The advantage of symbols derived/built from other symbols is that we /// preserve the relation between related(or even equivalent) expressions, so /// conjured symbols should be used sparingly. - DefinedOrUnknownSVal getConjuredSymbolVal(const void *symbolTag, - const Expr *expr, - const LocationContext *LCtx, - unsigned count); - DefinedOrUnknownSVal getConjuredSymbolVal(const void *symbolTag, - const Expr *expr, - const LocationContext *LCtx, - QualType type, - unsigned count); + DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + unsigned count); + DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + QualType type, + unsigned count); - DefinedOrUnknownSVal getConjuredSymbolVal(const Stmt *stmt, - const LocationContext *LCtx, - QualType type, - unsigned visitCount); + DefinedOrUnknownSVal conjureSymbolVal(const Stmt *stmt, + const LocationContext *LCtx, + QualType type, + unsigned visitCount); /// \brief Conjure a symbol representing heap allocated memory region. /// /// Note, the expression should represent a location. @@ -227,7 +227,7 @@ public: BasicVals.getValue(integer->getValue(), integer->getType()->isUnsignedIntegerOrEnumerationType())); } - + nonloc::ConcreteInt makeBoolVal(const ObjCBoolLiteralExpr *boolean) { return makeTruthVal(boolean->getValue(), boolean->getType()); } @@ -262,11 +262,6 @@ public: BasicVals.getIntWithPtrWidth(integer, isUnsigned)); } - NonLoc makeIntVal(uint64_t integer, unsigned bitWidth, bool isUnsigned) { - return nonloc::ConcreteInt( - BasicVals.getValue(integer, bitWidth, isUnsigned)); - } - NonLoc makeLocAsInteger(Loc loc, unsigned bits) { return nonloc::LocAsInteger(BasicVals.getPersistentSValWithData(loc, bits)); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index e0b5f64..c2134cf 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -154,9 +154,6 @@ public: SymExpr::symbol_iterator symbol_end() const { return SymExpr::symbol_end(); } - - // Implement isa<T> support. - static inline bool classof(const SVal*) { return true; } }; @@ -257,7 +254,7 @@ public: namespace nonloc { -enum Kind { ConcreteIntKind, SymbolValKind, SymExprValKind, +enum Kind { ConcreteIntKind, SymbolValKind, LocAsIntegerKind, CompoundValKind, LazyCompoundValKind }; /// \brief Represents symbolic expression. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index 138a590..979546b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -25,6 +25,7 @@ namespace clang { class Stmt; class Expr; class ObjCIvarDecl; +class CXXBasePath; class StackFrameContext; namespace ento { @@ -67,15 +68,26 @@ public: virtual StoreRef Bind(Store store, Loc loc, SVal val) = 0; virtual StoreRef BindDefault(Store store, const MemRegion *R, SVal V); - virtual StoreRef Remove(Store St, Loc L) = 0; - /// BindCompoundLiteral - Return the store that has the bindings currently - /// in 'store' plus the bindings for the CompoundLiteral. 'R' is the region - /// for the compound literal and 'BegInit' and 'EndInit' represent an - /// array of initializer values. - virtual StoreRef BindCompoundLiteral(Store store, - const CompoundLiteralExpr *cl, - const LocationContext *LC, SVal v) = 0; + /// \brief Create a new store with the specified binding removed. + /// \param ST the original store, that is the basis for the new store. + /// \param L the location whose binding should be removed. + virtual StoreRef killBinding(Store ST, Loc L) = 0; + + /// \brief Create a new store that binds a value to a compound literal. + /// + /// \param ST The original store whose bindings are the basis for the new + /// store. + /// + /// \param CL The compound literal to bind (the binding key). + /// + /// \param LC The LocationContext for the binding. + /// + /// \param V The value to bind to the compound literal. + virtual StoreRef bindCompoundLiteral(Store ST, + const CompoundLiteralExpr *CL, + const LocationContext *LC, + SVal V) = 0; /// getInitialStore - Returns the initial "empty" store representing the /// value bindings upon entry to an analyzed function. @@ -114,11 +126,15 @@ public: /// conversions between arrays and pointers. virtual SVal ArrayToPointer(Loc Array) = 0; - /// Evaluates DerivedToBase casts. - SVal evalDerivedToBase(SVal derived, const CastExpr *Cast); + /// Evaluates a chain of derived-to-base casts through the path specified in + /// \p Cast. + SVal evalDerivedToBase(SVal Derived, const CastExpr *Cast); + + /// Evaluates a chain of derived-to-base casts through the specified path. + SVal evalDerivedToBase(SVal Derived, const CXXBasePath &CastPath); /// Evaluates a derived-to-base cast through a single level of derivation. - virtual SVal evalDerivedToBase(SVal derived, QualType derivedPtrType) = 0; + SVal evalDerivedToBase(SVal Derived, QualType DerivedPtrType); /// \brief Evaluates C++ dynamic_cast cast. /// The callback may result in the following 3 scenarios: @@ -128,8 +144,7 @@ public: /// enough info to determine if the cast will succeed at run time). /// The function returns an SVal representing the derived class; it's /// valid only if Failed flag is set to false. - virtual SVal evalDynamicCast(SVal base, QualType derivedPtrType, - bool &Failed) = 0; + SVal evalDynamicCast(SVal Base, QualType DerivedPtrType, bool &Failed); const ElementRegion *GetElementZeroRegion(const MemRegion *R, QualType T); @@ -141,10 +156,6 @@ public: virtual StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper) = 0; - virtual StoreRef BindDecl(Store store, const VarRegion *VR, SVal initVal) = 0; - - virtual StoreRef BindDeclWithNoInit(Store store, const VarRegion *VR) = 0; - virtual bool includedInBindings(Store store, const MemRegion *region) const = 0; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index 68b81f1..1e71077 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -60,7 +60,8 @@ public: /// SubEngine is expected to populate dstNodes with new nodes representing /// updated analysis state, or generate no nodes at all if it doesn't. virtual void processCFGBlockEntrance(const BlockEdge &L, - NodeBuilderWithSinks &nodeBuilder) = 0; + NodeBuilderWithSinks &nodeBuilder, + ExplodedNode *Pred) = 0; /// Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a branch condition. @@ -81,7 +82,8 @@ public: /// Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. - virtual void processEndOfFunction(NodeBuilderContext& BC) = 0; + virtual void processEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred) = 0; // Generate the entry node of the callee. virtual void processCallEnter(CallEnter CE, ExplodedNode *Pred) = 0; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 5d27f86..873f773 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -65,12 +65,9 @@ public: virtual void dumpToStream(raw_ostream &os) const {} - virtual QualType getType(ASTContext&) const = 0; + virtual QualType getType() const = 0; virtual void Profile(llvm::FoldingSetNodeID& profile) = 0; - // Implement isa<T> support. - static inline bool classof(const SymExpr*) { return true; } - /// \brief Iterator over symbols that the current symbol depends on. /// /// For SymbolData, it's the symbol itself; for expressions, it's the @@ -144,7 +141,7 @@ public: virtual void dumpToStream(raw_ostream &os) const; - QualType getType(ASTContext&) const; + QualType getType() const; // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { @@ -173,7 +170,7 @@ public: unsigned getCount() const { return Count; } const void *getTag() const { return SymbolTag; } - QualType getType(ASTContext&) const; + QualType getType() const; virtual void dumpToStream(raw_ostream &os) const; @@ -211,7 +208,7 @@ public: SymbolRef getParentSymbol() const { return parentSymbol; } const TypedValueRegion *getRegion() const { return R; } - QualType getType(ASTContext&) const; + QualType getType() const; virtual void dumpToStream(raw_ostream &os) const; @@ -244,7 +241,7 @@ public: const SubRegion *getRegion() const { return R; } - QualType getType(ASTContext&) const; + QualType getType() const; virtual void dumpToStream(raw_ostream &os) const; @@ -283,7 +280,7 @@ public: unsigned getCount() const { return Count; } const void *getTag() const { return Tag; } - QualType getType(ASTContext&) const; + QualType getType() const; virtual void dumpToStream(raw_ostream &os) const; @@ -320,7 +317,7 @@ public: SymbolCast(const SymExpr *In, QualType From, QualType To) : SymExpr(CastSymbolKind), Operand(In), FromTy(From), ToTy(To) { } - QualType getType(ASTContext &C) const { return ToTy; } + QualType getType() const { return ToTy; } const SymExpr *getOperand() const { return Operand; } @@ -358,7 +355,7 @@ public: // FIXME: We probably need to make this out-of-line to avoid redundant // generation of virtual functions. - QualType getType(ASTContext &C) const { return T; } + QualType getType() const { return T; } BinaryOperator::Opcode getOpcode() const { return Op; } @@ -399,7 +396,7 @@ public: const SymExpr *rhs, QualType t) : SymExpr(IntSymKind), LHS(lhs), Op(op), RHS(rhs), T(t) {} - QualType getType(ASTContext &C) const { return T; } + QualType getType() const { return T; } BinaryOperator::Opcode getOpcode() const { return Op; } @@ -446,7 +443,7 @@ public: // FIXME: We probably need to make this out-of-line to avoid redundant // generation of virtual functions. - QualType getType(ASTContext &C) const { return T; } + QualType getType() const { return T; } virtual void dumpToStream(raw_ostream &os) const; @@ -495,18 +492,17 @@ public: /// \brief Make a unique symbol for MemRegion R according to its kind. const SymbolRegionValue* getRegionValueSymbol(const TypedValueRegion* R); - const SymbolConjured* getConjuredSymbol(const Stmt *E, - const LocationContext *LCtx, - QualType T, - unsigned VisitCount, - const void *SymbolTag = 0); + const SymbolConjured* conjureSymbol(const Stmt *E, + const LocationContext *LCtx, + QualType T, + unsigned VisitCount, + const void *SymbolTag = 0); - const SymbolConjured* getConjuredSymbol(const Expr *E, - const LocationContext *LCtx, - unsigned VisitCount, - const void *SymbolTag = 0) { - return getConjuredSymbol(E, LCtx, E->getType(), - VisitCount, SymbolTag); + const SymbolConjured* conjureSymbol(const Expr *E, + const LocationContext *LCtx, + unsigned VisitCount, + const void *SymbolTag = 0) { + return conjureSymbol(E, LCtx, E->getType(), VisitCount, SymbolTag); } const SymbolDerived *getDerivedSymbol(SymbolRef parentSymbol, @@ -541,7 +537,7 @@ public: const SymExpr *rhs, QualType t); QualType getType(const SymExpr *SE) const { - return SE->getType(Ctx); + return SE->getType(); } /// \brief Add artificial symbol dependency. @@ -584,9 +580,11 @@ public: /// /// If the statement is NULL, everything is this and parent contexts is /// considered live. - SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr, + /// If the stack frame context is NULL, everything on stack is considered + /// dead. + SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, SymbolManager& symmgr, StoreManager &storeMgr) - : LCtx(ctx->getCurrentStackFrame()), Loc(s), SymMgr(symmgr), + : LCtx(Ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {} ~SymbolReaper() {} diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h index 53205d3..c274cea 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h @@ -22,6 +22,8 @@ namespace ento { /// The GDM component containing the tainted root symbols. We lazily infer the /// taint of the dependent symbols. Currently, this is a map from a symbol to /// tag kind. TODO: Should support multiple tag kinds. +// FIXME: This does not use the nice trait macros because it must be accessible +// from multiple translation units. struct TaintMap {}; typedef llvm::ImmutableMap<SymbolRef, TaintTagType> TaintMapImpl; template<> struct ProgramStateTrait<TaintMap> |