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