diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 102 |
1 files changed, 37 insertions, 65 deletions
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 7d6358acbbace..577b5349f62ec 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -13,14 +13,14 @@ // //===----------------------------------------------------------------------===// -#include "ClangSACheckers.h" -#include "SelectorExtras.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/StmtObjC.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Analysis/SelectorExtras.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -36,6 +36,7 @@ using namespace clang; using namespace ento; +using namespace llvm; namespace { class APIMisuse : public BugType { @@ -156,6 +157,11 @@ void NilArgChecker::warnIfNilArg(CheckerContext &C, if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; + // NOTE: We cannot throw non-fatal errors from warnIfNilExpr, + // because it's called multiple times from some callers, so it'd cause + // an unwanted state split if two or more non-fatal errors are thrown + // within the same checker callback. For now we don't want to, but + // it'll need to be fixed if we ever want to. if (ExplodedNode *N = C.generateErrorNode()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -208,7 +214,7 @@ void NilArgChecker::generateBugReport(ExplodedNode *N, auto R = llvm::make_unique<BugReport>(*BT, Msg, N); R->addRange(Range); - bugreporter::trackNullOrUndefValue(N, E, *R); + bugreporter::trackExpressionValue(N, E, *R); C.emitReport(std::move(R)); } @@ -526,93 +532,59 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, //===----------------------------------------------------------------------===// namespace { -class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { - mutable std::unique_ptr<APIMisuse> BT; - mutable IdentifierInfo *Retain, *Release, *MakeCollectable, *Autorelease; +class CFRetainReleaseChecker : public Checker<check::PreCall> { + mutable APIMisuse BT{this, "null passed to CF memory management function"}; + CallDescription CFRetain{"CFRetain", 1}, + CFRelease{"CFRelease", 1}, + CFMakeCollectable{"CFMakeCollectable", 1}, + CFAutorelease{"CFAutorelease", 1}; public: - CFRetainReleaseChecker() - : Retain(nullptr), Release(nullptr), MakeCollectable(nullptr), - Autorelease(nullptr) {} - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; }; } // end anonymous namespace -void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, +void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - // If the CallExpr doesn't have exactly 1 argument just give up checking. - if (CE->getNumArgs() != 1) + // TODO: Make this check part of CallDescription. + if (!Call.isGlobalCFunction()) return; - ProgramStateRef state = C.getState(); - const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD) - return; - - if (!BT) { - ASTContext &Ctx = C.getASTContext(); - Retain = &Ctx.Idents.get("CFRetain"); - Release = &Ctx.Idents.get("CFRelease"); - MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); - Autorelease = &Ctx.Idents.get("CFAutorelease"); - BT.reset(new APIMisuse( - this, "null passed to CF memory management function")); - } - // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. - const IdentifierInfo *FuncII = FD->getIdentifier(); - if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable || - FuncII == Autorelease)) + if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) || + Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease))) return; - // FIXME: The rest of this just checks that the argument is non-null. - // It should probably be refactored and combined with NonNullParamChecker. - // Get the argument's value. - const Expr *Arg = CE->getArg(0); - SVal ArgVal = C.getSVal(Arg); + SVal ArgVal = Call.getArgSVal(0); Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; - // Get a NULL value. - SValBuilder &svalBuilder = C.getSValBuilder(); - DefinedSVal zero = - svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); - - // Make an expression asserting that they're equal. - DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); - - // Are they equal? - ProgramStateRef stateTrue, stateFalse; - std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); + // Is it null? + ProgramStateRef state = C.getState(); + ProgramStateRef stateNonNull, stateNull; + std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal); - if (stateTrue && !stateFalse) { - ExplodedNode *N = C.generateErrorNode(stateTrue); + if (!stateNonNull) { + ExplodedNode *N = C.generateErrorNode(stateNull); if (!N) return; - const char *description; - if (FuncII == Retain) - description = "Null pointer argument in call to CFRetain"; - else if (FuncII == Release) - description = "Null pointer argument in call to CFRelease"; - else if (FuncII == MakeCollectable) - description = "Null pointer argument in call to CFMakeCollectable"; - else if (FuncII == Autorelease) - description = "Null pointer argument in call to CFAutorelease"; - else - llvm_unreachable("impossible case"); + SmallString<64> Str; + raw_svector_ostream OS(Str); + OS << "Null pointer argument in call to " + << cast<FunctionDecl>(Call.getDecl())->getName(); - auto report = llvm::make_unique<BugReport>(*BT, description, N); - report->addRange(Arg->getSourceRange()); - bugreporter::trackNullOrUndefValue(N, Arg, *report); + auto report = llvm::make_unique<BugReport>(BT, OS.str(), N); + report->addRange(Call.getArgSourceRange(0)); + bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report); C.emitReport(std::move(report)); return; } // From here on, we know the argument is non-null. - C.addTransition(stateFalse); + C.addTransition(stateNonNull); } //===----------------------------------------------------------------------===// @@ -828,7 +800,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, //===----------------------------------------------------------------------===// // The map from container symbol to the container count symbol. -// We currently will remember the last countainer count symbol encountered. +// We currently will remember the last container count symbol encountered. REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool) |