[analyzer] Warn about nil elements/keys/values in array and dictionary literals.

llvm-svn: 181738
This commit is contained in:
Anna Zaks 2013-05-13 21:48:20 +00:00
parent cdb5b61a2d
commit bb2a2c865f
2 changed files with 109 additions and 19 deletions

View File

@ -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.
//===----------------------------------------------------------------------===//

View File

@ -36,6 +36,10 @@ typedef struct _NSZone NSZone;
- (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;
@ -58,6 +62,8 @@ typedef struct _NSZone NSZone;
+ (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
@ -147,6 +153,33 @@ NSDictionary *testNilArgNSDictionary2(NSObject *obj) {
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)