[analyzer] Warn when a nil key or value are passed to NSMutableDictionary and ensure it works with subscripting.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177789 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Anna Zaks 2013-03-23 00:39:21 +00:00
Родитель 683d25656f
Коммит b095782ec0
2 изменённых файлов: 136 добавлений и 22 удалений

Просмотреть файл

@ -83,10 +83,6 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
return result;
}
static inline bool isNil(SVal X) {
return X.getAs<loc::ConcreteInt>().hasValue();
}
//===----------------------------------------------------------------------===//
// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
//===----------------------------------------------------------------------===//
@ -95,26 +91,51 @@ namespace {
class NilArgChecker : public Checker<check::PreObjCMessage> {
mutable OwningPtr<APIMisuse> BT;
void WarnNilArg(CheckerContext &C,
const ObjCMethodCall &msg, unsigned Arg) const;
void WarnIfNilArg(CheckerContext &C,
const ObjCMethodCall &msg, unsigned Arg,
FoundationClass Class,
bool CanBeSubscript = false) const;
public:
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
};
}
void NilArgChecker::WarnNilArg(CheckerContext &C,
const ObjCMethodCall &msg,
unsigned int Arg) const
{
void NilArgChecker::WarnIfNilArg(CheckerContext &C,
const ObjCMethodCall &msg,
unsigned int Arg,
FoundationClass Class,
bool CanBeSubscript) const {
// Check if the argument is nil.
ProgramStateRef State = C.getState();
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);
os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
<< msg.getSelector().getAsString() << "' cannot be nil";
if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
if (Class == FC_NSArray) {
os << "Array element cannot be nil";
} else if (Class == FC_NSDictionary) {
if (Arg == 0)
os << "Dictionary object cannot be nil";
else {
assert(Arg == 1);
os << "Dictionary key cannot be nil";
}
} else
llvm_unreachable("Missing foundation class for the subscript expr");
} else {
os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
<< msg.getSelector().getAsString() << "' cannot be nil";
}
BugReport *R = new BugReport(*BT, os.str(), N);
R->addRange(msg.getArgSourceRange(Arg));
@ -132,7 +153,8 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
static const unsigned InvalidArgIndex = UINT_MAX;
unsigned Arg = InvalidArgIndex;
bool CanBeSubscript = false;
if (Class == FC_NSString) {
Selector S = msg.getSelector();
@ -176,14 +198,38 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
} else if (S.getNameForSlot(0).equals("setObject") &&
S.getNameForSlot(1).equals("atIndexedSubscript")) {
Arg = 0;
CanBeSubscript = true;
} else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
Arg = 0;
}
} else if (Class == FC_NSDictionary) {
Selector S = msg.getSelector();
if (S.isUnarySelector())
return;
if (S.getNameForSlot(0).equals("dictionaryWithObject") &&
S.getNameForSlot(1).equals("forKey")) {
Arg = 0;
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);
} else if (S.getNameForSlot(0).equals("setObject") &&
S.getNameForSlot(1).equals("forKeyedSubscript")) {
CanBeSubscript = true;
Arg = 0;
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) && isNil(msg.getArgSVal(Arg)))
WarnNilArg(C, msg, Arg);
if ((Arg != InvalidArgIndex))
WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
}

Просмотреть файл

@ -24,7 +24,6 @@ typedef struct _NSZone NSZone;
- (id)init;
+ (id)alloc;
@end
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
- (NSUInteger)count;
@ -47,30 +46,99 @@ typedef struct _NSZone NSZone;
@end
@interface NSDictionary : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
- (NSUInteger)count;
- (id)objectForKey:(id)aKey;
- (NSEnumerator *)keyEnumerator;
@end
@interface NSDictionary (NSDictionaryCreation)
+ (id)dictionary;
+ (id)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;
@end
@interface NSMutableDictionary : NSDictionary
- (void)removeObjectForKey:(id)aKey;
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
@end
@interface NSMutableDictionary (NSExtendedMutableDictionary)
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary;
- (void)removeAllObjects;
- (void)removeObjectsForKeys:(NSArray *)keyArray;
- (void)setDictionary:(NSDictionary *)otherDictionary;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key __attribute__((availability(macosx,introduced=10.8)));
@end
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
@end
// NSMutableArray API
void testNilArg1() {
void testNilArgNSMutableArray1() {
NSMutableArray *marray = [[NSMutableArray alloc] init];
[marray addObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'addObject:' cannot be nil}}
}
void testNilArg2() {
void testNilArgNSMutableArray2() {
NSMutableArray *marray = [[NSMutableArray alloc] init];
[marray insertObject:0 atIndex:1]; // expected-warning {{Argument to 'NSMutableArray' method 'insertObject:atIndex:' cannot be nil}}
}
void testNilArg3() {
void testNilArgNSMutableArray3() {
NSMutableArray *marray = [[NSMutableArray alloc] init];
[marray replaceObjectAtIndex:1 withObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'replaceObjectAtIndex:withObject:' cannot be nil}}
}
void testNilArg4() {
void testNilArgNSMutableArray4() {
NSMutableArray *marray = [[NSMutableArray alloc] init];
[marray setObject:0 atIndexedSubscript:1]; // expected-warning {{Argument to 'NSMutableArray' method 'setObject:atIndexedSubscript:' cannot be nil}}
}
void testNilArgNSMutableArray5() {
NSMutableArray *marray = [[NSMutableArray alloc] init];
marray[1] = 0; // expected-warning {{Array element cannot be nil}}
}
// NSArray API
void testNilArg5() {
void testNilArgNSArray1() {
NSArray *array = [[NSArray alloc] init];
NSArray *copyArray = [array arrayByAddingObject:0]; // expected-warning {{Argument to 'NSArray' method 'arrayByAddingObject:' cannot be nil}}
}
// NSMutableDictionary and NSDictionary APIs.
void testNilArgNSMutableDictionary1(NSMutableDictionary *d, NSString* key) {
[d setObject:0 forKey:key]; // expected-warning {{Argument to 'NSMutableDictionary' method 'setObject:forKey:' cannot be nil}}
}
void testNilArgNSMutableDictionary2(NSMutableDictionary *d, NSObject *obj) {
[d setObject:obj forKey:0]; // expected-warning {{Argument to 'NSMutableDictionary' method 'setObject:forKey:' cannot be nil}}
}
void testNilArgNSMutableDictionary3(NSMutableDictionary *d) {
[d removeObjectForKey:0]; // expected-warning {{Argument to 'NSMutableDictionary' method 'removeObjectForKey:' cannot be nil}}
}
void testNilArgNSMutableDictionary5(NSMutableDictionary *d, NSString* key) {
d[key] = 0; // expected-warning {{Dictionary object cannot be nil}}
}
void testNilArgNSMutableDictionary6(NSMutableDictionary *d, NSString *key) {
if (key)
;
d[key] = 0; // expected-warning {{Dictionary key cannot be nil}}
// expected-warning@-1 {{Dictionary object cannot be nil}}
}
NSDictionary *testNilArgNSDictionary1(NSString* key) {
return [NSDictionary dictionaryWithObject:0 forKey:key]; // expected-warning {{Argument to 'NSDictionary' method 'dictionaryWithObject:forKey:' cannot be nil}}
}
NSDictionary *testNilArgNSDictionary2(NSObject *obj) {
return [NSDictionary dictionaryWithObject:obj forKey:0]; // expected-warning {{Argument to 'NSDictionary' method 'dictionaryWithObject:forKey:' cannot be nil}}
}