зеркало из https://github.com/microsoft/clang-1.git
[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:
Родитель
683d25656f
Коммит
b095782ec0
|
@ -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}}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче