diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 95 |
1 files changed, 76 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. //===----------------------------------------------------------------------===// |