1 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines BasicObjCFoundationChecks, a class that encapsulates
11 // a set of simple checks to run on Objective-C code using Apple's Foundation
12 // classes.
13 //
14 //===----------------------------------------------------------------------===//
15
16 #include "ClangSACheckers.h"
17 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
24 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
26 #include "clang/AST/DeclObjC.h"
27 #include "clang/AST/Expr.h"
28 #include "clang/AST/ExprObjC.h"
29 #include "clang/AST/ASTContext.h"
30
31 using namespace clang;
32 using namespace ento;
33
34 namespace {
35 class APIMisuse : public BugType {
36 public:
APIMisuse(const char * name)37 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
38 };
39 } // end anonymous namespace
40
41 //===----------------------------------------------------------------------===//
42 // Utility functions.
43 //===----------------------------------------------------------------------===//
44
GetReceiverNameType(const ObjCMessage & msg)45 static const char* GetReceiverNameType(const ObjCMessage &msg) {
46 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
47 return ID->getIdentifier()->getNameStart();
48 return 0;
49 }
50
isReceiverClassOrSuperclass(const ObjCInterfaceDecl * ID,llvm::StringRef ClassName)51 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
52 llvm::StringRef ClassName) {
53 if (ID->getIdentifier()->getName() == ClassName)
54 return true;
55
56 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
57 return isReceiverClassOrSuperclass(Super, ClassName);
58
59 return false;
60 }
61
isNil(SVal X)62 static inline bool isNil(SVal X) {
63 return isa<loc::ConcreteInt>(X);
64 }
65
66 //===----------------------------------------------------------------------===//
67 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
68 //===----------------------------------------------------------------------===//
69
70 namespace {
71 class NilArgChecker : public Checker<check::PreObjCMessage> {
72 mutable llvm::OwningPtr<APIMisuse> BT;
73
74 void WarnNilArg(CheckerContext &C,
75 const ObjCMessage &msg, unsigned Arg) const;
76
77 public:
78 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
79 };
80 }
81
WarnNilArg(CheckerContext & C,const ObjCMessage & msg,unsigned int Arg) const82 void NilArgChecker::WarnNilArg(CheckerContext &C,
83 const ObjCMessage &msg,
84 unsigned int Arg) const
85 {
86 if (!BT)
87 BT.reset(new APIMisuse("nil argument"));
88
89 if (ExplodedNode *N = C.generateSink()) {
90 llvm::SmallString<128> sbuf;
91 llvm::raw_svector_ostream os(sbuf);
92 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
93 << msg.getSelector().getAsString() << "' cannot be nil";
94
95 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
96 R->addRange(msg.getArgSourceRange(Arg));
97 C.EmitReport(R);
98 }
99 }
100
checkPreObjCMessage(ObjCMessage msg,CheckerContext & C) const101 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
102 CheckerContext &C) const {
103 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
104 if (!ID)
105 return;
106
107 if (isReceiverClassOrSuperclass(ID, "NSString")) {
108 Selector S = msg.getSelector();
109
110 if (S.isUnarySelector())
111 return;
112
113 // FIXME: This is going to be really slow doing these checks with
114 // lexical comparisons.
115
116 std::string NameStr = S.getAsString();
117 llvm::StringRef Name(NameStr);
118 assert(!Name.empty());
119
120 // FIXME: Checking for initWithFormat: will not work in most cases
121 // yet because [NSString alloc] returns id, not NSString*. We will
122 // need support for tracking expected-type information in the analyzer
123 // to find these errors.
124 if (Name == "caseInsensitiveCompare:" ||
125 Name == "compare:" ||
126 Name == "compare:options:" ||
127 Name == "compare:options:range:" ||
128 Name == "compare:options:range:locale:" ||
129 Name == "componentsSeparatedByCharactersInSet:" ||
130 Name == "initWithFormat:") {
131 if (isNil(msg.getArgSVal(0, C.getState())))
132 WarnNilArg(C, msg, 0);
133 }
134 }
135 }
136
137 //===----------------------------------------------------------------------===//
138 // Error reporting.
139 //===----------------------------------------------------------------------===//
140
141 namespace {
142 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
143 mutable llvm::OwningPtr<APIMisuse> BT;
144 mutable IdentifierInfo* II;
145 public:
CFNumberCreateChecker()146 CFNumberCreateChecker() : II(0) {}
147
148 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
149
150 private:
151 void EmitError(const TypedRegion* R, const Expr* Ex,
152 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
153 };
154 } // end anonymous namespace
155
156 enum CFNumberType {
157 kCFNumberSInt8Type = 1,
158 kCFNumberSInt16Type = 2,
159 kCFNumberSInt32Type = 3,
160 kCFNumberSInt64Type = 4,
161 kCFNumberFloat32Type = 5,
162 kCFNumberFloat64Type = 6,
163 kCFNumberCharType = 7,
164 kCFNumberShortType = 8,
165 kCFNumberIntType = 9,
166 kCFNumberLongType = 10,
167 kCFNumberLongLongType = 11,
168 kCFNumberFloatType = 12,
169 kCFNumberDoubleType = 13,
170 kCFNumberCFIndexType = 14,
171 kCFNumberNSIntegerType = 15,
172 kCFNumberCGFloatType = 16
173 };
174
175 namespace {
176 template<typename T>
177 class Optional {
178 bool IsKnown;
179 T Val;
180 public:
Optional()181 Optional() : IsKnown(false), Val(0) {}
Optional(const T & val)182 Optional(const T& val) : IsKnown(true), Val(val) {}
183
isKnown() const184 bool isKnown() const { return IsKnown; }
185
getValue() const186 const T& getValue() const {
187 assert (isKnown());
188 return Val;
189 }
190
operator const T&() const191 operator const T&() const {
192 return getValue();
193 }
194 };
195 }
196
GetCFNumberSize(ASTContext & Ctx,uint64_t i)197 static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
198 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
199
200 if (i < kCFNumberCharType)
201 return FixedSize[i-1];
202
203 QualType T;
204
205 switch (i) {
206 case kCFNumberCharType: T = Ctx.CharTy; break;
207 case kCFNumberShortType: T = Ctx.ShortTy; break;
208 case kCFNumberIntType: T = Ctx.IntTy; break;
209 case kCFNumberLongType: T = Ctx.LongTy; break;
210 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
211 case kCFNumberFloatType: T = Ctx.FloatTy; break;
212 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
213 case kCFNumberCFIndexType:
214 case kCFNumberNSIntegerType:
215 case kCFNumberCGFloatType:
216 // FIXME: We need a way to map from names to Type*.
217 default:
218 return Optional<uint64_t>();
219 }
220
221 return Ctx.getTypeSize(T);
222 }
223
224 #if 0
225 static const char* GetCFNumberTypeStr(uint64_t i) {
226 static const char* Names[] = {
227 "kCFNumberSInt8Type",
228 "kCFNumberSInt16Type",
229 "kCFNumberSInt32Type",
230 "kCFNumberSInt64Type",
231 "kCFNumberFloat32Type",
232 "kCFNumberFloat64Type",
233 "kCFNumberCharType",
234 "kCFNumberShortType",
235 "kCFNumberIntType",
236 "kCFNumberLongType",
237 "kCFNumberLongLongType",
238 "kCFNumberFloatType",
239 "kCFNumberDoubleType",
240 "kCFNumberCFIndexType",
241 "kCFNumberNSIntegerType",
242 "kCFNumberCGFloatType"
243 };
244
245 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
246 }
247 #endif
248
checkPreStmt(const CallExpr * CE,CheckerContext & C) const249 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
250 CheckerContext &C) const {
251 const Expr* Callee = CE->getCallee();
252 const GRState *state = C.getState();
253 SVal CallV = state->getSVal(Callee);
254 const FunctionDecl* FD = CallV.getAsFunctionDecl();
255
256 if (!FD)
257 return;
258
259 ASTContext &Ctx = C.getASTContext();
260 if (!II)
261 II = &Ctx.Idents.get("CFNumberCreate");
262
263 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
264 return;
265
266 // Get the value of the "theType" argument.
267 SVal TheTypeVal = state->getSVal(CE->getArg(1));
268
269 // FIXME: We really should allow ranges of valid theType values, and
270 // bifurcate the state appropriately.
271 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
272 if (!V)
273 return;
274
275 uint64_t NumberKind = V->getValue().getLimitedValue();
276 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
277
278 // FIXME: In some cases we can emit an error.
279 if (!TargetSize.isKnown())
280 return;
281
282 // Look at the value of the integer being passed by reference. Essentially
283 // we want to catch cases where the value passed in is not equal to the
284 // size of the type being created.
285 SVal TheValueExpr = state->getSVal(CE->getArg(2));
286
287 // FIXME: Eventually we should handle arbitrary locations. We can do this
288 // by having an enhanced memory model that does low-level typing.
289 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
290 if (!LV)
291 return;
292
293 const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts());
294 if (!R)
295 return;
296
297 QualType T = Ctx.getCanonicalType(R->getValueType());
298
299 // FIXME: If the pointee isn't an integer type, should we flag a warning?
300 // People can do weird stuff with pointers.
301
302 if (!T->isIntegerType())
303 return;
304
305 uint64_t SourceSize = Ctx.getTypeSize(T);
306
307 // CHECK: is SourceSize == TargetSize
308 if (SourceSize == TargetSize)
309 return;
310
311 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
312 // otherwise generate a regular node.
313 //
314 // FIXME: We can actually create an abstract "CFNumber" object that has
315 // the bits initialized to the provided values.
316 //
317 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
318 : C.generateNode()) {
319 llvm::SmallString<128> sbuf;
320 llvm::raw_svector_ostream os(sbuf);
321
322 os << (SourceSize == 8 ? "An " : "A ")
323 << SourceSize << " bit integer is used to initialize a CFNumber "
324 "object that represents "
325 << (TargetSize == 8 ? "an " : "a ")
326 << TargetSize << " bit integer. ";
327
328 if (SourceSize < TargetSize)
329 os << (TargetSize - SourceSize)
330 << " bits of the CFNumber value will be garbage." ;
331 else
332 os << (SourceSize - TargetSize)
333 << " bits of the input integer will be lost.";
334
335 if (!BT)
336 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
337
338 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
339 report->addRange(CE->getArg(2)->getSourceRange());
340 C.EmitReport(report);
341 }
342 }
343
344 //===----------------------------------------------------------------------===//
345 // CFRetain/CFRelease checking for null arguments.
346 //===----------------------------------------------------------------------===//
347
348 namespace {
349 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
350 mutable llvm::OwningPtr<APIMisuse> BT;
351 mutable IdentifierInfo *Retain, *Release;
352 public:
CFRetainReleaseChecker()353 CFRetainReleaseChecker(): Retain(0), Release(0) {}
354 void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
355 };
356 } // end anonymous namespace
357
358
checkPreStmt(const CallExpr * CE,CheckerContext & C) const359 void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
360 CheckerContext& C) const {
361 // If the CallExpr doesn't have exactly 1 argument just give up checking.
362 if (CE->getNumArgs() != 1)
363 return;
364
365 // Get the function declaration of the callee.
366 const GRState* state = C.getState();
367 SVal X = state->getSVal(CE->getCallee());
368 const FunctionDecl* FD = X.getAsFunctionDecl();
369
370 if (!FD)
371 return;
372
373 if (!BT) {
374 ASTContext &Ctx = C.getASTContext();
375 Retain = &Ctx.Idents.get("CFRetain");
376 Release = &Ctx.Idents.get("CFRelease");
377 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
378 }
379
380 // Check if we called CFRetain/CFRelease.
381 const IdentifierInfo *FuncII = FD->getIdentifier();
382 if (!(FuncII == Retain || FuncII == Release))
383 return;
384
385 // FIXME: The rest of this just checks that the argument is non-null.
386 // It should probably be refactored and combined with AttrNonNullChecker.
387
388 // Get the argument's value.
389 const Expr *Arg = CE->getArg(0);
390 SVal ArgVal = state->getSVal(Arg);
391 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
392 if (!DefArgVal)
393 return;
394
395 // Get a NULL value.
396 SValBuilder &svalBuilder = C.getSValBuilder();
397 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
398
399 // Make an expression asserting that they're equal.
400 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
401
402 // Are they equal?
403 const GRState *stateTrue, *stateFalse;
404 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
405
406 if (stateTrue && !stateFalse) {
407 ExplodedNode *N = C.generateSink(stateTrue);
408 if (!N)
409 return;
410
411 const char *description = (FuncII == Retain)
412 ? "Null pointer argument in call to CFRetain"
413 : "Null pointer argument in call to CFRelease";
414
415 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
416 report->addRange(Arg->getSourceRange());
417 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
418 C.EmitReport(report);
419 return;
420 }
421
422 // From here on, we know the argument is non-null.
423 C.addTransition(stateFalse);
424 }
425
426 //===----------------------------------------------------------------------===//
427 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
428 //===----------------------------------------------------------------------===//
429
430 namespace {
431 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
432 mutable Selector releaseS;
433 mutable Selector retainS;
434 mutable Selector autoreleaseS;
435 mutable Selector drainS;
436 mutable llvm::OwningPtr<BugType> BT;
437
438 public:
439 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
440 };
441 }
442
checkPreObjCMessage(ObjCMessage msg,CheckerContext & C) const443 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
444 CheckerContext &C) const {
445
446 if (!BT) {
447 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
448 "instance"));
449
450 ASTContext &Ctx = C.getASTContext();
451 releaseS = GetNullarySelector("release", Ctx);
452 retainS = GetNullarySelector("retain", Ctx);
453 autoreleaseS = GetNullarySelector("autorelease", Ctx);
454 drainS = GetNullarySelector("drain", Ctx);
455 }
456
457 if (msg.isInstanceMessage())
458 return;
459 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
460 assert(Class);
461
462 Selector S = msg.getSelector();
463 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
464 return;
465
466 if (ExplodedNode *N = C.generateNode()) {
467 llvm::SmallString<200> buf;
468 llvm::raw_svector_ostream os(buf);
469
470 os << "The '" << S.getAsString() << "' message should be sent to instances "
471 "of class '" << Class->getName()
472 << "' and not the class directly";
473
474 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
475 report->addRange(msg.getSourceRange());
476 C.EmitReport(report);
477 }
478 }
479
480 //===----------------------------------------------------------------------===//
481 // Check for passing non-Objective-C types to variadic methods that expect
482 // only Objective-C types.
483 //===----------------------------------------------------------------------===//
484
485 namespace {
486 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
487 mutable Selector arrayWithObjectsS;
488 mutable Selector dictionaryWithObjectsAndKeysS;
489 mutable Selector setWithObjectsS;
490 mutable Selector initWithObjectsS;
491 mutable Selector initWithObjectsAndKeysS;
492 mutable llvm::OwningPtr<BugType> BT;
493
494 bool isVariadicMessage(const ObjCMessage &msg) const;
495
496 public:
497 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
498 };
499 }
500
501 /// isVariadicMessage - Returns whether the given message is a variadic message,
502 /// where all arguments must be Objective-C types.
503 bool
isVariadicMessage(const ObjCMessage & msg) const504 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
505 const ObjCMethodDecl *MD = msg.getMethodDecl();
506
507 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
508 return false;
509
510 Selector S = msg.getSelector();
511
512 if (msg.isInstanceMessage()) {
513 // FIXME: Ideally we'd look at the receiver interface here, but that's not
514 // useful for init, because alloc returns 'id'. In theory, this could lead
515 // to false positives, for example if there existed a class that had an
516 // initWithObjects: implementation that does accept non-Objective-C pointer
517 // types, but the chance of that happening is pretty small compared to the
518 // gains that this analysis gives.
519 const ObjCInterfaceDecl *Class = MD->getClassInterface();
520
521 // -[NSArray initWithObjects:]
522 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
523 S == initWithObjectsS)
524 return true;
525
526 // -[NSDictionary initWithObjectsAndKeys:]
527 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
528 S == initWithObjectsAndKeysS)
529 return true;
530
531 // -[NSSet initWithObjects:]
532 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
533 S == initWithObjectsS)
534 return true;
535 } else {
536 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
537
538 // -[NSArray arrayWithObjects:]
539 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
540 S == arrayWithObjectsS)
541 return true;
542
543 // -[NSDictionary dictionaryWithObjectsAndKeys:]
544 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
545 S == dictionaryWithObjectsAndKeysS)
546 return true;
547
548 // -[NSSet setWithObjects:]
549 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
550 S == setWithObjectsS)
551 return true;
552 }
553
554 return false;
555 }
556
checkPreObjCMessage(ObjCMessage msg,CheckerContext & C) const557 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
558 CheckerContext &C) const {
559 if (!BT) {
560 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
561 "Objective-C pointer types"));
562
563 ASTContext &Ctx = C.getASTContext();
564 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
565 dictionaryWithObjectsAndKeysS =
566 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
567 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
568
569 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
570 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
571 }
572
573 if (!isVariadicMessage(msg))
574 return;
575
576 // We are not interested in the selector arguments since they have
577 // well-defined types, so the compiler will issue a warning for them.
578 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
579
580 // We're not interested in the last argument since it has to be nil or the
581 // compiler would have issued a warning for it elsewhere.
582 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
583
584 if (variadicArgsEnd <= variadicArgsBegin)
585 return;
586
587 // Verify that all arguments have Objective-C types.
588 llvm::Optional<ExplodedNode*> errorNode;
589 const GRState *state = C.getState();
590
591 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
592 QualType ArgTy = msg.getArgType(I);
593 if (ArgTy->isObjCObjectPointerType())
594 continue;
595
596 // Block pointers are treaded as Objective-C pointers.
597 if (ArgTy->isBlockPointerType())
598 continue;
599
600 // Ignore pointer constants.
601 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state)))
602 continue;
603
604 // Ignore pointer types annotated with 'NSObject' attribute.
605 if (C.getASTContext().isObjCNSObjectType(ArgTy))
606 continue;
607
608 // Ignore CF references, which can be toll-free bridged.
609 if (coreFoundation::isCFObjectRef(ArgTy))
610 continue;
611
612 // Generate only one error node to use for all bug reports.
613 if (!errorNode.hasValue()) {
614 errorNode = C.generateNode();
615 }
616
617 if (!errorNode.getValue())
618 continue;
619
620 llvm::SmallString<128> sbuf;
621 llvm::raw_svector_ostream os(sbuf);
622
623 if (const char *TypeName = GetReceiverNameType(msg))
624 os << "Argument to '" << TypeName << "' method '";
625 else
626 os << "Argument to method '";
627
628 os << msg.getSelector().getAsString()
629 << "' should be an Objective-C pointer type, not '"
630 << ArgTy.getAsString() << "'";
631
632 RangedBugReport *R = new RangedBugReport(*BT, os.str(),
633 errorNode.getValue());
634 R->addRange(msg.getArgSourceRange(I));
635 C.EmitReport(R);
636 }
637 }
638
639 //===----------------------------------------------------------------------===//
640 // Check registration.
641 //===----------------------------------------------------------------------===//
642
registerNilArgChecker(CheckerManager & mgr)643 void ento::registerNilArgChecker(CheckerManager &mgr) {
644 mgr.registerChecker<NilArgChecker>();
645 }
646
registerCFNumberCreateChecker(CheckerManager & mgr)647 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
648 mgr.registerChecker<CFNumberCreateChecker>();
649 }
650
registerCFRetainReleaseChecker(CheckerManager & mgr)651 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
652 mgr.registerChecker<CFRetainReleaseChecker>();
653 }
654
registerClassReleaseChecker(CheckerManager & mgr)655 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
656 mgr.registerChecker<ClassReleaseChecker>();
657 }
658
registerVariadicMethodTypeChecker(CheckerManager & mgr)659 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
660 mgr.registerChecker<VariadicMethodTypeChecker>();
661 }
662