diff options
author | Ted Kremenek <kremenek@apple.com> | 2009-02-04 23:49:09 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2009-02-04 23:49:09 +0000 |
commit | cf118d41f7930a18dce97416ef7834a62642f587 (patch) | |
tree | 6039374169ed1df174e825aac0b026fc6683d561 | |
parent | a9b66347bec984d1fd0378f85f092e2330344747 (diff) |
Overhaul BugReporter interface and implementation. The new interface cleans up
the ownership of BugTypes and BugReports. Now BugReports are owned by BugTypes,
and BugTypes are owned by the BugReporter object.
The major functionality change in this patch is that reports are not immediately
emitted by a call to BugReporter::EmitWarning (now called EmitReport), but
instead of queued up in report "equivalence classes". When
BugReporter::FlushReports() is called, it emits one diagnostic per report
equivalence class. This provides a nice cleanup with the caching of reports as
well as enables the BugReporter engine to select the "best" path for reporting a
path-sensitive bug based on all the locations in the ExplodedGraph that the same
bug could occur.
Along with this patch, Leaks are now coalesced into a common equivalence class
by their allocation site, and the "summary" diagnostic for leaks now reports the
allocation site as the location of the bug (this may later be augmented to also
provide an example location where the leak occurs).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63796 91177308-0d34-0410-b5e6-96231b3b80d8
22 files changed, 805 insertions, 909 deletions
diff --git a/Driver/AnalysisConsumer.cpp b/Driver/AnalysisConsumer.cpp index 49ebde71c8..73ce3f6625 100644 --- a/Driver/AnalysisConsumer.cpp +++ b/Driver/AnalysisConsumer.cpp @@ -415,7 +415,7 @@ static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf, LiveVariables* L = mgr.getLiveVariables(); if (!L) return; - GRExprEngine Eng(*mgr.getCFG(), *mgr.getCodeDecl(), mgr.getContext(), *L, + GRExprEngine Eng(*mgr.getCFG(), *mgr.getCodeDecl(), mgr.getContext(), *L, mgr, PurgeDead, mgr.getStoreManagerCreator(), mgr.getConstraintManagerCreator()); @@ -442,7 +442,7 @@ static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf, ExplodedNodeImpl::SetAuditor(0); // Display warnings. - Eng.EmitWarnings(mgr); + Eng.getBugReporter().FlushReports(); // Visualize the exploded graph. if (mgr.shouldVisualizeGraphviz()) diff --git a/include/clang/Analysis/PathSensitive/BugReporter.h b/include/clang/Analysis/PathSensitive/BugReporter.h index bcc28bdc5c..e0c3792323 100644 --- a/include/clang/Analysis/PathSensitive/BugReporter.h +++ b/include/clang/Analysis/PathSensitive/BugReporter.h @@ -23,6 +23,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/ImmutableSet.h" #include <list> namespace clang { @@ -36,100 +37,143 @@ class BugReporter; class GRExprEngine; class GRState; class Stmt; -class BugReport; +class BugType; class ParentMap; -class BugType { -public: - BugType() {} - virtual ~BugType(); +//===----------------------------------------------------------------------===// +// Interface for individual bug reports. +//===----------------------------------------------------------------------===// - virtual const char* getName() const = 0; - virtual const char* getDescription() const { return getName(); } - virtual const char* getCategory() const { return ""; } +// FIXME: Combine this with RangedBugReport and remove RangedBugReport. +class BugReport { + BugType& BT; + std::string Description; + const ExplodedNode<GRState> *EndNode; + SourceRange R; - virtual std::pair<const char**,const char**> getExtraDescriptiveText() { - return std::pair<const char**, const char**>(0, 0); +protected: + friend class BugReporter; + friend class BugReportEquivClass; + + virtual void Profile(llvm::FoldingSetNodeID& hash) const { + hash.AddInteger(getLocation().getRawEncoding()); } - - virtual void EmitWarnings(BugReporter& BR) {} - virtual void GetErrorNodes(std::vector<ExplodedNode<GRState>*>& Nodes) {} - virtual bool isCached(BugReport& R) = 0; -}; - -class BugTypeCacheLocation : public BugType { - llvm::SmallSet<ProgramPoint,10> CachedErrors; -public: - BugTypeCacheLocation() {} - virtual ~BugTypeCacheLocation() {} - virtual bool isCached(BugReport& R); - bool isCached(ProgramPoint P); -}; - - -class BugReport { - BugType& Desc; - const ExplodedNode<GRState> *EndNode; - SourceRange R; public: - BugReport(BugType& D, const ExplodedNode<GRState> *n) : Desc(D), EndNode(n) {} + BugReport(BugType& bt, const char* desc, const ExplodedNode<GRState> *n) + : BT(bt), Description(desc), EndNode(n) {} + virtual ~BugReport(); + + const BugType& getBugType() const { return BT; } + BugType& getBugType() { return BT; } - const BugType& getBugType() const { return Desc; } - BugType& getBugType() { return Desc; } - + // FIXME: Perhaps this should be moved into a subclass? const ExplodedNode<GRState>* getEndNode() const { return EndNode; } + // FIXME: Do we need this? Maybe getLocation() should return a ProgramPoint + // object. Stmt* getStmt(BugReporter& BR) const; - - const char* getName() const { return getBugType().getName(); } - - virtual const char* getDescription() const { - return getBugType().getDescription(); - } - virtual const char* getCategory() const { - return getBugType().getCategory(); - } + const std::string& getDescription() const { return Description; } + // FIXME: Is this needed? virtual std::pair<const char**,const char**> getExtraDescriptiveText() { - return getBugType().getExtraDescriptiveText(); + return std::make_pair((const char**)0,(const char**)0); } + // FIXME: Perhaps move this into a subclass. virtual PathDiagnosticPiece* getEndPath(BugReporter& BR, const ExplodedNode<GRState>* N); - virtual FullSourceLoc getLocation(SourceManager& Mgr); + /// getLocation - Return the "definitive" location of the reported bug. + /// While a bug can span an entire path, usually there is a specific + /// location that can be used to identify where the key issue occured. + /// This location is used by clients rendering diagnostics. + virtual SourceLocation getLocation() const; + /// getRanges - Returns the source ranges associated with this bug. virtual void getRanges(BugReporter& BR,const SourceRange*& beg, const SourceRange*& end); - + + // FIXME: Perhaps this should be moved into a subclass? virtual PathDiagnosticPiece* VisitNode(const ExplodedNode<GRState>* N, const ExplodedNode<GRState>* PrevN, const ExplodedGraph<GRState>& G, BugReporter& BR); }; + +//===----------------------------------------------------------------------===// +// BugTypes (collections of related reports). +//===----------------------------------------------------------------------===// + +class BugReportEquivClass : public llvm::FoldingSetNode { + // List of *owned* BugReport objects. + std::list<BugReport*> Reports; + + friend class BugReporter; + void AddReport(BugReport* R) { Reports.push_back(R); } +public: + BugReportEquivClass(BugReport* R) { Reports.push_back(R); } + ~BugReportEquivClass(); + + void Profile(llvm::FoldingSetNodeID& ID) const { + assert(!Reports.empty()); + (*Reports.begin())->Profile(ID); + } + + class iterator { + std::list<BugReport*>::iterator impl; + public: + iterator(std::list<BugReport*>::iterator i) : impl(i) {} + iterator& operator++() { ++impl; return *this; } + bool operator==(const iterator& I) const { return I.impl == impl; } + bool operator!=(const iterator& I) const { return I.impl != impl; } + BugReport* operator*() const { return *impl; } + BugReport* operator->() const { return *impl; } + }; + + iterator begin() { return iterator(Reports.begin()); } + iterator end() { return iterator(Reports.end()); } +}; + +class BugType { +private: + const std::string Name; + const std::string Category; + llvm::FoldingSet<BugReportEquivClass> EQClasses; + friend class BugReporter; +public: + BugType(const char *name, const char* cat) : Name(name), Category(cat) {} + virtual ~BugType(); + // FIXME: Should these be made strings as well? + const std::string& getName() const { return Name; } + const std::string& getCategory() const { return Category; } + + virtual void FlushReports(BugReporter& BR); + void AddReport(BugReport* BR); +}; + +//===----------------------------------------------------------------------===// +// Specialized subclasses of BugReport. +//===----------------------------------------------------------------------===// + +// FIXME: Collapse this with the default BugReport class. class RangedBugReport : public BugReport { std::vector<SourceRange> Ranges; - const char* desc; public: - RangedBugReport(BugType& D, ExplodedNode<GRState> *n, - const char* description = 0) - : BugReport(D, n), desc(description) {} + RangedBugReport(BugType& D, const char* description, ExplodedNode<GRState> *n) + : BugReport(D, description, n) {} - virtual ~RangedBugReport(); + ~RangedBugReport(); - virtual const char* getDescription() const { - return desc ? desc : BugReport::getDescription(); - } - + // FIXME: Move this out of line. void addRange(SourceRange R) { Ranges.push_back(R); } - - virtual void getRanges(BugReporter& BR,const SourceRange*& beg, - const SourceRange*& end) { + // FIXME: Move this out of line. + void getRanges(BugReporter& BR,const SourceRange*& beg, + const SourceRange*& end) { if (Ranges.empty()) { beg = NULL; @@ -142,6 +186,10 @@ public: } }; +//===----------------------------------------------------------------------===// +// BugReporter and friends. +//===----------------------------------------------------------------------===// + class BugReporterData { public: virtual ~BugReporterData(); @@ -158,16 +206,25 @@ class BugReporter { public: enum Kind { BaseBRKind, GRBugReporterKind }; -protected: - Kind kind; +private: + typedef llvm::ImmutableSet<BugType*> BugTypesTy; + BugTypesTy::Factory F; + BugTypesTy BugTypes; + + const Kind kind; BugReporterData& D; - BugReporter(BugReporterData& d, Kind k) : kind(k), D(d) {} - + void FlushReport(BugReportEquivClass& EQ); + +protected: + BugReporter(BugReporterData& d, Kind k) : BugTypes(F.GetEmptySet()), kind(k), D(d) {} + public: - BugReporter(BugReporterData& d) : kind(BaseBRKind), D(d) {} + BugReporter(BugReporterData& d) : BugTypes(F.GetEmptySet()), kind(BaseBRKind), D(d) {} virtual ~BugReporter(); + void FlushReports(); + Kind getKind() const { return kind; } Diagnostic& getDiagnostic() { @@ -178,29 +235,22 @@ public: return D.getPathDiagnosticClient(); } - ASTContext& getContext() { - return D.getContext(); - } + ASTContext& getContext() { return D.getContext(); } - SourceManager& getSourceManager() { - return D.getSourceManager(); - } + SourceManager& getSourceManager() { return D.getSourceManager(); } - CFG* getCFG() { - return D.getCFG(); - } + CFG* getCFG() { return D.getCFG(); } - ParentMap& getParentMap() { - return D.getParentMap(); - } + ParentMap& getParentMap() { return D.getParentMap(); } - LiveVariables* getLiveVariables() { - return D.getLiveVariables(); - } + LiveVariables* getLiveVariables() { return D.getLiveVariables(); } - virtual void GeneratePathDiagnostic(PathDiagnostic& PD, BugReport& R) {} + virtual void GeneratePathDiagnostic(PathDiagnostic& PD, + BugReportEquivClass& EQ) {} - void EmitWarning(BugReport& R); + void Register(BugType *BT); + + void EmitReport(BugReport *R); void EmitBasicReport(const char* BugName, const char* BugStr, SourceLocation Loc, @@ -234,11 +284,11 @@ public: static bool classof(const BugReporter* R) { return true; } }; +// FIXME: Get rid of GRBugReporter. It's the wrong abstraction. class GRBugReporter : public BugReporter { GRExprEngine& Eng; llvm::SmallSet<SymbolRef, 10> NotableSymbols; -public: - +public: GRBugReporter(BugReporterData& d, GRExprEngine& eng) : BugReporter(d, GRBugReporterKind), Eng(eng) {} @@ -246,9 +296,7 @@ public: /// getEngine - Return the analysis engine used to analyze a given /// function or method. - GRExprEngine& getEngine() { - return Eng; - } + GRExprEngine& getEngine() { return Eng; } /// getGraph - Get the exploded graph created by the analysis engine /// for the analyzed method or function. @@ -258,7 +306,8 @@ public: /// engine. GRStateManager& getStateManager(); - virtual void GeneratePathDiagnostic(PathDiagnostic& PD, BugReport& R); + virtual void GeneratePathDiagnostic(PathDiagnostic& PD, + BugReportEquivClass& R); void addNotableSymbol(SymbolRef Sym) { NotableSymbols.insert(Sym); @@ -273,22 +322,18 @@ public: return R->getKind() == GRBugReporterKind; } }; - class DiagBugReport : public RangedBugReport { std::list<std::string> Strs; FullSourceLoc L; - const char* description; public: - DiagBugReport(const char* desc, BugType& D, FullSourceLoc l) : - RangedBugReport(D, NULL), L(l), description(desc) {} + DiagBugReport(BugType& D, const char* desc, FullSourceLoc l) : + RangedBugReport(D, desc, 0), L(l) {} virtual ~DiagBugReport() {} - virtual FullSourceLoc getLocation(SourceManager&) { return L; } - virtual const char* getDescription() const { - return description; - } + // FIXME: Move out-of-line (virtual function). + SourceLocation getLocation() const { return L; } void addString(const std::string& s) { Strs.push_back(s); } @@ -297,39 +342,6 @@ public: str_iterator str_end() const { return Strs.end(); } }; -class DiagCollector : public DiagnosticClient { - std::list<DiagBugReport> Reports; - BugType& D; -public: - DiagCollector(BugType& d) : D(d) {} - - virtual ~DiagCollector() {} - - bool IncludeInDiagnosticCounts() const { return false; } - - void HandleDiagnostic(Diagnostic::Level DiagLevel, - const DiagnosticInfo &Info); - - // Iterators. - typedef std::list<DiagBugReport>::iterator iterator; - iterator begin() { return Reports.begin(); } - iterator end() { return Reports.end(); } -}; - -class SimpleBugType : public BugTypeCacheLocation { - const char* name; - const char* category; - const char* desc; -public: - SimpleBugType(const char* n) : name(n), category(""), desc(0) {} - SimpleBugType(const char* n, const char* c, const char* d) - : name(n), category(c), desc(d) {} - - virtual const char* getName() const { return name; } - virtual const char* getDescription() const { return desc ? desc : name; } - virtual const char* getCategory() const { return category; } -}; - } // end clang namespace #endif diff --git a/include/clang/Analysis/PathSensitive/ExplodedGraph.h b/include/clang/Analysis/PathSensitive/ExplodedGraph.h index 954b096b8e..757efdc64c 100644 --- a/include/clang/Analysis/PathSensitive/ExplodedGraph.h +++ b/include/clang/Analysis/PathSensitive/ExplodedGraph.h @@ -39,6 +39,12 @@ class GRIndirectGotoNodeBuilderImpl; class GRSwitchNodeBuilderImpl; class GREndPathNodebuilderImpl; +//===----------------------------------------------------------------------===// +// ExplodedGraph "implementation" classes. These classes are not typed to +// contain a specific kind of state. Typed-specialized versions are defined +// on top of these classes. +//===----------------------------------------------------------------------===// + class ExplodedNodeImpl : public llvm::FoldingSetNode { protected: friend class ExplodedGraphImpl; @@ -204,6 +210,7 @@ public: } }; +class InterExplodedGraphMapImpl; class ExplodedGraphImpl { protected: @@ -289,9 +296,39 @@ public: return llvm::dyn_cast<FunctionDecl>(&CodeDecl); } + typedef llvm::DenseMap<const ExplodedNodeImpl*, ExplodedNodeImpl*> NodeMap; + ExplodedGraphImpl* Trim(const ExplodedNodeImpl* const * NBeg, - const ExplodedNodeImpl* const * NEnd) const; + const ExplodedNodeImpl* const * NEnd, + InterExplodedGraphMapImpl *M) const; +}; + +class InterExplodedGraphMapImpl { + llvm::DenseMap<const ExplodedNodeImpl*, ExplodedNodeImpl*> M; + friend class ExplodedGraphImpl; + void add(const ExplodedNodeImpl* From, ExplodedNodeImpl* To); + +protected: + ExplodedNodeImpl* getMappedImplNode(const ExplodedNodeImpl* N) const; + + InterExplodedGraphMapImpl(); +public: + virtual ~InterExplodedGraphMapImpl() {} +}; + +//===----------------------------------------------------------------------===// +// Type-specialized ExplodedGraph classes. +//===----------------------------------------------------------------------===// +template <typename STATE> +class InterExplodedGraphMap : public InterExplodedGraphMapImpl { +public: + InterExplodedGraphMap() {}; + ~InterExplodedGraphMap() {}; + + ExplodedNode<STATE>* getMappedNode(const ExplodedNode<STATE>* N) const { + return static_cast<ExplodedNode<STATE>*>(getMappedImplNode(N)); + } }; template <typename STATE> @@ -409,13 +446,12 @@ public: return const_cast<ExplodedGraph>(this)->eop_end(); } - // Utility. - - ExplodedGraph* + std::pair<ExplodedGraph*, InterExplodedGraphMap<STATE>*> Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd) const { if (NBeg == NEnd) - return NULL; + return std::make_pair((ExplodedGraph*) 0, + (InterExplodedGraphMap<STATE>*) 0); assert (NBeg < NEnd); @@ -424,12 +460,15 @@ public: const ExplodedNodeImpl* const* NEndImpl = (const ExplodedNodeImpl* const*) NEnd; - return static_cast<ExplodedGraph*>(ExplodedGraphImpl::Trim(NBegImpl, - NEndImpl)); + llvm::OwningPtr<InterExplodedGraphMap<STATE> > + M(new InterExplodedGraphMap<STATE>()); + + ExplodedGraphImpl* G = ExplodedGraphImpl::Trim(NBegImpl, NEndImpl, M.get()); + + return std::make_pair(static_cast<ExplodedGraph*>(G), M.take()); } }; - - + template <typename StateTy> class ExplodedNodeSet { diff --git a/include/clang/Analysis/PathSensitive/GRExprEngine.h b/include/clang/Analysis/PathSensitive/GRExprEngine.h index 2a72794dd7..9a4ed49ffd 100644 --- a/include/clang/Analysis/PathSensitive/GRExprEngine.h +++ b/include/clang/Analysis/PathSensitive/GRExprEngine.h @@ -20,18 +20,16 @@ #include "clang/Analysis/PathSensitive/GRState.h" #include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h" #include "clang/Analysis/PathSensitive/GRTransferFuncs.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/AST/Type.h" #include "clang/AST/ExprObjC.h" namespace clang { - class BugType; class PathDiagnosticClient; class Diagnostic; - class BugReporterData; -class GRExprEngine { - +class GRExprEngine { public: typedef GRState StateTy; typedef ExplodedGraph<StateTy> GraphTy; @@ -44,7 +42,6 @@ public: typedef GRSwitchNodeBuilder<StateTy> SwitchNodeBuilder; typedef GREndPathNodeBuilder<StateTy> EndPathNodeBuilder; typedef ExplodedNodeSet<StateTy> NodeSet; - protected: GRCoreEngine<GRExprEngine> CoreEngine; @@ -62,11 +59,7 @@ protected: /// StateMgr - Object that manages the data for all created states. GRStateManager StateMgr; - - /// BugTypes - Objects used for reporting bugs. - typedef std::vector<BugType*> BugTypeSet; - BugTypeSet BugTypes; - + /// SymMgr - Object that manages the symbol information. SymbolManager& SymMgr; @@ -92,6 +85,11 @@ protected: /// PurgeDead - Remove dead bindings before processing a statement. bool PurgeDead; + /// BR - The BugReporter associated with this engine. It is important that + // this object be placed at the very end of member variables so that its + // destructor is called before the rest of the GRExprEngine is destroyed. + GRBugReporter BR; + public: typedef llvm::SmallPtrSet<NodeTy*,2> ErrorNodes; typedef llvm::DenseMap<NodeTy*, Expr*> UndefArgsTy; @@ -177,10 +175,11 @@ public: public: GRExprEngine(CFG& cfg, Decl& CD, ASTContext& Ctx, LiveVariables& L, + BugReporterData& BRD, bool purgeDead, StoreManagerCreator SMC = CreateBasicStoreManager, ConstraintManagerCreator CMC = CreateBasicConstraintManager); - + ~GRExprEngine(); void ExecuteWorkList(unsigned Steps = 150000) { @@ -195,6 +194,8 @@ public: GRTransferFuncs& getTF() { return *StateMgr.TF; } + BugReporter& getBugReporter() { return BR; } + /// setTransferFunctions void setTransferFunctions(GRTransferFuncs* tf); @@ -219,27 +220,9 @@ public: GraphTy& getGraph() { return G; } const GraphTy& getGraph() const { return G; } - - typedef BugTypeSet::iterator bug_type_iterator; - typedef BugTypeSet::const_iterator const_bug_type_iterator; - - bug_type_iterator bug_types_begin() { return BugTypes.begin(); } - bug_type_iterator bug_types_end() { return BugTypes.end(); } - const_bug_type_iterator bug_types_begin() const { return BugTypes.begin(); } - const_bug_type_iterator bug_types_end() const { return BugTypes.end(); } - - /// Register - Register a BugType with the analyzer engine. A registered - /// BugType object will have its 'EmitWarnings' method called when the - /// the analyzer finishes analyzing a method or function. - void Register(BugType* B) { - BugTypes.push_back(B); - } - void RegisterInternalChecks(); - void EmitWarnings(BugReporterData& BRData); - bool isRetStackAddr(const NodeTy* N) const { return N->isSink() && RetsStackAddr.count(const_cast<NodeTy*>(N)) != 0; } diff --git a/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h b/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h index 66b2fb1ae8..e54b31dfe8 100644 --- a/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h +++ b/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h @@ -33,7 +33,6 @@ class GRSimpleAPICheck : public GRAuditor<GRState> { public: GRSimpleAPICheck() {} virtual ~GRSimpleAPICheck() {} - virtual void EmitWarnings(BugReporter& BR) = 0; }; } // end namespace clang diff --git a/include/clang/Analysis/PathSensitive/GRTransferFuncs.h b/include/clang/Analysis/PathSensitive/GRTransferFuncs.h index 6f4266b9df..dba42c2515 100644 --- a/include/clang/Analysis/PathSensitive/GRTransferFuncs.h +++ b/include/clang/Analysis/PathSensitive/GRTransferFuncs.h @@ -23,28 +23,24 @@ namespace clang { class GRExprEngine; + class BugReporter; class ObjCMessageExpr; class GRTransferFuncs { - - friend class GRExprEngine; - + friend class GRExprEngine; protected: - - virtual SVal DetermEvalBinOpNN(GRExprEngine& Eng, BinaryOperator::Opcode Op, NonLoc L, NonLoc R) { return UnknownVal(); } - public: GRTransferFuncs() {} virtual ~GRTransferFuncs() {} virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) {} - virtual void RegisterChecks(GRExprEngine& Eng); + virtual void RegisterChecks(BugReporter& BR) {} // Casts. diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp index be729bee6c..8a5c3e0a84 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ b/lib/Analysis/BasicObjCFoundationChecks.cpp @@ -23,15 +23,12 @@ #include "clang/Analysis/PathSensitive/MemRegion.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/LocalCheckers.h" - #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ASTContext.h" #include "llvm/Support/Compiler.h" -#include <sstream> - using namespace clang; static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) { @@ -60,64 +57,17 @@ static const char* GetReceiverNameType(ObjCMessageExpr* ME) { namespace { -class VISIBILITY_HIDDEN APIMisuse : public BugTypeCacheLocation { -public: - const char* getCategory() const { - return "API Misuse (Apple)"; - } -}; - -class VISIBILITY_HIDDEN NilArg : public APIMisuse { +class VISIBILITY_HIDDEN APIMisuse : public BugType { public: - virtual ~NilArg() {} - - virtual const char* getName() const { - return "nil argument"; - } - - class Report : public BugReport { - std::string Msg; - const char* s; - SourceRange R; - public: - - Report(NilArg& Desc, ExplodedNode<GRState>* N, - ObjCMessageExpr* ME, unsigned Arg) - : BugReport(Desc, N) { - - Expr* E = ME->getArg(Arg); - R = E->getSourceRange(); - - std::ostringstream os; - - os << "Argument to '" << GetReceiverNameType(ME) << "' method '" - << ME->getSelector().getAsString() << "' cannot be nil."; - - Msg = os.str(); - s = Msg.c_str(); - } - - virtual ~Report() {} - - virtual const char* getDescription() const { return s; } - - virtual void getRanges(BugReporter& BR, - const SourceRange*& B, const SourceRange*& E) { - B = &R; - E = B+1; - } - }; + APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} }; - class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck { - NilArg Desc; + APIMisuse *BT; + BugReporter& BR; ASTContext &Ctx; GRStateManager* VMgr; - - typedef std::vector<BugReport*> ErrorsTy; - ErrorsTy Errors; - + SVal GetSVal(const GRState* St, Expr* E) { return VMgr->GetSVal(St, E); } bool isNSString(ObjCInterfaceType* T, const char* suffix); @@ -129,26 +79,26 @@ class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck { bool CheckNilArg(NodeTy* N, unsigned Arg); public: - BasicObjCFoundationChecks(ASTContext& ctx, GRStateManager* vmgr) - : Ctx(ctx), VMgr(vmgr) {} - - virtual ~BasicObjCFoundationChecks() { - for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I) - delete *I; - } - - virtual bool Audit(ExplodedNode<GRState>* N, GRStateManager&); - - virtual void EmitWarnings(BugReporter& BR); - -private: - - void AddError(BugReport* R) { - Errors.push_back(R); - } - - void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) { - AddError(new NilArg::Report(Desc, N, ME, Arg)); + BasicObjCFoundationChecks(ASTContext& ctx, GRStateManager* vmgr, + BugReporter& br) + : BT(0), BR(br), Ctx(ctx), VMgr(vmgr) {} + + bool Audit(ExplodedNode<GRState>* N, GRStateManager&); + +private: + void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Argument to '" << GetReceiverNameType(ME) << "' method '" + << ME->getSelector().getAsString() << "' cannot be nil."; + + // Lazily create the BugType object for NilArg. This will be owned + // by the BugReporter object 'BR' once we call BR.EmitWarning. + if (!BT) BT = new APIMisuse("nil argument"); + + RangedBugReport *R = new RangedBugReport(*BT, os.str().c_str(), N); + R->addRange(ME->getArg(Arg)->getSourceRange()); + BR.EmitReport(R); } }; @@ -157,9 +107,9 @@ private: GRSimpleAPICheck* clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, - GRStateManager* VMgr) { + GRStateManager* VMgr, BugReporter& BR) { - return new BasicObjCFoundationChecks(Ctx, VMgr); + return new BasicObjCFoundationChecks(Ctx, VMgr, BR); } @@ -201,13 +151,6 @@ static inline bool isNil(SVal X) { // Error reporting. //===----------------------------------------------------------------------===// - -void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) { - - for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I) - BR.EmitWarning(**I); -} - bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) { ObjCMessageExpr* ME = cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); @@ -307,41 +250,9 @@ bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N, //===----------------------------------------------------------------------===// namespace { - -class VISIBILITY_HIDDEN BadCFNumberCreate : public APIMisuse{ -public: - typedef std::vector<BugReport*> AllErrorsTy; - AllErrorsTy AllErrors; - - virtual const char* getName() const { - return "Bad use of CFNumber |