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.cpp93
1 files changed, 74 insertions, 19 deletions
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 6388a8df64..ba779ff191 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -90,20 +90,49 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
//===----------------------------------------------------------------------===//
namespace {
- class NilArgChecker : public Checker<check::PreObjCMessage> {
+ class NilArgChecker : public Checker<check::PreObjCMessage,
+ check::PostStmt<ObjCDictionaryLiteral>,
+ check::PostStmt<ObjCArrayLiteral> > {
mutable OwningPtr<APIMisuse> BT;
- void WarnIfNilArg(CheckerContext &C,
- const ObjCMethodCall &msg, unsigned Arg,
- FoundationClass Class,
- bool CanBeSubscript = false) const;
+ void warnIfNilExpr(const Expr *E,
+ const char *Msg,
+ CheckerContext &C) const;
+
+ void warnIfNilArg(CheckerContext &C,
+ const ObjCMethodCall &msg, unsigned Arg,
+ FoundationClass Class,
+ bool CanBeSubscript = false) const;
+
+ void generateBugReport(ExplodedNode *N,
+ StringRef Msg,
+ SourceRange Range,
+ const Expr *Expr,
+ CheckerContext &C) const;
public:
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPostStmt(const ObjCDictionaryLiteral *DL,
+ CheckerContext &C) const;
+ void checkPostStmt(const ObjCArrayLiteral *AL,
+ CheckerContext &C) const;
};
}
-void NilArgChecker::WarnIfNilArg(CheckerContext &C,
+void NilArgChecker::warnIfNilExpr(const Expr *E,
+ const char *Msg,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
+
+ if (ExplodedNode *N = C.generateSink()) {
+ generateBugReport(N, Msg, E->getSourceRange(), E, C);
+ }
+
+ }
+}
+
+void NilArgChecker::warnIfNilArg(CheckerContext &C,
const ObjCMethodCall &msg,
unsigned int Arg,
FoundationClass Class,
@@ -113,9 +142,6 @@ void NilArgChecker::WarnIfNilArg(CheckerContext &C,
if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
return;
- if (!BT)
- BT.reset(new APIMisuse("nil argument"));
-
if (ExplodedNode *N = C.generateSink()) {
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
@@ -149,14 +175,26 @@ void NilArgChecker::WarnIfNilArg(CheckerContext &C,
<< msg.getSelector().getAsString() << "' cannot be nil";
}
}
-
- BugReport *R = new BugReport(*BT, os.str(), N);
- R->addRange(msg.getArgSourceRange(Arg));
- bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R);
- C.emitReport(R);
+
+ generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
+ msg.getArgExpr(Arg), C);
}
}
+void NilArgChecker::generateBugReport(ExplodedNode *N,
+ StringRef Msg,
+ SourceRange Range,
+ const Expr *E,
+ CheckerContext &C) const {
+ if (!BT)
+ BT.reset(new APIMisuse("nil argument"));
+
+ BugReport *R = new BugReport(*BT, Msg, N);
+ R->addRange(Range);
+ bugreporter::trackNullOrUndefValue(N, E, *R);
+ C.emitReport(R);
+}
+
void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
@@ -225,28 +263,45 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
if (S.getNameForSlot(0).equals("dictionaryWithObject") &&
S.getNameForSlot(1).equals("forKey")) {
Arg = 0;
- WarnIfNilArg(C, msg, /* Arg */1, Class);
+ warnIfNilArg(C, msg, /* Arg */1, Class);
} else if (S.getNameForSlot(0).equals("setObject") &&
S.getNameForSlot(1).equals("forKey")) {
Arg = 0;
- WarnIfNilArg(C, msg, /* Arg */1, Class);
+ warnIfNilArg(C, msg, /* Arg */1, Class);
} else if (S.getNameForSlot(0).equals("setObject") &&
S.getNameForSlot(1).equals("forKeyedSubscript")) {
CanBeSubscript = true;
Arg = 0;
- WarnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript);
+ warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript);
} else if (S.getNameForSlot(0).equals("removeObjectForKey")) {
Arg = 0;
}
}
-
// If argument is '0', report a warning.
if ((Arg != InvalidArgIndex))
- WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
+ warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
}
+void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
+ CheckerContext &C) const {
+ unsigned NumOfElements = AL->getNumElements();
+ for (unsigned i = 0; i < NumOfElements; ++i) {
+ warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
+ }
+}
+
+void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
+ CheckerContext &C) const {
+ unsigned NumOfElements = DL->getNumElements();
+ for (unsigned i = 0; i < NumOfElements; ++i) {
+ ObjCDictionaryElement Element = DL->getKeyValueElement(i);
+ warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
+ warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
+ }
+}
+
//===----------------------------------------------------------------------===//
// Error reporting.
//===----------------------------------------------------------------------===//