summaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp102
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)