//===----------------------------------------------------------------------===//
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,
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);
<< 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();
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.
//===----------------------------------------------------------------------===//
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx __attribute__((availability(macosx,introduced=10.8)));
@end
+@interface NSArray (NSArrayCreation)
++ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
+@end
+
@interface NSMutableArray : NSArray
- (void)addObject:(id)anObject;
+ (id)dictionary;
+ (id)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;
++ (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt;
+
@end
@interface NSMutableDictionary : NSDictionary
return [NSDictionary dictionaryWithObject:obj forKey:0]; // expected-warning {{Key argument to 'dictionaryWithObject:forKey:' cannot be nil}}
}
+id testCreateDictionaryLiteralKey(id value, id nilKey) {
+ if (nilKey)
+ ;
+ return @{@"abc":value, nilKey:@"abc"}; // expected-warning {{Dictionary key cannot be nil}}
+}
+
+id testCreateDictionaryLiteralValue(id nilValue) {
+ if (nilValue)
+ ;
+ return @{@"abc":nilValue}; // expected-warning {{Dictionary value cannot be nil}}
+}
+
+id testCreateDictionaryLiteral(id nilValue, id nilKey) {
+ if (nilValue)
+ ;
+ if (nilKey)
+ ;
+ return @{@"abc":nilValue, nilKey:@"abc"}; // expected-warning {{Dictionary key cannot be nil}}
+ // expected-warning@-1 {{Dictionary value cannot be nil}}
+}
+
+id testCreateArrayLiteral(id myNil) {
+ if (myNil)
+ ;
+ return @[ @"a", myNil, @"c" ]; // expected-warning {{Array element cannot be nil}}
+}
+
// Test inline defensive checks suppression.
void idc(id x) {
if (x)