diff options
author | Anna Zaks <ganna@apple.com> | 2013-03-23 00:39:21 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2013-03-23 00:39:21 +0000 |
commit | b095782ec09329b474a4e0d0ccdad4c15d515b39 (patch) | |
tree | 43089eae43d662fbb495d2306dd31820906510c7 /lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | |
parent | 683d25656f28937f78c815f70545139c432f1ff3 (diff) |
[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
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 78 |
1 files changed, 62 insertions, 16 deletions
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index a6bcb6930a..fb43964a89 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -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); } |