1//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines BasicObjCFoundationChecks, a class that encapsulates
10// a set of simple checks to run on Objective-C code using Apple's Foundation
11// classes.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/AST/ASTContext.h"
16#include "clang/AST/DeclObjC.h"
17#include "clang/AST/Expr.h"
18#include "clang/AST/ExprObjC.h"
19#include "clang/AST/StmtObjC.h"
20#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
21#include "clang/Analysis/SelectorExtras.h"
22#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24#include "clang/StaticAnalyzer/Core/Checker.h"
25#include "clang/StaticAnalyzer/Core/CheckerManager.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
30#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
32#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
33#include "llvm/ADT/STLExtras.h"
34#include "llvm/ADT/StringMap.h"
35#include "llvm/Support/raw_ostream.h"
36#include <optional>
37
38using namespace clang;
39using namespace ento;
40using namespace llvm;
41
42namespace {
43class APIMisuse : public BugType {
44public:
45 APIMisuse(const CheckerBase *checker, const char *name)
46 : BugType(checker, name, categories::AppleAPIMisuse) {}
47};
48} // end anonymous namespace
49
50//===----------------------------------------------------------------------===//
51// Utility functions.
52//===----------------------------------------------------------------------===//
53
54static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
55 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
56 return ID->getIdentifier()->getName();
57 return StringRef();
58}
59
60enum FoundationClass {
61 FC_None,
62 FC_NSArray,
63 FC_NSDictionary,
64 FC_NSEnumerator,
65 FC_NSNull,
66 FC_NSOrderedSet,
67 FC_NSSet,
68 FC_NSString
69};
70
71static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
72 bool IncludeSuperclasses = true) {
73 static llvm::StringMap<FoundationClass> Classes;
74 if (Classes.empty()) {
75 Classes["NSArray"] = FC_NSArray;
76 Classes["NSDictionary"] = FC_NSDictionary;
77 Classes["NSEnumerator"] = FC_NSEnumerator;
78 Classes["NSNull"] = FC_NSNull;
79 Classes["NSOrderedSet"] = FC_NSOrderedSet;
80 Classes["NSSet"] = FC_NSSet;
81 Classes["NSString"] = FC_NSString;
82 }
83
84 // FIXME: Should we cache this at all?
85 FoundationClass result = Classes.lookup(Key: ID->getIdentifier()->getName());
86 if (result == FC_None && IncludeSuperclasses)
87 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
88 return findKnownClass(ID: Super);
89
90 return result;
91}
92
93//===----------------------------------------------------------------------===//
94// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
95//===----------------------------------------------------------------------===//
96
97namespace {
98class NilArgChecker : public Checker<check::PreObjCMessage,
99 check::PostStmt<ObjCDictionaryLiteral>,
100 check::PostStmt<ObjCArrayLiteral>,
101 EventDispatcher<ImplicitNullDerefEvent>> {
102 mutable std::unique_ptr<APIMisuse> BT;
103
104 mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
105 mutable Selector ArrayWithObjectSel;
106 mutable Selector AddObjectSel;
107 mutable Selector InsertObjectAtIndexSel;
108 mutable Selector ReplaceObjectAtIndexWithObjectSel;
109 mutable Selector SetObjectAtIndexedSubscriptSel;
110 mutable Selector ArrayByAddingObjectSel;
111 mutable Selector DictionaryWithObjectForKeySel;
112 mutable Selector SetObjectForKeySel;
113 mutable Selector SetObjectForKeyedSubscriptSel;
114 mutable Selector RemoveObjectForKeySel;
115
116 void warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const;
117
118 void warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned Arg,
119 FoundationClass Class, bool CanBeSubscript = false) const;
120
121 void generateBugReport(ExplodedNode *N, StringRef Msg, SourceRange Range,
122 const Expr *Expr, CheckerContext &C) const;
123
124public:
125 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
126 void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
127 void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
128};
129} // end anonymous namespace
130
131void NilArgChecker::warnIfNilExpr(const Expr *E,
132 const char *Msg,
133 CheckerContext &C) const {
134 auto Location = C.getSVal(S: E).getAs<Loc>();
135 if (!Location)
136 return;
137
138 auto [NonNull, Null] = C.getState()->assume(Cond: *Location);
139
140 // If it's known to be null.
141 if (!NonNull && Null) {
142 if (ExplodedNode *N = C.generateErrorNode()) {
143 generateBugReport(N, Msg, Range: E->getSourceRange(), Expr: E, C);
144 return;
145 }
146 }
147
148 // If it might be null, assume that it cannot after this operation.
149 if (Null) {
150 // One needs to make sure the pointer is non-null to be used here.
151 if (ExplodedNode *N = C.generateSink(State: Null, Pred: C.getPredecessor())) {
152 dispatchEvent(event: {.Location: *Location, /*IsLoad=*/false, .SinkNode: N, .BR: &C.getBugReporter(),
153 /*IsDirectDereference=*/false});
154 }
155 C.addTransition(State: NonNull);
156 }
157}
158
159void NilArgChecker::warnIfNilArg(CheckerContext &C,
160 const ObjCMethodCall &msg,
161 unsigned int Arg,
162 FoundationClass Class,
163 bool CanBeSubscript) const {
164 // Check if the argument is nil.
165 ProgramStateRef State = C.getState();
166 if (!State->isNull(V: msg.getArgSVal(Index: Arg)).isConstrainedTrue())
167 return;
168
169 // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
170 // because it's called multiple times from some callers, so it'd cause
171 // an unwanted state split if two or more non-fatal errors are thrown
172 // within the same checker callback. For now we don't want to, but
173 // it'll need to be fixed if we ever want to.
174 if (ExplodedNode *N = C.generateErrorNode()) {
175 SmallString<128> sbuf;
176 llvm::raw_svector_ostream os(sbuf);
177
178 if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
179
180 if (Class == FC_NSArray) {
181 os << "Array element cannot be nil";
182 } else if (Class == FC_NSDictionary) {
183 if (Arg == 0) {
184 os << "Value stored into '";
185 os << GetReceiverInterfaceName(msg) << "' cannot be nil";
186 } else {
187 assert(Arg == 1);
188 os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
189 }
190 } else
191 llvm_unreachable("Missing foundation class for the subscript expr");
192
193 } else {
194 if (Class == FC_NSDictionary) {
195 if (Arg == 0)
196 os << "Value argument ";
197 else {
198 assert(Arg == 1);
199 os << "Key argument ";
200 }
201 os << "to '";
202 msg.getSelector().print(OS&: os);
203 os << "' cannot be nil";
204 } else {
205 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
206 msg.getSelector().print(OS&: os);
207 os << "' cannot be nil";
208 }
209 }
210
211 generateBugReport(N, Msg: os.str(), Range: msg.getArgSourceRange(Index: Arg),
212 Expr: msg.getArgExpr(Index: Arg), C);
213 }
214}
215
216void NilArgChecker::generateBugReport(ExplodedNode *N,
217 StringRef Msg,
218 SourceRange Range,
219 const Expr *E,
220 CheckerContext &C) const {
221 if (!BT)
222 BT.reset(p: new APIMisuse(this, "nil argument"));
223
224 auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args&: Msg, args&: N);
225 R->addRange(R: Range);
226 bugreporter::trackExpressionValue(N, E, R&: *R);
227 C.emitReport(R: std::move(R));
228}
229
230void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
231 CheckerContext &C) const {
232 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
233 if (!ID)
234 return;
235
236 FoundationClass Class = findKnownClass(ID);
237
238 static const unsigned InvalidArgIndex = UINT_MAX;
239 unsigned Arg = InvalidArgIndex;
240 bool CanBeSubscript = false;
241
242 if (Class == FC_NSString) {
243 Selector S = msg.getSelector();
244
245 if (S.isUnarySelector())
246 return;
247
248 if (StringSelectors.empty()) {
249 ASTContext &Ctx = C.getASTContext();
250 Selector Sels[] = {
251 getKeywordSelector(Ctx, IIs: "caseInsensitiveCompare"),
252 getKeywordSelector(Ctx, IIs: "compare"),
253 getKeywordSelector(Ctx, IIs: "compare", IIs: "options"),
254 getKeywordSelector(Ctx, IIs: "compare", IIs: "options", IIs: "range"),
255 getKeywordSelector(Ctx, IIs: "compare", IIs: "options", IIs: "range", IIs: "locale"),
256 getKeywordSelector(Ctx, IIs: "componentsSeparatedByCharactersInSet"),
257 getKeywordSelector(Ctx, IIs: "initWithFormat"),
258 getKeywordSelector(Ctx, IIs: "localizedCaseInsensitiveCompare"),
259 getKeywordSelector(Ctx, IIs: "localizedCompare"),
260 getKeywordSelector(Ctx, IIs: "localizedStandardCompare"),
261 };
262 for (Selector KnownSel : Sels)
263 StringSelectors[KnownSel] = 0;
264 }
265 auto I = StringSelectors.find(Val: S);
266 if (I == StringSelectors.end())
267 return;
268 Arg = I->second;
269 } else if (Class == FC_NSArray) {
270 Selector S = msg.getSelector();
271
272 if (S.isUnarySelector())
273 return;
274
275 if (ArrayWithObjectSel.isNull()) {
276 ASTContext &Ctx = C.getASTContext();
277 ArrayWithObjectSel = getKeywordSelector(Ctx, IIs: "arrayWithObject");
278 AddObjectSel = getKeywordSelector(Ctx, IIs: "addObject");
279 InsertObjectAtIndexSel =
280 getKeywordSelector(Ctx, IIs: "insertObject", IIs: "atIndex");
281 ReplaceObjectAtIndexWithObjectSel =
282 getKeywordSelector(Ctx, IIs: "replaceObjectAtIndex", IIs: "withObject");
283 SetObjectAtIndexedSubscriptSel =
284 getKeywordSelector(Ctx, IIs: "setObject", IIs: "atIndexedSubscript");
285 ArrayByAddingObjectSel = getKeywordSelector(Ctx, IIs: "arrayByAddingObject");
286 }
287
288 if (S == ArrayWithObjectSel || S == AddObjectSel ||
289 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
290 Arg = 0;
291 } else if (S == SetObjectAtIndexedSubscriptSel) {
292 Arg = 0;
293 CanBeSubscript = true;
294 } else if (S == ReplaceObjectAtIndexWithObjectSel) {
295 Arg = 1;
296 }
297 } else if (Class == FC_NSDictionary) {
298 Selector S = msg.getSelector();
299
300 if (S.isUnarySelector())
301 return;
302
303 if (DictionaryWithObjectForKeySel.isNull()) {
304 ASTContext &Ctx = C.getASTContext();
305 DictionaryWithObjectForKeySel =
306 getKeywordSelector(Ctx, IIs: "dictionaryWithObject", IIs: "forKey");
307 SetObjectForKeySel = getKeywordSelector(Ctx, IIs: "setObject", IIs: "forKey");
308 SetObjectForKeyedSubscriptSel =
309 getKeywordSelector(Ctx, IIs: "setObject", IIs: "forKeyedSubscript");
310 RemoveObjectForKeySel = getKeywordSelector(Ctx, IIs: "removeObjectForKey");
311 }
312
313 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
314 Arg = 0;
315 warnIfNilArg(C, msg, /* Arg */1, Class);
316 } else if (S == SetObjectForKeyedSubscriptSel) {
317 CanBeSubscript = true;
318 Arg = 1;
319 } else if (S == RemoveObjectForKeySel) {
320 Arg = 0;
321 }
322 }
323
324 // If argument is '0', report a warning.
325 if ((Arg != InvalidArgIndex))
326 warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
327}
328
329void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
330 CheckerContext &C) const {
331 unsigned NumOfElements = AL->getNumElements();
332 for (unsigned i = 0; i < NumOfElements; ++i) {
333 warnIfNilExpr(E: AL->getElement(Index: i), Msg: "Array element cannot be nil", C);
334 }
335}
336
337void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
338 CheckerContext &C) const {
339 unsigned NumOfElements = DL->getNumElements();
340 for (unsigned i = 0; i < NumOfElements; ++i) {
341 ObjCDictionaryElement Element = DL->getKeyValueElement(Index: i);
342 warnIfNilExpr(E: Element.Key, Msg: "Dictionary key cannot be nil", C);
343 warnIfNilExpr(E: Element.Value, Msg: "Dictionary value cannot be nil", C);
344 }
345}
346
347//===----------------------------------------------------------------------===//
348// Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue.
349//===----------------------------------------------------------------------===//
350
351namespace {
352class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
353 mutable std::unique_ptr<APIMisuse> BT;
354 mutable IdentifierInfo *ICreate = nullptr, *IGetValue = nullptr;
355public:
356 CFNumberChecker() = default;
357
358 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
359};
360} // end anonymous namespace
361
362enum CFNumberType {
363 kCFNumberSInt8Type = 1,
364 kCFNumberSInt16Type = 2,
365 kCFNumberSInt32Type = 3,
366 kCFNumberSInt64Type = 4,
367 kCFNumberFloat32Type = 5,
368 kCFNumberFloat64Type = 6,
369 kCFNumberCharType = 7,
370 kCFNumberShortType = 8,
371 kCFNumberIntType = 9,
372 kCFNumberLongType = 10,
373 kCFNumberLongLongType = 11,
374 kCFNumberFloatType = 12,
375 kCFNumberDoubleType = 13,
376 kCFNumberCFIndexType = 14,
377 kCFNumberNSIntegerType = 15,
378 kCFNumberCGFloatType = 16
379};
380
381static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
382 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
383
384 if (i < kCFNumberCharType)
385 return FixedSize[i-1];
386
387 QualType T;
388
389 switch (i) {
390 case kCFNumberCharType: T = Ctx.CharTy; break;
391 case kCFNumberShortType: T = Ctx.ShortTy; break;
392 case kCFNumberIntType: T = Ctx.IntTy; break;
393 case kCFNumberLongType: T = Ctx.LongTy; break;
394 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
395 case kCFNumberFloatType: T = Ctx.FloatTy; break;
396 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
397 case kCFNumberCFIndexType:
398 case kCFNumberNSIntegerType:
399 case kCFNumberCGFloatType:
400 // FIXME: We need a way to map from names to Type*.
401 default:
402 return std::nullopt;
403 }
404
405 return Ctx.getTypeSize(T);
406}
407
408#if 0
409static const char* GetCFNumberTypeStr(uint64_t i) {
410 static const char* Names[] = {
411 "kCFNumberSInt8Type",
412 "kCFNumberSInt16Type",
413 "kCFNumberSInt32Type",
414 "kCFNumberSInt64Type",
415 "kCFNumberFloat32Type",
416 "kCFNumberFloat64Type",
417 "kCFNumberCharType",
418 "kCFNumberShortType",
419 "kCFNumberIntType",
420 "kCFNumberLongType",
421 "kCFNumberLongLongType",
422 "kCFNumberFloatType",
423 "kCFNumberDoubleType",
424 "kCFNumberCFIndexType",
425 "kCFNumberNSIntegerType",
426 "kCFNumberCGFloatType"
427 };
428
429 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
430}
431#endif
432
433void CFNumberChecker::checkPreStmt(const CallExpr *CE,
434 CheckerContext &C) const {
435 ProgramStateRef state = C.getState();
436 const FunctionDecl *FD = C.getCalleeDecl(CE);
437 if (!FD)
438 return;
439
440 ASTContext &Ctx = C.getASTContext();
441 if (!ICreate) {
442 ICreate = &Ctx.Idents.get(Name: "CFNumberCreate");
443 IGetValue = &Ctx.Idents.get(Name: "CFNumberGetValue");
444 }
445 if (!(FD->getIdentifier() == ICreate || FD->getIdentifier() == IGetValue) ||
446 CE->getNumArgs() != 3)
447 return;
448
449 // Get the value of the "theType" argument.
450 SVal TheTypeVal = C.getSVal(S: CE->getArg(Arg: 1));
451
452 // FIXME: We really should allow ranges of valid theType values, and
453 // bifurcate the state appropriately.
454 std::optional<nonloc::ConcreteInt> V =
455 dyn_cast<nonloc::ConcreteInt>(Val&: TheTypeVal);
456 if (!V)
457 return;
458
459 uint64_t NumberKind = V->getValue()->getLimitedValue();
460 std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, i: NumberKind);
461
462 // FIXME: In some cases we can emit an error.
463 if (!OptCFNumberSize)
464 return;
465
466 uint64_t CFNumberSize = *OptCFNumberSize;
467
468 // Look at the value of the integer being passed by reference. Essentially
469 // we want to catch cases where the value passed in is not equal to the
470 // size of the type being created.
471 SVal TheValueExpr = C.getSVal(S: CE->getArg(Arg: 2));
472
473 // FIXME: Eventually we should handle arbitrary locations. We can do this
474 // by having an enhanced memory model that does low-level typing.
475 std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
476 if (!LV)
477 return;
478
479 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(Val: LV->stripCasts());
480 if (!R)
481 return;
482
483 QualType T = Ctx.getCanonicalType(T: R->getValueType());
484
485 // FIXME: If the pointee isn't an integer type, should we flag a warning?
486 // People can do weird stuff with pointers.
487
488 if (!T->isIntegralOrEnumerationType())
489 return;
490
491 uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T);
492
493 if (PrimitiveTypeSize == CFNumberSize)
494 return;
495
496 // FIXME: We can actually create an abstract "CFNumber" object that has
497 // the bits initialized to the provided values.
498 ExplodedNode *N = C.generateNonFatalErrorNode();
499 if (N) {
500 SmallString<128> sbuf;
501 llvm::raw_svector_ostream os(sbuf);
502 bool isCreate = (FD->getIdentifier() == ICreate);
503
504 if (isCreate) {
505 os << (PrimitiveTypeSize == 8 ? "An " : "A ")
506 << PrimitiveTypeSize << "-bit integer is used to initialize a "
507 << "CFNumber object that represents "
508 << (CFNumberSize == 8 ? "an " : "a ")
509 << CFNumberSize << "-bit integer; ";
510 } else {
511 os << "A CFNumber object that represents "
512 << (CFNumberSize == 8 ? "an " : "a ")
513 << CFNumberSize << "-bit integer is used to initialize "
514 << (PrimitiveTypeSize == 8 ? "an " : "a ")
515 << PrimitiveTypeSize << "-bit integer; ";
516 }
517
518 if (PrimitiveTypeSize < CFNumberSize)
519 os << (CFNumberSize - PrimitiveTypeSize)
520 << " bits of the CFNumber value will "
521 << (isCreate ? "be garbage." : "overwrite adjacent storage.");
522 else
523 os << (PrimitiveTypeSize - CFNumberSize)
524 << " bits of the integer value will be "
525 << (isCreate ? "lost." : "garbage.");
526
527 if (!BT)
528 BT.reset(p: new APIMisuse(this, "Bad use of CFNumber APIs"));
529
530 auto report = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: os.str(), args&: N);
531 report->addRange(R: CE->getArg(Arg: 2)->getSourceRange());
532 C.emitReport(R: std::move(report));
533 }
534}
535
536//===----------------------------------------------------------------------===//
537// CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
538//===----------------------------------------------------------------------===//
539
540namespace {
541class CFRetainReleaseChecker : public Checker<check::PreCall> {
542 mutable APIMisuse BT{this, "null passed to CF memory management function"};
543 const CallDescriptionSet ModelledCalls = {
544 {CDM::CLibrary, {"CFRetain"}, 1},
545 {CDM::CLibrary, {"CFRelease"}, 1},
546 {CDM::CLibrary, {"CFMakeCollectable"}, 1},
547 {CDM::CLibrary, {"CFAutorelease"}, 1},
548 };
549
550public:
551 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
552};
553} // end anonymous namespace
554
555void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
556 CheckerContext &C) const {
557 // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
558 if (!ModelledCalls.contains(Call))
559 return;
560
561 // Get the argument's value.
562 SVal ArgVal = Call.getArgSVal(Index: 0);
563 std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
564 if (!DefArgVal)
565 return;
566
567 // Is it null?
568 ProgramStateRef state = C.getState();
569 ProgramStateRef stateNonNull, stateNull;
570 std::tie(args&: stateNonNull, args&: stateNull) = state->assume(Cond: *DefArgVal);
571
572 if (!stateNonNull) {
573 ExplodedNode *N = C.generateErrorNode(State: stateNull);
574 if (!N)
575 return;
576
577 SmallString<64> Str;
578 raw_svector_ostream OS(Str);
579 OS << "Null pointer argument in call to "
580 << cast<FunctionDecl>(Val: Call.getDecl())->getName();
581
582 auto report = std::make_unique<PathSensitiveBugReport>(args&: BT, args: OS.str(), args&: N);
583 report->addRange(R: Call.getArgSourceRange(Index: 0));
584 bugreporter::trackExpressionValue(N, E: Call.getArgExpr(Index: 0), R&: *report);
585 C.emitReport(R: std::move(report));
586 return;
587 }
588
589 // From here on, we know the argument is non-null.
590 C.addTransition(State: stateNonNull);
591}
592
593//===----------------------------------------------------------------------===//
594// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
595//===----------------------------------------------------------------------===//
596
597namespace {
598class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
599 mutable Selector releaseS;
600 mutable Selector retainS;
601 mutable Selector autoreleaseS;
602 mutable Selector drainS;
603 mutable std::unique_ptr<BugType> BT;
604
605public:
606 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
607};
608} // end anonymous namespace
609
610void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
611 CheckerContext &C) const {
612 if (!BT) {
613 BT.reset(p: new APIMisuse(
614 this, "message incorrectly sent to class instead of class instance"));
615
616 ASTContext &Ctx = C.getASTContext();
617 releaseS = GetNullarySelector(name: "release", Ctx);
618 retainS = GetNullarySelector(name: "retain", Ctx);
619 autoreleaseS = GetNullarySelector(name: "autorelease", Ctx);
620 drainS = GetNullarySelector(name: "drain", Ctx);
621 }
622
623 if (msg.isInstanceMessage())
624 return;
625 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
626 assert(Class);
627
628 Selector S = msg.getSelector();
629 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
630 return;
631
632 if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
633 SmallString<200> buf;
634 llvm::raw_svector_ostream os(buf);
635
636 os << "The '";
637 S.print(OS&: os);
638 os << "' message should be sent to instances "
639 "of class '" << Class->getName()
640 << "' and not the class directly";
641
642 auto report = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: os.str(), args&: N);
643 report->addRange(R: msg.getSourceRange());
644 C.emitReport(R: std::move(report));
645 }
646}
647
648//===----------------------------------------------------------------------===//
649// Check for passing non-Objective-C types to variadic methods that expect
650// only Objective-C types.
651//===----------------------------------------------------------------------===//
652
653namespace {
654class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
655 mutable Selector arrayWithObjectsS;
656 mutable Selector dictionaryWithObjectsAndKeysS;
657 mutable Selector setWithObjectsS;
658 mutable Selector orderedSetWithObjectsS;
659 mutable Selector initWithObjectsS;
660 mutable Selector initWithObjectsAndKeysS;
661 mutable std::unique_ptr<BugType> BT;
662
663 bool isVariadicMessage(const ObjCMethodCall &msg) const;
664
665public:
666 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
667};
668} // end anonymous namespace
669
670/// isVariadicMessage - Returns whether the given message is a variadic message,
671/// where all arguments must be Objective-C types.
672bool
673VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
674 const ObjCMethodDecl *MD = msg.getDecl();
675
676 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(Val: MD->getDeclContext()))
677 return false;
678
679 Selector S = msg.getSelector();
680
681 if (msg.isInstanceMessage()) {
682 // FIXME: Ideally we'd look at the receiver interface here, but that's not
683 // useful for init, because alloc returns 'id'. In theory, this could lead
684 // to false positives, for example if there existed a class that had an
685 // initWithObjects: implementation that does accept non-Objective-C pointer
686 // types, but the chance of that happening is pretty small compared to the
687 // gains that this analysis gives.
688 const ObjCInterfaceDecl *Class = MD->getClassInterface();
689
690 switch (findKnownClass(ID: Class)) {
691 case FC_NSArray:
692 case FC_NSOrderedSet:
693 case FC_NSSet:
694 return S == initWithObjectsS;
695 case FC_NSDictionary:
696 return S == initWithObjectsAndKeysS;
697 default:
698 return false;
699 }
700 } else {
701 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
702
703 switch (findKnownClass(ID: Class)) {
704 case FC_NSArray:
705 return S == arrayWithObjectsS;
706 case FC_NSOrderedSet:
707 return S == orderedSetWithObjectsS;
708 case FC_NSSet:
709 return S == setWithObjectsS;
710 case FC_NSDictionary:
711 return S == dictionaryWithObjectsAndKeysS;
712 default:
713 return false;
714 }
715 }
716}
717
718void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
719 CheckerContext &C) const {
720 if (!BT) {
721 BT.reset(p: new APIMisuse(this,
722 "Arguments passed to variadic method aren't all "
723 "Objective-C pointer types"));
724
725 ASTContext &Ctx = C.getASTContext();
726 arrayWithObjectsS = GetUnarySelector(name: "arrayWithObjects", Ctx);
727 dictionaryWithObjectsAndKeysS =
728 GetUnarySelector(name: "dictionaryWithObjectsAndKeys", Ctx);
729 setWithObjectsS = GetUnarySelector(name: "setWithObjects", Ctx);
730 orderedSetWithObjectsS = GetUnarySelector(name: "orderedSetWithObjects", Ctx);
731
732 initWithObjectsS = GetUnarySelector(name: "initWithObjects", Ctx);
733 initWithObjectsAndKeysS = GetUnarySelector(name: "initWithObjectsAndKeys", Ctx);
734 }
735
736 if (!isVariadicMessage(msg))
737 return;
738
739 // We are not interested in the selector arguments since they have
740 // well-defined types, so the compiler will issue a warning for them.
741 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
742
743 // We're not interested in the last argument since it has to be nil or the
744 // compiler would have issued a warning for it elsewhere.
745 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
746
747 if (variadicArgsEnd <= variadicArgsBegin)
748 return;
749
750 // Verify that all arguments have Objective-C types.
751 std::optional<ExplodedNode *> errorNode;
752
753 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
754 QualType ArgTy = msg.getArgExpr(Index: I)->getType();
755 if (ArgTy->isObjCObjectPointerType())
756 continue;
757
758 // Block pointers are treaded as Objective-C pointers.
759 if (ArgTy->isBlockPointerType())
760 continue;
761
762 // Ignore pointer constants.
763 if (isa<loc::ConcreteInt>(Val: msg.getArgSVal(Index: I)))
764 continue;
765
766 // Ignore pointer types annotated with 'NSObject' attribute.
767 if (C.getASTContext().isObjCNSObjectType(Ty: ArgTy))
768 continue;
769
770 // Ignore CF references, which can be toll-free bridged.
771 if (coreFoundation::isCFObjectRef(T: ArgTy))
772 continue;
773
774 // Generate only one error node to use for all bug reports.
775 if (!errorNode)
776 errorNode = C.generateNonFatalErrorNode();
777
778 if (!*errorNode)
779 continue;
780
781 SmallString<128> sbuf;
782 llvm::raw_svector_ostream os(sbuf);
783
784 StringRef TypeName = GetReceiverInterfaceName(msg);
785 if (!TypeName.empty())
786 os << "Argument to '" << TypeName << "' method '";
787 else
788 os << "Argument to method '";
789
790 msg.getSelector().print(OS&: os);
791 os << "' should be an Objective-C pointer type, not '";
792 ArgTy.print(OS&: os, Policy: C.getLangOpts());
793 os << "'";
794
795 auto R =
796 std::make_unique<PathSensitiveBugReport>(args&: *BT, args: os.str(), args&: *errorNode);
797 R->addRange(R: msg.getArgSourceRange(Index: I));
798 C.emitReport(R: std::move(R));
799 }
800}
801
802//===----------------------------------------------------------------------===//
803// Improves the modeling of loops over Cocoa collections.
804//===----------------------------------------------------------------------===//
805
806// The map from container symbol to the container count symbol.
807// We currently will remember the last container count symbol encountered.
808REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
809REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
810
811namespace {
812class ObjCLoopChecker
813 : public Checker<check::PostStmt<ObjCForCollectionStmt>,
814 check::PostObjCMessage,
815 check::DeadSymbols,
816 check::PointerEscape > {
817 mutable IdentifierInfo *CountSelectorII = nullptr;
818
819 bool isCollectionCountMethod(const ObjCMethodCall &M,
820 CheckerContext &C) const;
821
822public:
823 ObjCLoopChecker() = default;
824 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
825 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
826 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
827 ProgramStateRef checkPointerEscape(ProgramStateRef State,
828 const InvalidatedSymbols &Escaped,
829 const CallEvent *Call,
830 PointerEscapeKind Kind) const;
831};
832} // end anonymous namespace
833
834static bool isKnownNonNilCollectionType(QualType T) {
835 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
836 if (!PT)
837 return false;
838
839 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
840 if (!ID)
841 return false;
842
843 switch (findKnownClass(ID)) {
844 case FC_NSArray:
845 case FC_NSDictionary:
846 case FC_NSEnumerator:
847 case FC_NSOrderedSet:
848 case FC_NSSet:
849 return true;
850 default:
851 return false;
852 }
853}
854
855/// Assumes that the collection is non-nil.
856///
857/// If the collection is known to be nil, returns NULL to indicate an infeasible
858/// path.
859static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
860 ProgramStateRef State,
861 const ObjCForCollectionStmt *FCS) {
862 if (!State)
863 return nullptr;
864
865 SVal CollectionVal = C.getSVal(S: FCS->getCollection());
866 std::optional<DefinedSVal> KnownCollection =
867 CollectionVal.getAs<DefinedSVal>();
868 if (!KnownCollection)
869 return State;
870
871 ProgramStateRef StNonNil, StNil;
872 std::tie(args&: StNonNil, args&: StNil) = State->assume(Cond: *KnownCollection);
873 if (StNil && !StNonNil) {
874 // The collection is nil. This path is infeasible.
875 return nullptr;
876 }
877
878 return StNonNil;
879}
880
881/// Assumes that the collection elements are non-nil.
882///
883/// This only applies if the collection is one of those known not to contain
884/// nil values.
885static ProgramStateRef checkElementNonNil(CheckerContext &C,
886 ProgramStateRef State,
887 const ObjCForCollectionStmt *FCS) {
888 if (!State)
889 return nullptr;
890
891 // See if the collection is one where we /know/ the elements are non-nil.
892 if (!isKnownNonNilCollectionType(T: FCS->getCollection()->getType()))
893 return State;
894
895 const LocationContext *LCtx = C.getLocationContext();
896 const Stmt *Element = FCS->getElement();
897
898 // FIXME: Copied from ExprEngineObjC.
899 std::optional<Loc> ElementLoc;
900 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Val: Element)) {
901 const VarDecl *ElemDecl = cast<VarDecl>(Val: DS->getSingleDecl());
902 assert(ElemDecl->getInit() == nullptr);
903 ElementLoc = State->getLValue(VD: ElemDecl, LC: LCtx);
904 } else {
905 ElementLoc = State->getSVal(Ex: Element, LCtx).getAs<Loc>();
906 }
907
908 if (!ElementLoc)
909 return State;
910
911 // Go ahead and assume the value is non-nil.
912 SVal Val = State->getSVal(LV: *ElementLoc);
913 return State->assume(Cond: cast<DefinedOrUnknownSVal>(Val), Assumption: true);
914}
915
916/// Returns NULL state if the collection is known to contain elements
917/// (or is known not to contain elements if the Assumption parameter is false.)
918static ProgramStateRef
919assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
920 SymbolRef CollectionS, bool Assumption) {
921 if (!State || !CollectionS)
922 return State;
923
924 const SymbolRef *CountS = State->get<ContainerCountMap>(key: CollectionS);
925 if (!CountS) {
926 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(key: CollectionS);
927 if (!KnownNonEmpty)
928 return State->set<ContainerNonEmptyMap>(K: CollectionS, E: Assumption);
929 return (Assumption == *KnownNonEmpty) ? State : nullptr;
930 }
931
932 SValBuilder &SvalBuilder = C.getSValBuilder();
933 SVal CountGreaterThanZeroVal =
934 SvalBuilder.evalBinOp(state: State, op: BO_GT,
935 lhs: nonloc::SymbolVal(*CountS),
936 rhs: SvalBuilder.makeIntVal(integer: 0, type: (*CountS)->getType()),
937 type: SvalBuilder.getConditionType());
938 std::optional<DefinedSVal> CountGreaterThanZero =
939 CountGreaterThanZeroVal.getAs<DefinedSVal>();
940 if (!CountGreaterThanZero) {
941 // The SValBuilder cannot construct a valid SVal for this condition.
942 // This means we cannot properly reason about it.
943 return State;
944 }
945
946 return State->assume(Cond: *CountGreaterThanZero, Assumption);
947}
948
949static ProgramStateRef
950assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
951 const ObjCForCollectionStmt *FCS,
952 bool Assumption) {
953 if (!State)
954 return nullptr;
955
956 SymbolRef CollectionS = C.getSVal(S: FCS->getCollection()).getAsSymbol();
957 return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
958}
959
960/// If the fist block edge is a back edge, we are reentering the loop.
961static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
962 const ObjCForCollectionStmt *FCS) {
963 if (!N)
964 return false;
965
966 ProgramPoint P = N->getLocation();
967 if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
968 return BE->getSrc()->getLoopTarget() == FCS;
969 }
970
971 // Keep looking for a block edge.
972 for (const ExplodedNode *N : N->preds()) {
973 if (alreadyExecutedAtLeastOneLoopIteration(N, FCS))
974 return true;
975 }
976
977 return false;
978}
979
980void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
981 CheckerContext &C) const {
982 ProgramStateRef State = C.getState();
983
984 // Check if this is the branch for the end of the loop.
985 if (!ExprEngine::hasMoreIteration(State, O: FCS, LC: C.getLocationContext())) {
986 if (!alreadyExecutedAtLeastOneLoopIteration(N: C.getPredecessor(), FCS))
987 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
988
989 // Otherwise, this is a branch that goes through the loop body.
990 } else {
991 State = checkCollectionNonNil(C, State, FCS);
992 State = checkElementNonNil(C, State, FCS);
993 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
994 }
995
996 if (!State)
997 C.generateSink(State: C.getState(), Pred: C.getPredecessor());
998 else if (State != C.getState())
999 C.addTransition(State);
1000}
1001
1002bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
1003 CheckerContext &C) const {
1004 Selector S = M.getSelector();
1005 // Initialize the identifiers on first use.
1006 if (!CountSelectorII)
1007 CountSelectorII = &C.getASTContext().Idents.get(Name: "count");
1008
1009 // If the method returns collection count, record the value.
1010 return S.isUnarySelector() &&
1011 (S.getIdentifierInfoForSlot(argIndex: 0) == CountSelectorII);
1012}
1013
1014void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1015 CheckerContext &C) const {
1016 if (!M.isInstanceMessage())
1017 return;
1018
1019 const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
1020 if (!ClassID)
1021 return;
1022
1023 FoundationClass Class = findKnownClass(ID: ClassID);
1024 if (Class != FC_NSDictionary &&
1025 Class != FC_NSArray &&
1026 Class != FC_NSSet &&
1027 Class != FC_NSOrderedSet)
1028 return;
1029
1030 SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
1031 if (!ContainerS)
1032 return;
1033
1034 // If we are processing a call to "count", get the symbolic value returned by
1035 // a call to "count" and add it to the map.
1036 if (!isCollectionCountMethod(M, C))
1037 return;
1038
1039 const Expr *MsgExpr = M.getOriginExpr();
1040 SymbolRef CountS = C.getSVal(S: MsgExpr).getAsSymbol();
1041 if (CountS) {
1042 ProgramStateRef State = C.getState();
1043
1044 C.getSymbolManager().addSymbolDependency(Primary: ContainerS, Dependent: CountS);
1045 State = State->set<ContainerCountMap>(K: ContainerS, E: CountS);
1046
1047 if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(key: ContainerS)) {
1048 State = State->remove<ContainerNonEmptyMap>(K: ContainerS);
1049 State = assumeCollectionNonEmpty(C, State, CollectionS: ContainerS, Assumption: *NonEmpty);
1050 }
1051
1052 C.addTransition(State);
1053 }
1054}
1055
1056static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
1057 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Val: Call);
1058 if (!Message)
1059 return nullptr;
1060
1061 const ObjCMethodDecl *MD = Message->getDecl();
1062 if (!MD)
1063 return nullptr;
1064
1065 const ObjCInterfaceDecl *StaticClass;
1066 if (isa<ObjCProtocolDecl>(Val: MD->getDeclContext())) {
1067 // We can't find out where the method was declared without doing more work.
1068 // Instead, see if the receiver is statically typed as a known immutable
1069 // collection.
1070 StaticClass = Message->getOriginExpr()->getReceiverInterface();
1071 } else {
1072 StaticClass = MD->getClassInterface();
1073 }
1074
1075 if (!StaticClass)
1076 return nullptr;
1077
1078 switch (findKnownClass(ID: StaticClass, /*IncludeSuper=*/IncludeSuperclasses: false)) {
1079 case FC_None:
1080 return nullptr;
1081 case FC_NSArray:
1082 case FC_NSDictionary:
1083 case FC_NSEnumerator:
1084 case FC_NSNull:
1085 case FC_NSOrderedSet:
1086 case FC_NSSet:
1087 case FC_NSString:
1088 break;
1089 }
1090
1091 return Message->getReceiverSVal().getAsSymbol();
1092}
1093
1094ProgramStateRef
1095ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
1096 const InvalidatedSymbols &Escaped,
1097 const CallEvent *Call,
1098 PointerEscapeKind Kind) const {
1099 SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
1100
1101 // Remove the invalidated symbols from the collection count map.
1102 for (SymbolRef Sym : Escaped) {
1103 // Don't invalidate this symbol's count if we know the method being called
1104 // is declared on an immutable class. This isn't completely correct if the
1105 // receiver is also passed as an argument, but in most uses of NSArray,
1106 // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1107 if (Sym == ImmutableReceiver)
1108 continue;
1109
1110 // The symbol escaped. Pessimistically, assume that the count could have
1111 // changed.
1112 State = State->remove<ContainerCountMap>(K: Sym);
1113 State = State->remove<ContainerNonEmptyMap>(K: Sym);
1114 }
1115 return State;
1116}
1117
1118void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1119 CheckerContext &C) const {
1120 ProgramStateRef State = C.getState();
1121
1122 // Remove the dead symbols from the collection count map.
1123 ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1124 for (SymbolRef Sym : llvm::make_first_range(c&: Tracked)) {
1125 if (SymReaper.isDead(sym: Sym)) {
1126 State = State->remove<ContainerCountMap>(K: Sym);
1127 State = State->remove<ContainerNonEmptyMap>(K: Sym);
1128 }
1129 }
1130
1131 C.addTransition(State);
1132}
1133
1134namespace {
1135/// \class ObjCNonNilReturnValueChecker
1136/// The checker restricts the return values of APIs known to
1137/// never (or almost never) return 'nil'.
1138class ObjCNonNilReturnValueChecker
1139 : public Checker<check::PostObjCMessage,
1140 check::PostStmt<ObjCArrayLiteral>,
1141 check::PostStmt<ObjCDictionaryLiteral>,
1142 check::PostStmt<ObjCBoxedExpr> > {
1143 mutable bool Initialized = false;
1144 mutable Selector ObjectAtIndex;
1145 mutable Selector ObjectAtIndexedSubscript;
1146 mutable Selector NullSelector;
1147
1148public:
1149 ObjCNonNilReturnValueChecker() = default;
1150
1151 ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
1152 ProgramStateRef State,
1153 CheckerContext &C) const;
1154 void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
1155 C.addTransition(State: assumeExprIsNonNull(NonNullExpr: E, State: C.getState(), C));
1156 }
1157
1158 void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
1159 assumeExprIsNonNull(E, C);
1160 }
1161 void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
1162 assumeExprIsNonNull(E, C);
1163 }
1164 void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
1165 assumeExprIsNonNull(E, C);
1166 }
1167
1168 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
1169};
1170} // end anonymous namespace
1171
1172ProgramStateRef
1173ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
1174 ProgramStateRef State,
1175 CheckerContext &C) const {
1176 SVal Val = C.getSVal(S: NonNullExpr);
1177 if (std::optional<DefinedOrUnknownSVal> DV =
1178 Val.getAs<DefinedOrUnknownSVal>())
1179 return State->assume(Cond: *DV, Assumption: true);
1180 return State;
1181}
1182
1183void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1184 CheckerContext &C)
1185 const {
1186 ProgramStateRef State = C.getState();
1187
1188 if (!Initialized) {
1189 ASTContext &Ctx = C.getASTContext();
1190 ObjectAtIndex = GetUnarySelector(name: "objectAtIndex", Ctx);
1191 ObjectAtIndexedSubscript = GetUnarySelector(name: "objectAtIndexedSubscript", Ctx);
1192 NullSelector = GetNullarySelector(name: "null", Ctx);
1193 }
1194
1195 // Check the receiver type.
1196 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
1197
1198 // Assume that object returned from '[self init]' or '[super init]' is not
1199 // 'nil' if we are processing an inlined function/method.
1200 //
1201 // A defensive callee will (and should) check if the object returned by
1202 // '[super init]' is 'nil' before doing it's own initialization. However,
1203 // since 'nil' is rarely returned in practice, we should not warn when the
1204 // caller to the defensive constructor uses the object in contexts where
1205 // 'nil' is not accepted.
1206 if (!C.inTopFrame() && M.getDecl() &&
1207 M.getDecl()->getMethodFamily() == OMF_init &&
1208 M.isReceiverSelfOrSuper()) {
1209 State = assumeExprIsNonNull(NonNullExpr: M.getOriginExpr(), State, C);
1210 }
1211
1212 FoundationClass Cl = findKnownClass(ID: Interface);
1213
1214 // Objects returned from
1215 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1216 // are never 'nil'.
1217 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
1218 Selector Sel = M.getSelector();
1219 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1220 // Go ahead and assume the value is non-nil.
1221 State = assumeExprIsNonNull(NonNullExpr: M.getOriginExpr(), State, C);
1222 }
1223 }
1224
1225 // Objects returned from [NSNull null] are not nil.
1226 if (Cl == FC_NSNull) {
1227 if (M.getSelector() == NullSelector) {
1228 // Go ahead and assume the value is non-nil.
1229 State = assumeExprIsNonNull(NonNullExpr: M.getOriginExpr(), State, C);
1230 }
1231 }
1232 }
1233 C.addTransition(State);
1234}
1235
1236//===----------------------------------------------------------------------===//
1237// Check registration.
1238//===----------------------------------------------------------------------===//
1239
1240void ento::registerNilArgChecker(CheckerManager &mgr) {
1241 mgr.registerChecker<NilArgChecker>();
1242}
1243
1244bool ento::shouldRegisterNilArgChecker(const CheckerManager &mgr) {
1245 return true;
1246}
1247
1248void ento::registerCFNumberChecker(CheckerManager &mgr) {
1249 mgr.registerChecker<CFNumberChecker>();
1250}
1251
1252bool ento::shouldRegisterCFNumberChecker(const CheckerManager &mgr) {
1253 return true;
1254}
1255
1256void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
1257 mgr.registerChecker<CFRetainReleaseChecker>();
1258}
1259
1260bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager &mgr) {
1261 return true;
1262}
1263
1264void ento::registerClassReleaseChecker(CheckerManager &mgr) {
1265 mgr.registerChecker<ClassReleaseChecker>();
1266}
1267
1268bool ento::shouldRegisterClassReleaseChecker(const CheckerManager &mgr) {
1269 return true;
1270}
1271
1272void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
1273 mgr.registerChecker<VariadicMethodTypeChecker>();
1274}
1275
1276bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager &mgr) {
1277 return true;
1278}
1279
1280void ento::registerObjCLoopChecker(CheckerManager &mgr) {
1281 mgr.registerChecker<ObjCLoopChecker>();
1282}
1283
1284bool ento::shouldRegisterObjCLoopChecker(const CheckerManager &mgr) {
1285 return true;
1286}
1287
1288void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1289 mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1290}
1291
1292bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager &mgr) {
1293 return true;
1294}
1295