|
@@ -90,20 +90,53 @@ 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,
|
|
|
+ llvm::raw_svector_ostream &os,
|
|
|
+ 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();
|
|
|
+ SVal SV = State->getSVal(E, C.getLocationContext());
|
|
|
+ if (State->isNull(SV).isConstrainedTrue()) {
|
|
|
+
|
|
|
+ if (ExplodedNode *N = C.generateSink()) {
|
|
|
+ SmallString<128> sbuf;
|
|
|
+ llvm::raw_svector_ostream os(sbuf);
|
|
|
+ os << Msg;
|
|
|
+ generateBugReport(N, os, E->getSourceRange(), E, C);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void NilArgChecker::warnIfNilArg(CheckerContext &C,
|
|
|
const ObjCMethodCall &msg,
|
|
|
unsigned int Arg,
|
|
|
FoundationClass Class,
|
|
@@ -113,9 +146,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 +179,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, msg.getArgSourceRange(Arg),
|
|
|
+ msg.getArgExpr(Arg), C);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void NilArgChecker::generateBugReport(ExplodedNode *N,
|
|
|
+ llvm::raw_svector_ostream &os,
|
|
|
+ SourceRange Range,
|
|
|
+ const Expr *Expr,
|
|
|
+ CheckerContext &C) const {
|
|
|
+ if (!BT)
|
|
|
+ BT.reset(new APIMisuse("nil argument"));
|
|
|
+
|
|
|
+ BugReport *R = new BugReport(*BT, os.str(), N);
|
|
|
+ R->addRange(Range);
|
|
|
+ bugreporter::trackNullOrUndefValue(N, Expr, *R);
|
|
|
+ C.emitReport(R);
|
|
|
+}
|
|
|
+
|
|
|
void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
|
|
CheckerContext &C) const {
|
|
|
const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
|
|
@@ -225,28 +267,43 @@ 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 {
|
|
|
+ for (unsigned i = 0; i < AL->getNumElements(); ++i) {
|
|
|
+ warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
|
|
|
+ CheckerContext &C) const {
|
|
|
+ for (unsigned i = 0; i < DL->getNumElements(); ++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.
|
|
|
//===----------------------------------------------------------------------===//
|