зеркало из 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isNil(SVal X) {
|
|
||||||
return X.getAs<loc::ConcreteInt>().hasValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
|
// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -95,26 +91,51 @@ namespace {
|
||||||
class NilArgChecker : public Checker<check::PreObjCMessage> {
|
class NilArgChecker : public Checker<check::PreObjCMessage> {
|
||||||
mutable OwningPtr<APIMisuse> BT;
|
mutable OwningPtr<APIMisuse> BT;
|
||||||
|
|
||||||
void WarnNilArg(CheckerContext &C,
|
void WarnIfNilArg(CheckerContext &C,
|
||||||
const ObjCMethodCall &msg, unsigned Arg) const;
|
const ObjCMethodCall &msg, unsigned Arg,
|
||||||
|
FoundationClass Class,
|
||||||
|
bool CanBeSubscript = false) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
|
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void NilArgChecker::WarnNilArg(CheckerContext &C,
|
void NilArgChecker::WarnIfNilArg(CheckerContext &C,
|
||||||
const ObjCMethodCall &msg,
|
const ObjCMethodCall &msg,
|
||||||
unsigned int Arg) const
|
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)
|
if (!BT)
|
||||||
BT.reset(new APIMisuse("nil argument"));
|
BT.reset(new APIMisuse("nil argument"));
|
||||||
|
|
||||||
if (ExplodedNode *N = C.generateSink()) {
|
if (ExplodedNode *N = C.generateSink()) {
|
||||||
SmallString<128> sbuf;
|
SmallString<128> sbuf;
|
||||||
llvm::raw_svector_ostream os(sbuf);
|
llvm::raw_svector_ostream os(sbuf);
|
||||||
|
|
||||||
|
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 '"
|
os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
|
||||||
<< msg.getSelector().getAsString() << "' cannot be nil";
|
<< msg.getSelector().getAsString() << "' cannot be nil";
|
||||||
|
}
|
||||||
|
|
||||||
BugReport *R = new BugReport(*BT, os.str(), N);
|
BugReport *R = new BugReport(*BT, os.str(), N);
|
||||||
R->addRange(msg.getArgSourceRange(Arg));
|
R->addRange(msg.getArgSourceRange(Arg));
|
||||||
|
@ -132,6 +153,7 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
|
|
||||||
static const unsigned InvalidArgIndex = UINT_MAX;
|
static const unsigned InvalidArgIndex = UINT_MAX;
|
||||||
unsigned Arg = InvalidArgIndex;
|
unsigned Arg = InvalidArgIndex;
|
||||||
|
bool CanBeSubscript = false;
|
||||||
|
|
||||||
if (Class == FC_NSString) {
|
if (Class == FC_NSString) {
|
||||||
Selector S = msg.getSelector();
|
Selector S = msg.getSelector();
|
||||||
|
@ -176,14 +198,38 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
} else if (S.getNameForSlot(0).equals("setObject") &&
|
} else if (S.getNameForSlot(0).equals("setObject") &&
|
||||||
S.getNameForSlot(1).equals("atIndexedSubscript")) {
|
S.getNameForSlot(1).equals("atIndexedSubscript")) {
|
||||||
Arg = 0;
|
Arg = 0;
|
||||||
|
CanBeSubscript = true;
|
||||||
} else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
|
} else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
|
||||||
Arg = 0;
|
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 argument is '0', report a warning.
|
||||||
if ((Arg != InvalidArgIndex) && isNil(msg.getArgSVal(Arg)))
|
if ((Arg != InvalidArgIndex))
|
||||||
WarnNilArg(C, msg, Arg);
|
WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ typedef struct _NSZone NSZone;
|
||||||
- (id)init;
|
- (id)init;
|
||||||
+ (id)alloc;
|
+ (id)alloc;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
|
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
|
||||||
|
|
||||||
- (NSUInteger)count;
|
- (NSUInteger)count;
|
||||||
|
@ -47,30 +46,99 @@ typedef struct _NSZone NSZone;
|
||||||
|
|
||||||
@end
|
@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
|
// NSMutableArray API
|
||||||
void testNilArg1() {
|
void testNilArgNSMutableArray1() {
|
||||||
NSMutableArray *marray = [[NSMutableArray alloc] init];
|
NSMutableArray *marray = [[NSMutableArray alloc] init];
|
||||||
[marray addObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'addObject:' cannot be nil}}
|
[marray addObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'addObject:' cannot be nil}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testNilArg2() {
|
void testNilArgNSMutableArray2() {
|
||||||
NSMutableArray *marray = [[NSMutableArray alloc] init];
|
NSMutableArray *marray = [[NSMutableArray alloc] init];
|
||||||
[marray insertObject:0 atIndex:1]; // expected-warning {{Argument to 'NSMutableArray' method 'insertObject:atIndex:' cannot be nil}}
|
[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];
|
NSMutableArray *marray = [[NSMutableArray alloc] init];
|
||||||
[marray replaceObjectAtIndex:1 withObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'replaceObjectAtIndex:withObject:' cannot be nil}}
|
[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];
|
NSMutableArray *marray = [[NSMutableArray alloc] init];
|
||||||
[marray setObject:0 atIndexedSubscript:1]; // expected-warning {{Argument to 'NSMutableArray' method 'setObject:atIndexedSubscript:' cannot be nil}}
|
[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
|
// NSArray API
|
||||||
void testNilArg5() {
|
void testNilArgNSArray1() {
|
||||||
NSArray *array = [[NSArray alloc] init];
|
NSArray *array = [[NSArray alloc] init];
|
||||||
NSArray *copyArray = [array arrayByAddingObject:0]; // expected-warning {{Argument to 'NSArray' method 'arrayByAddingObject:' cannot be nil}}
|
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}}
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче