diff options
author | Anna Zaks <ganna@apple.com> | 2013-05-13 21:48:20 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2013-05-13 21:48:20 +0000 |
commit | b834a78f9b79cb71b093ebbbb381b92f9d4bbf3b (patch) | |
tree | b4b8409a2bce8c2cfcd94998ac201cb9f9c0a587 | |
parent | 27c7f54cf7d18276be2979d5c795533cc5592675 (diff) | |
download | clang-b834a78f9b79cb71b093ebbbb381b92f9d4bbf3b.tar.gz clang-b834a78f9b79cb71b093ebbbb381b92f9d4bbf3b.tar.bz2 clang-b834a78f9b79cb71b093ebbbb381b92f9d4bbf3b.tar.xz |
[analyzer] Warn about nil elements/keys/values in array and dictionary literals.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181738 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 95 | ||||
-rw-r--r-- | test/Analysis/NSContainers.m | 33 |
2 files changed, 109 insertions, 19 deletions
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 6388a8df64..c723e4f29b 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -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. //===----------------------------------------------------------------------===// diff --git a/test/Analysis/NSContainers.m b/test/Analysis/NSContainers.m index 6b4089b3e5..959f367d28 100644 --- a/test/Analysis/NSContainers.m +++ b/test/Analysis/NSContainers.m @@ -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) |