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