From: Argyrios Kyrtzidis Date: Mon, 28 Feb 2011 17:36:18 +0000 (+0000) Subject: [analyzer] Migrate NSErrorChecker and DereferenceChecker to CheckerV2. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b3d74da3e1620c9a7a378afb5f244e4987e6713e;p=clang [analyzer] Migrate NSErrorChecker and DereferenceChecker to CheckerV2. They cooperate in that NSErrorChecker listens for ImplicitNullDerefEvent events that DereferenceChecker can dispatch. ImplicitNullDerefEvent is when we dereferenced a location that may be null. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126659 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/CheckerV2.h b/include/clang/StaticAnalyzer/Core/CheckerV2.h index a4c7c7d806..07f1109864 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerV2.h +++ b/include/clang/StaticAnalyzer/Core/CheckerV2.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_SA_CORE_CHECKERV2 #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/Support/Casting.h" namespace clang { @@ -358,6 +359,14 @@ public: } }; +/// \brief We dereferenced a location that may be null. +struct ImplicitNullDerefEvent { + SVal Location; + bool IsLoad; + ExplodedNode *SinkNode; + BugReporter *BR; +}; + } // end ento namespace } // end clang namespace diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 99556847b5..4dec676404 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -70,11 +70,19 @@ def ObjCMethSigsChecker : Checker<"MethodSigs">, def ObjCUnusedIvarsChecker : Checker<"UnusedIvars">, HelpText<"Warn about private ivars that are never used">, DescFile<"ObjCUnusedIVarsChecker.cpp">; + +def NSErrorChecker : Checker<"NSError">, + HelpText<"Check usage of NSError** parameters">, + DescFile<"NSErrorChecker.cpp">; } // end "cocoa" let ParentPackage = Core in { +def DereferenceChecker : Checker<"Deref">, + HelpText<"Check for null pointers at loads and stores">, + DescFile<"DereferenceChecker.cpp">; + def CallAndMessageChecker : Checker<"CallAndMsg">, HelpText<"Check for errors of call and objc message expressions">, DescFile<"CallAndMessageChecker.cpp">; @@ -162,6 +170,11 @@ def CFRetainReleaseChecker : Checker<"CFRetainRelease">, HelpText<"Check for null arguments to CFRetain/CFRelease">, DescFile<"BasicObjCFoundationChecks.cpp">; +def CFErrorChecker : Checker<"CFError">, + InPackage, + HelpText<"Check usage of CFErrorRef* parameters">, + DescFile<"NSErrorChecker.cpp">; + def LLVMConventionsChecker : Checker<"Conventions">, InPackage, HelpText<"Check code for LLVM codebase conventions">, diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 606ac4ab4e..11a20d39ab 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -12,51 +12,31 @@ // //===----------------------------------------------------------------------===// -#include "InternalChecks.h" +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; namespace { -class DereferenceChecker : public Checker { - BuiltinBug *BT_null; - BuiltinBug *BT_undef; - llvm::SmallVector ImplicitNullDerefNodes; +class DereferenceChecker + : public CheckerV2< check::Location, + EventDispatcher > { + mutable llvm::OwningPtr BT_null; + mutable llvm::OwningPtr BT_undef; + public: - DereferenceChecker() : BT_null(0), BT_undef(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void visitLocation(CheckerContext &C, const Stmt *S, SVal location, - bool isLoad); - - std::pair - getImplicitNodes() const { - return std::make_pair(ImplicitNullDerefNodes.data(), - ImplicitNullDerefNodes.data() + - ImplicitNullDerefNodes.size()); - } - void AddDerefSource(llvm::raw_ostream &os, - llvm::SmallVectorImpl &Ranges, - const Expr *Ex, bool loadedFrom = false); + void checkLocation(SVal location, bool isLoad, CheckerContext &C) const; + + static void AddDerefSource(llvm::raw_ostream &os, + llvm::SmallVectorImpl &Ranges, + const Expr *Ex, bool loadedFrom = false); }; } // end anonymous namespace -void ento::RegisterDereferenceChecker(ExprEngine &Eng) { - Eng.registerCheck(new DereferenceChecker()); -} - -std::pair -ento::GetImplicitNullDereferences(ExprEngine &Eng) { - DereferenceChecker *checker = Eng.getChecker(); - if (!checker) - return std::make_pair((ExplodedNode * const *) 0, - (ExplodedNode * const *) 0); - return checker->getImplicitNodes(); -} - void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os, llvm::SmallVectorImpl &Ranges, const Expr *Ex, @@ -85,13 +65,13 @@ void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os, } } -void DereferenceChecker::visitLocation(CheckerContext &C, const Stmt *S, - SVal l, bool isLoad) { +void DereferenceChecker::checkLocation(SVal l, bool isLoad, + CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) - BT_undef = new BuiltinBug("Dereference of undefined pointer value"); + BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); @@ -108,6 +88,7 @@ void DereferenceChecker::visitLocation(CheckerContext &C, const Stmt *S, if (!isa(location)) return; + const Stmt *S = C.getStmt(); const GRState *state = C.getState(); const GRState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(location); @@ -123,7 +104,7 @@ void DereferenceChecker::visitLocation(CheckerContext &C, const Stmt *S, // We know that 'location' cannot be non-null. This is what // we call an "explicit" null dereference. if (!BT_null) - BT_null = new BuiltinBug("Dereference of null pointer"); + BT_null.reset(new BuiltinBug("Dereference of null pointer")); llvm::SmallString<100> buf; llvm::SmallVector Ranges; @@ -195,11 +176,17 @@ void DereferenceChecker::visitLocation(CheckerContext &C, const Stmt *S, // Otherwise, we have the case where the location could either be // null or not-null. Record the error node as an "implicit" null // dereference. - if (ExplodedNode *N = C.generateSink(nullState)) - ImplicitNullDerefNodes.push_back(N); + if (ExplodedNode *N = C.generateSink(nullState)) { + ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; + dispatchEvent(event); + } } } // From this point forward, we know that the location is not null. C.addTransition(notNullState); } + +void ento::registerDereferenceChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp index 5b8bd18d81..552ca7d84a 100644 --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -308,22 +308,6 @@ void ExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst, // Engine construction and deletion. //===----------------------------------------------------------------------===// -static void RegisterInternalChecks(ExprEngine &Eng) { - // Register internal "built-in" BugTypes with the BugReporter. These BugTypes - // are different than what probably many checks will do since they don't - // create BugReports on-the-fly but instead wait until ExprEngine finishes - // analyzing a function. Generation of BugReport objects is done via a call - // to 'FlushReports' from BugReporter. - // The following checks do not need to have their associated BugTypes - // explicitly registered with the BugReporter. If they issue any BugReports, - // their associated BugType will get registered with the BugReporter - // automatically. Note that the check itself is owned by the ExprEngine - // object. - // CallAndMessageChecker should be registered before AttrNonNullChecker, - // where we assume arguments are not undefined. - RegisterDereferenceChecker(Eng); -} - ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf) : AMgr(mgr), Engine(*this), @@ -338,8 +322,6 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf) NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), RaiseSel(GetNullarySelector("raise", getContext())), BR(mgr, *this), TF(tf) { - // Register internal checks. - RegisterInternalChecks(*this); // FIXME: Eventually remove the TF object entirely. TF->RegisterChecks(*this); diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 7a1b978775..ed7094f124 100644 --- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -1,4 +1,4 @@ -//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==// +//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==// // // The LLVM Compiler Infrastructure // @@ -15,11 +15,12 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" -#include "BasicObjCFoundationChecks.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" #include "llvm/ADT/SmallVector.h" @@ -27,142 +28,248 @@ using namespace clang; using namespace ento; -namespace { -class NSErrorChecker : public BugType { - const Decl &CodeDecl; - const bool isNSErrorWarning; - IdentifierInfo * const II; - ExprEngine &Eng; +static bool IsNSError(const ParmVarDecl *PD, IdentifierInfo *II); +static bool IsCFError(const ParmVarDecl *PD, IdentifierInfo *II); - void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams); +//===----------------------------------------------------------------------===// +// NSErrorMethodChecker +//===----------------------------------------------------------------------===// - void CheckSignature(const FunctionDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams); +namespace { +class NSErrorMethodChecker + : public CheckerV2< check::ASTDecl > { + mutable IdentifierInfo *II; - bool CheckNSErrorArgument(QualType ArgTy); - bool CheckCFErrorArgument(QualType ArgTy); +public: + NSErrorMethodChecker() : II(0) { } - void CheckParamDeref(const VarDecl *V, const LocationContext *LC, - const GRState *state, BugReporter& BR); + void checkASTDecl(const ObjCMethodDecl *D, + AnalysisManager &mgr, BugReporter &BR) const; +}; +} - void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); +void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + if (!D->isThisDeclarationADefinition()) + return; + if (!D->getResultType()->isVoidType()) + return; -public: - NSErrorChecker(const Decl &D, bool isNSError, ExprEngine& eng) - : BugType(isNSError ? "NSError** null dereference" - : "CFErrorRef* null dereference", - "Coding conventions (Apple)"), - CodeDecl(D), - isNSErrorWarning(isNSError), - II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")), - Eng(eng) {} - - void FlushReports(BugReporter& BR); -}; + if (!II) + II = &D->getASTContext().Idents.get("NSError"); -} // end anonymous namespace + bool hasNSError = false; + for (ObjCMethodDecl::param_iterator + I = D->param_begin(), E = D->param_end(); I != E; ++I) { + if (IsNSError(*I, II)) { + hasNSError = true; + break; + } + } -void ento::RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng, - const Decl &D) { - BR.Register(new NSErrorChecker(D, true, Eng)); - BR.Register(new NSErrorChecker(D, false, Eng)); + if (hasNSError) { + const char *err = "Method accepting NSError** " + "should have a non-void return value to indicate whether or not an " + "error occurred"; + BR.EmitBasicReport("Bad return type when passing NSError**", + "Coding conventions (Apple)", err, D->getLocation()); + } } -void NSErrorChecker::FlushReports(BugReporter& BR) { - // Get the analysis engine and the exploded analysis graph. - ExplodedGraph& G = Eng.getGraph(); +//===----------------------------------------------------------------------===// +// CFErrorFunctionChecker +//===----------------------------------------------------------------------===// - // Get the ASTContext, which is useful for querying type information. - ASTContext &Ctx = BR.getContext(); +namespace { +class CFErrorFunctionChecker + : public CheckerV2< check::ASTDecl > { + mutable IdentifierInfo *II; - QualType ResultTy; - llvm::SmallVector ErrorParams; +public: + CFErrorFunctionChecker() : II(0) { } - if (const ObjCMethodDecl* MD = dyn_cast(&CodeDecl)) - CheckSignature(*MD, ResultTy, ErrorParams); - else if (const FunctionDecl* FD = dyn_cast(&CodeDecl)) - CheckSignature(*FD, ResultTy, ErrorParams); - else - return; + void checkASTDecl(const FunctionDecl *D, + AnalysisManager &mgr, BugReporter &BR) const; +}; +} - if (ErrorParams.empty()) +void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + if (!D->isThisDeclarationADefinition()) + return; + if (!D->getResultType()->isVoidType()) return; - if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); + if (!II) + II = &D->getASTContext().Idents.get("CFErrorRef"); - for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end(); - RI!=RE; ++RI) { - // Scan the parameters for an implicit null dereference. - for (llvm::SmallVectorImpl::iterator I=ErrorParams.begin(), - E=ErrorParams.end(); I!=E; ++I) - CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR); + bool hasCFError = false; + for (FunctionDecl::param_const_iterator + I = D->param_begin(), E = D->param_end(); I != E; ++I) { + if (IsCFError(*I, II)) { + hasCFError = true; + break; + } + } + + if (hasCFError) { + const char *err = "Function accepting CFErrorRef* " + "should have a non-void return value to indicate whether or not an " + "error occurred"; + BR.EmitBasicReport("Bad return type when passing CFErrorRef*", + "Coding conventions (Apple)", err, D->getLocation()); } } -void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); +//===----------------------------------------------------------------------===// +// NSOrCFErrorDerefChecker +//===----------------------------------------------------------------------===// - if (isa(CodeDecl)) - os << "Method"; - else - os << "Function"; +namespace { - os << " accepting "; - os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*"); - os << " should have a non-void return value to indicate whether or not an " - "error occurred"; +class NSErrorDerefBug : public BugType { +public: + NSErrorDerefBug() : BugType("NSError** null dereference", + "Coding conventions (Apple)") {} +}; - BR.EmitBasicReport(isNSErrorWarning - ? "Bad return type when passing NSError**" - : "Bad return type when passing CFError*", - getCategory(), os.str(), - CodeDecl.getLocation()); +class CFErrorDerefBug : public BugType { +public: + CFErrorDerefBug() : BugType("CFErrorRef* null dereference", + "Coding conventions (Apple)") {} +}; + +} + +namespace { +class NSOrCFErrorDerefChecker + : public CheckerV2< check::Location, + check::Event > { + mutable IdentifierInfo *NSErrorII, *CFErrorII; +public: + bool ShouldCheckNSError, ShouldCheckCFError; + NSOrCFErrorDerefChecker() : NSErrorII(0), CFErrorII(0), + ShouldCheckNSError(0), ShouldCheckCFError(0) { } + + void checkLocation(SVal loc, bool isLoad, CheckerContext &C) const; + void checkEvent(ImplicitNullDerefEvent event) const; +}; +} + +namespace { struct NSErrorOut {}; } +namespace { struct CFErrorOut {}; } + +typedef llvm::ImmutableMap ErrorOutFlag; + +namespace clang { +namespace ento { + template <> + struct GRStateTrait : public GRStatePartialTrait { + static void *GDMIndex() { static int index = 0; return &index; } + }; + template <> + struct GRStateTrait : public GRStatePartialTrait { + static void *GDMIndex() { static int index = 0; return &index; } + }; +} } -void -NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams) { +template +static bool hasFlag(SVal val, const GRState *state) { + if (SymbolRef sym = val.getAsSymbol()) + if (const unsigned *attachedFlags = state->get(sym)) + return *attachedFlags; + return false; +} - ResultTy = M.getResultType(); +template +static void setFlag(const GRState *state, SVal val, CheckerContext &C) { + // We tag the symbol that the SVal wraps. + if (SymbolRef sym = val.getAsSymbol()) + C.addTransition(state->set(sym, true)); +} - for (ObjCMethodDecl::param_iterator I=M.param_begin(), - E=M.param_end(); I!=E; ++I) { +void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, + CheckerContext &C) const { + if (!isLoad) + return; + if (loc.isUndef() || !isa(loc)) + return; - QualType T = (*I)->getType(); + const GRState *state = C.getState(); + + // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting + // SVal so that we can later check it when handling the + // ImplicitNullDerefEvent event. + // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of + // function ? + + const VarDecl *VD = loc.getAsVarDecl(); + if (!VD) return; + const ParmVarDecl *PD = dyn_cast(VD); + if (!PD) return; + + if (!NSErrorII) + NSErrorII = &PD->getASTContext().Idents.get("NSError"); + if (!CFErrorII) + CFErrorII = &PD->getASTContext().Idents.get("CFErrorRef"); + + if (ShouldCheckNSError && IsNSError(PD, NSErrorII)) { + setFlag(state, state->getSVal(cast(loc)), C); + return; + } - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); + if (ShouldCheckCFError && IsCFError(PD, CFErrorII)) { + setFlag(state, state->getSVal(cast(loc)), C); + return; } } -void -NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams) { +void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { + if (event.IsLoad) + return; - ResultTy = F.getResultType(); + SVal loc = event.Location; + const GRState *state = event.SinkNode->getState(); + BugReporter &BR = *event.BR; - for (FunctionDecl::param_const_iterator I = F.param_begin(), - E = F.param_end(); I != E; ++I) { + bool isNSError = hasFlag(loc, state); + bool isCFError = false; + if (!isNSError) + isCFError = hasFlag(loc, state); - QualType T = (*I)->getType(); + if (!(isNSError || isCFError)) + return; - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); - } -} + // Storing to possible null NSError/CFErrorRef out parameter. + + // Emit an error. + std::string err; + llvm::raw_string_ostream os(err); + os << "Potential null dereference. According to coding standards "; + + if (isNSError) + os << "in 'Creating and Returning NSError Objects' the parameter '"; + else + os << "documented in CoreFoundation/CFError.h the parameter '"; + os << "' may be null."; -bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { + BugType *bug = 0; + if (isNSError) + bug = new NSErrorDerefBug(); + else + bug = new CFErrorDerefBug(); + EnhancedBugReport *report = new EnhancedBugReport(*bug, os.str(), + event.SinkNode); + BR.EmitReport(report); +} + +static bool IsNSError(const ParmVarDecl *PD, IdentifierInfo *II) { - const PointerType* PPT = ArgTy->getAs(); + const PointerType* PPT = PD->getType()->getAs(); if (!PPT) return false; @@ -181,9 +288,8 @@ bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { return false; } -bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { - - const PointerType* PPT = ArgTy->getAs(); +static bool IsCFError(const ParmVarDecl *PD, IdentifierInfo *II) { + const PointerType* PPT = PD->getType()->getAs(); if (!PPT) return false; const TypedefType* TT = PPT->getPointeeType()->getAs(); @@ -192,47 +298,16 @@ bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { return TT->getDecl()->getIdentifier() == II; } -void NSErrorChecker::CheckParamDeref(const VarDecl *Param, - const LocationContext *LC, - const GRState *rootState, - BugReporter& BR) { - - SVal ParamL = rootState->getLValue(Param, LC); - const MemRegion* ParamR = cast(ParamL).getRegionAs(); - assert (ParamR && "Parameters always have VarRegions."); - SVal ParamSVal = rootState->getSVal(ParamR); - - // FIXME: For now assume that ParamSVal is symbolic. We need to generalize - // this later. - SymbolRef ParamSym = ParamSVal.getAsLocSymbol(); - if (!ParamSym) - return; +void ento::registerNSErrorChecker(CheckerManager &mgr) { + mgr.registerChecker(); + NSOrCFErrorDerefChecker * + checker = mgr.registerChecker(); + checker->ShouldCheckNSError = true; +} - // Iterate over the implicit-null dereferences. - ExplodedNode *const* I, *const* E; - llvm::tie(I, E) = GetImplicitNullDereferences(Eng); - for ( ; I != E; ++I) { - const GRState *state = (*I)->getState(); - SVal location = state->getSVal((*I)->getLocationAs()->getStmt()); - if (location.getAsSymbol() != ParamSym) - continue; - - // Emit an error. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Potential null dereference. According to coding standards "; - - if (isNSErrorWarning) - os << "in 'Creating and Returning NSError Objects' the parameter '"; - else - os << "documented in CoreFoundation/CFError.h the parameter '"; - - os << Param << "' may be null."; - - BugReport *report = new BugReport(*this, os.str(), *I); - // FIXME: Notable symbols are now part of the report. We should - // add support for notable symbols in BugReport. - // BR.addNotableSymbol(SV->getSymbol()); - BR.EmitReport(report); - } +void ento::registerCFErrorChecker(CheckerManager &mgr) { + mgr.registerChecker(); + NSOrCFErrorDerefChecker * + checker = mgr.registerChecker(); + checker->ShouldCheckCFError = true; } diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index cc5fb6a599..787d38d4c9 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -339,8 +339,6 @@ static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr, return; ExprEngine Eng(mgr, TF.take()); - RegisterNSErrorChecks(Eng.getBugReporter(), Eng, *D); - // Set the graph auditor. llvm::OwningPtr Auditor; if (mgr.shouldVisualizeUbigraph()) { diff --git a/test/Analysis/CheckNSError.m b/test/Analysis/CheckNSError.m index d12ad4eb2e..9265972209 100644 --- a/test/Analysis/CheckNSError.m +++ b/test/Analysis/CheckNSError.m @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=basic -verify %s -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -verify %s -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify %s -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cocoa.NSError,macosx.CFError -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=basic -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cocoa.NSError,macosx.CFError -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cocoa.NSError,macosx.CFError -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cocoa.NSError,macosx.CFError -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -verify %s typedef signed char BOOL; diff --git a/test/Analysis/bstring.c b/test/Analysis/bstring.c index ff73d745be..ec2cc86a68 100644 --- a/test/Analysis/bstring.c +++ b/test/Analysis/bstring.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s -// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,core.experimental.CString -analyzer-check-objc-mem -analyzer-store=region -verify %s //===----------------------------------------------------------------------=== // Declarations diff --git a/test/Analysis/complex.c b/test/Analysis/complex.c index c8bdce0153..a0980ec0d2 100644 --- a/test/Analysis/complex.c +++ b/test/Analysis/complex.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=basic -verify -Wno-unreachable-code -ffreestanding %s -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify -Wno-unreachable-code -ffreestanding %s -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -verify -Wno-unreachable-code -ffreestanding %s -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -verify -Wno-unreachable-code -ffreestanding %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=basic -verify -Wno-unreachable-code -ffreestanding %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify -Wno-unreachable-code -ffreestanding %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -verify -Wno-unreachable-code -ffreestanding %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -verify -Wno-unreachable-code -ffreestanding %s #include diff --git a/test/Analysis/constant-folding.c b/test/Analysis/constant-folding.c index 5eea2519c7..4181e8afa6 100644 --- a/test/Analysis/constant-folding.c +++ b/test/Analysis/constant-folding.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental.UnreachableCode -analyzer-check-objc-mem -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental.UnreachableCode -analyzer-check-objc-mem -verify %s // Trigger a warning if the analyzer reaches this point in the control flow. #define WARN ((void)*(char*)0) diff --git a/test/Analysis/dtor.cpp b/test/Analysis/dtor.cpp index ea5b04684d..f6b7138a70 100644 --- a/test/Analysis/dtor.cpp +++ b/test/Analysis/dtor.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store region -analyzer-inline-call -cfg-add-implicit-dtors -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store region -analyzer-inline-call -cfg-add-implicit-dtors -verify %s class A { public: diff --git a/test/Analysis/flat-store.c b/test/Analysis/flat-store.c index bb274b0d5d..fc739df5fc 100644 --- a/test/Analysis/flat-store.c +++ b/test/Analysis/flat-store.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=flat -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=flat -verify %s #define FAIL ((void)*(char*)0) struct simple { int x; }; diff --git a/test/Analysis/idempotent-operations-limited-loops.c b/test/Analysis/idempotent-operations-limited-loops.c index e4c34cdead..807280fa05 100644 --- a/test/Analysis/idempotent-operations-limited-loops.c +++ b/test/Analysis/idempotent-operations-limited-loops.c @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-checker=core.experimental.IdempotentOps -analyzer-max-loop 3 -verify %s -// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-checker=core.experimental.IdempotentOps -analyzer-max-loop 4 -verify %s -// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-checker=core.experimental.IdempotentOps %s -verify +// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-checker=core,core.experimental.IdempotentOps -analyzer-max-loop 3 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-checker=core,core.experimental.IdempotentOps -analyzer-max-loop 4 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-checker=core,core.experimental.IdempotentOps %s -verify void always_warning() { int *p = 0; *p = 0xDEADBEEF; } // expected-warning{{Dereference of null pointer (loaded from variable 'p')}} diff --git a/test/Analysis/inline.c b/test/Analysis/inline.c index d7a599a765..aee821bd54 100644 --- a/test/Analysis/inline.c +++ b/test/Analysis/inline.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-inline-call -analyzer-store region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-inline-call -analyzer-store region -verify %s int test1_f1() { int y = 1; diff --git a/test/Analysis/misc-ps-basic-store.m b/test/Analysis/misc-ps-basic-store.m index 55042c1425..cf235a99a8 100644 --- a/test/Analysis/misc-ps-basic-store.m +++ b/test/Analysis/misc-ps-basic-store.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-store=basic -verify -fblocks %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-store=basic -verify -fblocks %s //--------------------------------------------------------------------------- // Test case 'checkaccess_union' differs for region store and basic store. diff --git a/test/Analysis/misc-ps-region-store.cpp b/test/Analysis/misc-ps-region-store.cpp index 7c296fd3db..c6206bf19f 100644 --- a/test/Analysis/misc-ps-region-store.cpp +++ b/test/Analysis/misc-ps-region-store.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s -// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s +// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s // Test basic handling of references. char &test1_aux(); diff --git a/test/Analysis/override-werror.c b/test/Analysis/override-werror.c index ce0f1ac9ab..4b812295fd 100644 --- a/test/Analysis/override-werror.c +++ b/test/Analysis/override-werror.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -Werror %s -analyzer-store=basic -verify -// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -Werror %s -analyzer-store=region -verify +// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -Werror %s -analyzer-store=basic -verify +// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -Werror %s -analyzer-store=region -verify // This test case illustrates that using '-analyze' overrides the effect of // -Werror. This allows basic warnings not to interfere with producing diff --git a/test/Analysis/plist-output-alternate.m b/test/Analysis/plist-output-alternate.m index 7b8c2673b0..e22fd2d7f9 100644 --- a/test/Analysis/plist-output-alternate.m +++ b/test/Analysis/plist-output-alternate.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-output=plist -o - %s | FileCheck %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-output=plist -o - %s | FileCheck %s void test_null_init(void) { int *p = 0; diff --git a/test/Analysis/retain-release-region-store.m b/test/Analysis/retain-release-region-store.m index ec765e3fe8..4279bcfc55 100644 --- a/test/Analysis/retain-release-region-store.m +++ b/test/Analysis/retain-release-region-store.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-max-loop 6 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=region -analyzer-max-loop 6 -verify %s //===----------------------------------------------------------------------===// // The following code is reduced using delta-debugging from diff --git a/test/Analysis/string.c b/test/Analysis/string.c index 756115180c..967fe4d515 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s -// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,core.experimental.CString,core.experimental.UnreachableCode -analyzer-check-objc-mem -analyzer-store=region -verify %s //===----------------------------------------------------------------------=== // Declarations diff --git a/test/Analysis/unreachable-code-path.c b/test/Analysis/unreachable-code-path.c index 34763e63b9..322611aa13 100644 --- a/test/Analysis/unreachable-code-path.c +++ b/test/Analysis/unreachable-code-path.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=DeadStores,core.experimental.UnreachableCode -analyzer-check-objc-mem -verify -analyzer-opt-analyze-nested-blocks -Wno-unused-value %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,DeadStores,core.experimental.UnreachableCode -analyzer-check-objc-mem -verify -analyzer-opt-analyze-nested-blocks -Wno-unused-value %s extern void foo(int a); diff --git a/test/Coverage/html-diagnostics.c b/test/Coverage/html-diagnostics.c index be820fb90f..0b1580e886 100644 --- a/test/Coverage/html-diagnostics.c +++ b/test/Coverage/html-diagnostics.c @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -analyze -analyzer-output=html -analyzer-check-objc-mem -o %t %s +// RUN: %clang_cc1 -analyze -analyzer-output=html -analyzer-checker=core -analyzer-check-objc-mem -o %t %s // RUN: cat %t/*.html | FileCheck %s // CHECK:

Annotated Source Code