void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) return; if (findKnownClass(ID) == FC_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))) WarnNilArg(C, msg, 0); } } }
bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME) { Selector S = ME->getSelector(); if (S.isUnarySelector()) return false; // FIXME: This is going to be really slow doing these checks with // lexical comparisons. std::string NameStr = S.getAsString(); llvm::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:") return CheckNilArg(N, 0); return false; }
void ODRHash::AddDeclarationName(DeclarationName Name) { // Index all DeclarationName and use index numbers to refer to them. auto Result = DeclNameMap.insert(std::make_pair(Name, DeclNameMap.size())); ID.AddInteger(Result.first->second); if (!Result.second) { // If found in map, the the DeclarationName has previously been processed. return; } // First time processing each DeclarationName, also process its details. AddBoolean(Name.isEmpty()); if (Name.isEmpty()) return; auto Kind = Name.getNameKind(); ID.AddInteger(Kind); switch (Kind) { case DeclarationName::Identifier: AddIdentifierInfo(Name.getAsIdentifierInfo()); break; case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector S = Name.getObjCSelector(); AddBoolean(S.isNull()); AddBoolean(S.isKeywordSelector()); AddBoolean(S.isUnarySelector()); unsigned NumArgs = S.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { AddIdentifierInfo(S.getIdentifierInfoForSlot(i)); } break; } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXOperatorName: ID.AddInteger(Name.getCXXOverloadedOperator()); break; case DeclarationName::CXXLiteralOperatorName: AddIdentifierInfo(Name.getCXXLiteralIdentifier()); break; case DeclarationName::CXXConversionFunctionName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXUsingDirective: break; case DeclarationName::CXXDeductionGuideName: { auto *Template = Name.getCXXDeductionGuideTemplate(); AddBoolean(Template); if (Template) { AddDecl(Template); } } } }
bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, CheckerContext &C) const { Selector S = M.getSelector(); // Initialize the identifiers on first use. if (!CountSelectorII) CountSelectorII = &C.getASTContext().Idents.get("count"); // If the method returns collection count, record the value. return S.isUnarySelector() && (S.getIdentifierInfoForSlot(0) == CountSelectorII); }
void ODRHash::AddDeclarationName(DeclarationName Name) { AddBoolean(Name.isEmpty()); if (Name.isEmpty()) return; auto Kind = Name.getNameKind(); ID.AddInteger(Kind); switch (Kind) { case DeclarationName::Identifier: AddIdentifierInfo(Name.getAsIdentifierInfo()); break; case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector S = Name.getObjCSelector(); AddBoolean(S.isNull()); AddBoolean(S.isKeywordSelector()); AddBoolean(S.isUnarySelector()); unsigned NumArgs = S.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { AddIdentifierInfo(S.getIdentifierInfoForSlot(i)); } break; } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXOperatorName: ID.AddInteger(Name.getCXXOverloadedOperator()); break; case DeclarationName::CXXLiteralOperatorName: AddIdentifierInfo(Name.getCXXLiteralIdentifier()); break; case DeclarationName::CXXConversionFunctionName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXUsingDirective: break; case DeclarationName::CXXDeductionGuideName: { auto *Template = Name.getCXXDeductionGuideTemplate(); AddBoolean(Template); if (Template) { AddDecl(Template); } } } }
ObjCMethodFamily Selector::getMethodFamilyImpl(Selector sel) { IdentifierInfo *first = sel.getIdentifierInfoForSlot(0); if (!first) return OMF_None; StringRef name = first->getName(); if (sel.isUnarySelector()) { if (name == "autorelease") return OMF_autorelease; if (name == "dealloc") return OMF_dealloc; if (name == "finalize") return OMF_finalize; if (name == "release") return OMF_release; if (name == "retain") return OMF_retain; if (name == "retainCount") return OMF_retainCount; if (name == "self") return OMF_self; if (name == "initialize") return OMF_initialize; } if (name == "performSelector" || name == "performSelectorInBackground" || name == "performSelectorOnMainThread") return OMF_performSelector; // The other method families may begin with a prefix of underscores. while (!name.empty() && name.front() == '_') name = name.substr(1); if (name.empty()) return OMF_None; switch (name.front()) { case 'a': if (startsWithWord(name, "alloc")) return OMF_alloc; break; case 'c': if (startsWithWord(name, "copy")) return OMF_copy; break; case 'i': if (startsWithWord(name, "init")) return OMF_init; break; case 'm': if (startsWithWord(name, "mutableCopy")) return OMF_mutableCopy; break; case 'n': if (startsWithWord(name, "new")) return OMF_new; break; default: break; } return OMF_None; }
/// \brief Get the ASTContext-specific selector. Selector GlobalSelector::getSelector(ASTContext &AST) const { if (isInvalid()) return Selector(); Selector GlobSel = Selector(reinterpret_cast<uintptr_t>(Val)); llvm::SmallVector<IdentifierInfo *, 8> Ids; for (unsigned i = 0, e = GlobSel.isUnarySelector() ? 1 : GlobSel.getNumArgs(); i != e; ++i) { IdentifierInfo *GlobII = GlobSel.getIdentifierInfoForSlot(i); IdentifierInfo *II = &AST.Idents.get(GlobII->getName(), GlobII->getName() + GlobII->getLength()); Ids.push_back(II); } return AST.Selectors.getSelector(GlobSel.getNumArgs(), Ids.data()); }
/// \brief Get a GlobalSelector for the ASTContext-specific selector. GlobalSelector GlobalSelector::get(Selector Sel, Program &Prog) { if (Sel.isNull()) return GlobalSelector(); ProgramImpl &ProgImpl = *static_cast<ProgramImpl*>(Prog.Impl); llvm::SmallVector<IdentifierInfo *, 8> Ids; for (unsigned i = 0, e = Sel.isUnarySelector() ? 1 : Sel.getNumArgs(); i != e; ++i) { IdentifierInfo *II = Sel.getIdentifierInfoForSlot(i); IdentifierInfo *GlobII = &ProgImpl.getIdents().get(II->getName(), II->getName() + II->getLength()); Ids.push_back(GlobII); } Selector GlobSel = ProgImpl.getSelectors().getSelector(Sel.getNumArgs(), Ids.data()); return GlobalSelector(GlobSel.getAsOpaquePtr()); }
void NilArgChecker::preVisitObjCMessage(CheckerContext &C, ObjCMessage msg) { const ObjCInterfaceType *ReceiverType = GetReceiverType(msg); if (!ReceiverType) return; if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) { 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(); llvm::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.getState()))) WarnNilArg(C, msg, 0); } } }
void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) return; FoundationClass Class = findKnownClass(ID); static const unsigned InvalidArgIndex = UINT_MAX; unsigned Arg = InvalidArgIndex; bool CanBeSubscript = false; if (Class == FC_NSString) { Selector S = msg.getSelector(); if (S.isUnarySelector()) return; if (StringSelectors.empty()) { ASTContext &Ctx = C.getASTContext(); Selector Sels[] = { getKeywordSelector(Ctx, "caseInsensitiveCompare", nullptr), getKeywordSelector(Ctx, "compare", nullptr), getKeywordSelector(Ctx, "compare", "options", nullptr), getKeywordSelector(Ctx, "compare", "options", "range", nullptr), getKeywordSelector(Ctx, "compare", "options", "range", "locale", nullptr), getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet", nullptr), getKeywordSelector(Ctx, "initWithFormat", nullptr), getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare", nullptr), getKeywordSelector(Ctx, "localizedCompare", nullptr), getKeywordSelector(Ctx, "localizedStandardCompare", nullptr), }; for (Selector KnownSel : Sels) StringSelectors[KnownSel] = 0; } auto I = StringSelectors.find(S); if (I == StringSelectors.end()) return; Arg = I->second; } else if (Class == FC_NSArray) { Selector S = msg.getSelector(); if (S.isUnarySelector()) return; if (ArrayWithObjectSel.isNull()) { ASTContext &Ctx = C.getASTContext(); ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject", nullptr); AddObjectSel = getKeywordSelector(Ctx, "addObject", nullptr); InsertObjectAtIndexSel = getKeywordSelector(Ctx, "insertObject", "atIndex", nullptr); ReplaceObjectAtIndexWithObjectSel = getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject", nullptr); SetObjectAtIndexedSubscriptSel = getKeywordSelector(Ctx, "setObject", "atIndexedSubscript", nullptr); ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject", nullptr); } if (S == ArrayWithObjectSel || S == AddObjectSel || S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) { Arg = 0; } else if (S == SetObjectAtIndexedSubscriptSel) { Arg = 0; CanBeSubscript = true; } else if (S == ReplaceObjectAtIndexWithObjectSel) { Arg = 1; } } else if (Class == FC_NSDictionary) { Selector S = msg.getSelector(); if (S.isUnarySelector()) return; if (DictionaryWithObjectForKeySel.isNull()) { ASTContext &Ctx = C.getASTContext(); DictionaryWithObjectForKeySel = getKeywordSelector(Ctx, "dictionaryWithObject", "forKey", nullptr); SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey", nullptr); SetObjectForKeyedSubscriptSel = getKeywordSelector(Ctx, "setObject", "forKeyedSubscript", nullptr); RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey", nullptr); } if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) { Arg = 0; warnIfNilArg(C, msg, /* Arg */1, Class); } else if (S == SetObjectForKeyedSubscriptSel) { CanBeSubscript = true; Arg = 0; warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript); } else if (S == RemoveObjectForKeySel) { Arg = 0; } } // If argument is '0', report a warning. if ((Arg != InvalidArgIndex)) warnIfNilArg(C, msg, Arg, Class, CanBeSubscript); }
void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) return; FoundationClass Class = findKnownClass(ID); static const unsigned InvalidArgIndex = UINT_MAX; unsigned Arg = InvalidArgIndex; bool CanBeSubscript = false; if (Class == FC_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:") { Arg = 0; } } else if (Class == FC_NSArray) { Selector S = msg.getSelector(); if (S.isUnarySelector()) return; if (S.getNameForSlot(0).equals("addObject")) { Arg = 0; } else if (S.getNameForSlot(0).equals("insertObject") && S.getNameForSlot(1).equals("atIndex")) { Arg = 0; } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") && S.getNameForSlot(1).equals("withObject")) { Arg = 1; } 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)) WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript); }
bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME) { Selector S = ME->getSelector(); if (S.isUnarySelector()) return false; // FIXME: This is going to be really slow doing these checks with // lexical comparisons. std::string name = S.getAsString(); assert (!name.empty()); const char* cstr = &name[0]; unsigned len = name.size(); switch (len) { default: break; case 8: if (!strcmp(cstr, "compare:")) return CheckNilArg(N, 0); break; case 15: // 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 (!strcmp(cstr, "initWithFormat:")) return CheckNilArg(N, 0); break; case 16: if (!strcmp(cstr, "compare:options:")) return CheckNilArg(N, 0); break; case 22: if (!strcmp(cstr, "compare:options:range:")) return CheckNilArg(N, 0); break; case 23: if (!strcmp(cstr, "caseInsensitiveCompare:")) return CheckNilArg(N, 0); break; case 29: if (!strcmp(cstr, "compare:options:range:locale:")) return CheckNilArg(N, 0); break; case 37: if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:")) return CheckNilArg(N, 0); break; } return false; }