void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) return; if (isReceiverClassOrSuperclass(ID, "NSString")) { Selector S = msg.getSelector(); if (S.isUnarySelector()) return; // FIXME: This is going to be really slow doing these checks with // lexical comparisons. std::string NameStr = S.getAsString(); StringRef Name(NameStr); assert(!Name.empty()); // FIXME: Checking for initWithFormat: will not work in most cases // yet because [NSString alloc] returns id, not NSString*. We will // need support for tracking expected-type information in the analyzer // to find these errors. if (Name == "caseInsensitiveCompare:" || Name == "compare:" || Name == "compare:options:" || Name == "compare:options:range:" || Name == "compare:options:range:locale:" || Name == "componentsSeparatedByCharactersInSet:" || Name == "initWithFormat:") { if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState()))) WarnNilArg(C, msg, 0); } } }
/// isVariadicMessage - Returns whether the given message is a variadic message, /// where all arguments must be Objective-C types. bool VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { const ObjCMethodDecl *MD = msg.getMethodDecl(); if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) return false; Selector S = msg.getSelector(); if (msg.isInstanceMessage()) { // FIXME: Ideally we'd look at the receiver interface here, but that's not // useful for init, because alloc returns 'id'. In theory, this could lead // to false positives, for example if there existed a class that had an // initWithObjects: implementation that does accept non-Objective-C pointer // types, but the chance of that happening is pretty small compared to the // gains that this analysis gives. const ObjCInterfaceDecl *Class = MD->getClassInterface(); // -[NSArray initWithObjects:] if (isReceiverClassOrSuperclass(Class, "NSArray") && S == initWithObjectsS) return true; // -[NSDictionary initWithObjectsAndKeys:] if (isReceiverClassOrSuperclass(Class, "NSDictionary") && S == initWithObjectsAndKeysS) return true; // -[NSSet initWithObjects:] if (isReceiverClassOrSuperclass(Class, "NSSet") && S == initWithObjectsS) return true; } else { const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); // -[NSArray arrayWithObjects:] if (isReceiverClassOrSuperclass(Class, "NSArray") && S == arrayWithObjectsS) return true; // -[NSDictionary dictionaryWithObjectsAndKeys:] if (isReceiverClassOrSuperclass(Class, "NSDictionary") && S == dictionaryWithObjectsAndKeysS) return true; // -[NSSet setWithObjects:] if (isReceiverClassOrSuperclass(Class, "NSSet") && S == setWithObjectsS) return true; } return false; }
void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const { // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler // handleFailureInMethod:object:file:lineNumber:description:] // -[NSAssertionHandler // handleFailureInFunction:file:lineNumber:description:] // Eventually these should be annotated with __attribute__((noreturn)). // Because ObjC messages use dynamic dispatch, it is not generally safe to // assume certain methods can't return. In cases where it is definitely valid, // see if you can mark the methods noreturn or analyzer_noreturn instead of // adding more explicit checks to this method. if (!Msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); if (!Receiver) return; if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) return; Selector Sel = Msg.getSelector(); switch (Sel.getNumArgs()) { default: return; case 4: if (!isMultiArgSelector(&Sel, "handleFailureInFunction", "file", "lineNumber", "description", NULL)) return; break; case 5: if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file", "lineNumber", "description", NULL)) return; break; } // If we got here, it's one of the messages we care about. C.generateSink(); }
void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C, ObjCMessage msg) { if (!BT) { BT = new APIMisuse("message incorrectly sent to class instead of class " "instance"); ASTContext &Ctx = C.getASTContext(); releaseS = GetNullarySelector("release", Ctx); retainS = GetNullarySelector("retain", Ctx); autoreleaseS = GetNullarySelector("autorelease", Ctx); drainS = GetNullarySelector("drain", Ctx); } if (msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); assert(Class); Selector S = msg.getSelector(); if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) return; if (ExplodedNode *N = C.generateNode()) { llvm::SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The '" << S.getAsString() << "' message should be sent to instances " "of class '" << Class->getName() << "' and not the class directly"; RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); report->addRange(msg.getSourceRange()); C.EmitReport(report); } }