1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
31 | #include "clang/AST/Attr.h" |
32 | #include "clang/AST/DeclObjC.h" |
33 | #include "clang/AST/StmtVisitor.h" |
34 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
35 | #include "clang/StaticAnalyzer/Core/Checker.h" |
36 | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
37 | #include "llvm/ADT/DenseMap.h" |
38 | #include "llvm/ADT/SetVector.h" |
39 | #include "llvm/ADT/SmallString.h" |
40 | |
41 | using namespace clang; |
42 | using namespace ento; |
43 | |
44 | namespace { |
45 | struct ChecksFilter { |
46 | |
47 | DefaultBool check_MissingInvalidationMethod; |
48 | |
49 | DefaultBool check_InstanceVariableInvalidation; |
50 | |
51 | CheckName checkName_MissingInvalidationMethod; |
52 | CheckName checkName_InstanceVariableInvalidation; |
53 | }; |
54 | |
55 | class IvarInvalidationCheckerImpl { |
56 | typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet; |
57 | typedef llvm::DenseMap<const ObjCMethodDecl*, |
58 | const ObjCIvarDecl*> MethToIvarMapTy; |
59 | typedef llvm::DenseMap<const ObjCPropertyDecl*, |
60 | const ObjCIvarDecl*> PropToIvarMapTy; |
61 | typedef llvm::DenseMap<const ObjCIvarDecl*, |
62 | const ObjCPropertyDecl*> IvarToPropMapTy; |
63 | |
64 | struct InvalidationInfo { |
65 | |
66 | bool IsInvalidated; |
67 | |
68 | |
69 | MethodSet InvalidationMethods; |
70 | |
71 | InvalidationInfo() : IsInvalidated(false) {} |
72 | void addInvalidationMethod(const ObjCMethodDecl *MD) { |
73 | InvalidationMethods.insert(MD); |
74 | } |
75 | |
76 | bool needsInvalidation() const { |
77 | return !InvalidationMethods.empty(); |
78 | } |
79 | |
80 | bool hasMethod(const ObjCMethodDecl *MD) { |
81 | if (IsInvalidated) |
82 | return true; |
83 | for (MethodSet::iterator I = InvalidationMethods.begin(), |
84 | E = InvalidationMethods.end(); I != E; ++I) { |
85 | if (*I == MD) { |
86 | IsInvalidated = true; |
87 | return true; |
88 | } |
89 | } |
90 | return false; |
91 | } |
92 | }; |
93 | |
94 | typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet; |
95 | |
96 | |
97 | |
98 | class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { |
99 | |
100 | IvarSet &IVars; |
101 | |
102 | |
103 | |
104 | bool &CalledAnotherInvalidationMethod; |
105 | |
106 | |
107 | const MethToIvarMapTy &PropertySetterToIvarMap; |
108 | |
109 | |
110 | const MethToIvarMapTy &PropertyGetterToIvarMap; |
111 | |
112 | |
113 | const PropToIvarMapTy &PropertyToIvarMap; |
114 | |
115 | |
116 | const ObjCMethodDecl *InvalidationMethod; |
117 | |
118 | ASTContext &Ctx; |
119 | |
120 | |
121 | const Expr *peel(const Expr *E) const; |
122 | |
123 | |
124 | bool isZero(const Expr *E) const; |
125 | |
126 | |
127 | void markInvalidated(const ObjCIvarDecl *Iv); |
128 | |
129 | |
130 | |
131 | void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); |
132 | |
133 | |
134 | |
135 | void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); |
136 | |
137 | |
138 | |
139 | void checkObjCMessageExpr(const ObjCMessageExpr *ME); |
140 | |
141 | |
142 | void check(const Expr *E); |
143 | |
144 | public: |
145 | MethodCrawler(IvarSet &InIVars, |
146 | bool &InCalledAnotherInvalidationMethod, |
147 | const MethToIvarMapTy &InPropertySetterToIvarMap, |
148 | const MethToIvarMapTy &InPropertyGetterToIvarMap, |
149 | const PropToIvarMapTy &InPropertyToIvarMap, |
150 | ASTContext &InCtx) |
151 | : IVars(InIVars), |
152 | CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod), |
153 | PropertySetterToIvarMap(InPropertySetterToIvarMap), |
154 | PropertyGetterToIvarMap(InPropertyGetterToIvarMap), |
155 | PropertyToIvarMap(InPropertyToIvarMap), |
156 | InvalidationMethod(nullptr), |
157 | Ctx(InCtx) {} |
158 | |
159 | void VisitStmt(const Stmt *S) { VisitChildren(S); } |
160 | |
161 | void VisitBinaryOperator(const BinaryOperator *BO); |
162 | |
163 | void VisitObjCMessageExpr(const ObjCMessageExpr *ME); |
164 | |
165 | void VisitChildren(const Stmt *S) { |
166 | for (const auto *Child : S->children()) { |
167 | if (Child) |
168 | this->Visit(Child); |
169 | if (CalledAnotherInvalidationMethod) |
170 | return; |
171 | } |
172 | } |
173 | }; |
174 | |
175 | |
176 | |
177 | |
178 | |
179 | static void containsInvalidationMethod(const ObjCContainerDecl *D, |
180 | InvalidationInfo &Out, |
181 | bool LookForPartial); |
182 | |
183 | |
184 | |
185 | static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars, |
186 | const ObjCIvarDecl **FirstIvarDecl); |
187 | |
188 | |
189 | |
190 | |
191 | static const ObjCIvarDecl *findPropertyBackingIvar( |
192 | const ObjCPropertyDecl *Prop, |
193 | const ObjCInterfaceDecl *InterfaceD, |
194 | IvarSet &TrackedIvars, |
195 | const ObjCIvarDecl **FirstIvarDecl); |
196 | |
197 | |
198 | static void printIvar(llvm::raw_svector_ostream &os, |
199 | const ObjCIvarDecl *IvarDecl, |
200 | const IvarToPropMapTy &IvarToPopertyMap); |
201 | |
202 | void reportNoInvalidationMethod(CheckName CheckName, |
203 | const ObjCIvarDecl *FirstIvarDecl, |
204 | const IvarToPropMapTy &IvarToPopertyMap, |
205 | const ObjCInterfaceDecl *InterfaceD, |
206 | bool MissingDeclaration) const; |
207 | |
208 | void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, |
209 | const IvarToPropMapTy &IvarToPopertyMap, |
210 | const ObjCMethodDecl *MethodD) const; |
211 | |
212 | AnalysisManager& Mgr; |
213 | BugReporter &BR; |
214 | |
215 | const ChecksFilter &Filter; |
216 | |
217 | public: |
218 | IvarInvalidationCheckerImpl(AnalysisManager& InMgr, |
219 | BugReporter &InBR, |
220 | const ChecksFilter &InFilter) : |
221 | Mgr (InMgr), BR(InBR), Filter(InFilter) {} |
222 | |
223 | void visit(const ObjCImplementationDecl *D) const; |
224 | }; |
225 | |
226 | static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) { |
227 | for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) { |
228 | if (!LookForPartial && |
229 | Ann->getAnnotation() == "objc_instance_variable_invalidator") |
230 | return true; |
231 | if (LookForPartial && |
232 | Ann->getAnnotation() == "objc_instance_variable_invalidator_partial") |
233 | return true; |
234 | } |
235 | return false; |
236 | } |
237 | |
238 | void IvarInvalidationCheckerImpl::containsInvalidationMethod( |
239 | const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) { |
240 | |
241 | if (!D) |
242 | return; |
243 | |
244 | (D)", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp", 244, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(!isa<ObjCImplementationDecl>(D)); |
245 | |
246 | |
247 | |
248 | for (const auto *MDI : D->methods()) |
249 | if (isInvalidationMethod(MDI, Partial)) |
250 | OutInfo.addInvalidationMethod( |
251 | cast<ObjCMethodDecl>(MDI->getCanonicalDecl())); |
252 | |
253 | |
254 | if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) { |
255 | |
256 | |
257 | for (const auto *I : InterfD->protocols()) |
258 | containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); |
259 | |
260 | |
261 | |
262 | for (const auto *Ext : InterfD->visible_extensions()) |
263 | containsInvalidationMethod(Ext, OutInfo, Partial); |
264 | |
265 | containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial); |
266 | return; |
267 | } |
268 | |
269 | |
270 | if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { |
271 | for (const auto *I : ProtD->protocols()) { |
272 | containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); |
273 | } |
274 | return; |
275 | } |
276 | } |
277 | |
278 | bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv, |
279 | IvarSet &TrackedIvars, |
280 | const ObjCIvarDecl **FirstIvarDecl) { |
281 | QualType IvQTy = Iv->getType(); |
282 | const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); |
283 | if (!IvTy) |
284 | return false; |
285 | const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); |
286 | |
287 | InvalidationInfo Info; |
288 | containsInvalidationMethod(IvInterf, Info, false); |
289 | if (Info.needsInvalidation()) { |
290 | const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl()); |
291 | TrackedIvars[I] = Info; |
292 | if (!*FirstIvarDecl) |
293 | *FirstIvarDecl = I; |
294 | return true; |
295 | } |
296 | return false; |
297 | } |
298 | |
299 | const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( |
300 | const ObjCPropertyDecl *Prop, |
301 | const ObjCInterfaceDecl *InterfaceD, |
302 | IvarSet &TrackedIvars, |
303 | const ObjCIvarDecl **FirstIvarDecl) { |
304 | const ObjCIvarDecl *IvarD = nullptr; |
305 | |
306 | |
307 | IvarD = Prop->getPropertyIvarDecl(); |
308 | |
309 | |
310 | if (IvarD && IvarD->getContainingInterface() == InterfaceD) { |
311 | if (TrackedIvars.count(IvarD)) { |
312 | return IvarD; |
313 | } |
314 | |
315 | if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl)) |
316 | return IvarD; |
317 | } |
318 | |
319 | |
320 | StringRef PropName = Prop->getIdentifier()->getName(); |
321 | for (IvarSet::const_iterator I = TrackedIvars.begin(), |
322 | E = TrackedIvars.end(); I != E; ++I) { |
323 | const ObjCIvarDecl *Iv = I->first; |
324 | StringRef IvarName = Iv->getName(); |
325 | |
326 | if (IvarName == PropName) |
327 | return Iv; |
328 | |
329 | SmallString<128> PropNameWithUnderscore; |
330 | { |
331 | llvm::raw_svector_ostream os(PropNameWithUnderscore); |
332 | os << '_' << PropName; |
333 | } |
334 | if (IvarName == PropNameWithUnderscore) |
335 | return Iv; |
336 | } |
337 | |
338 | |
339 | |
340 | |
341 | return nullptr; |
342 | } |
343 | |
344 | void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os, |
345 | const ObjCIvarDecl *IvarDecl, |
346 | const IvarToPropMapTy &IvarToPopertyMap) { |
347 | if (IvarDecl->getSynthesize()) { |
348 | const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl); |
349 | (0) . __assert_fail ("PD &&\"Do we synthesize ivars for something other than properties?\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp", 349, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(PD &&"Do we synthesize ivars for something other than properties?"); |
350 | os << "Property "<< PD->getName() << " "; |
351 | } else { |
352 | os << "Instance variable "<< IvarDecl->getName() << " "; |
353 | } |
354 | } |
355 | |
356 | |
357 | |
358 | void IvarInvalidationCheckerImpl:: |
359 | visit(const ObjCImplementationDecl *ImplD) const { |
360 | |
361 | IvarSet Ivars; |
362 | |
363 | |
364 | |
365 | const ObjCIvarDecl *FirstIvarDecl = nullptr; |
366 | const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface(); |
367 | |
368 | |
369 | ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD); |
370 | for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; |
371 | Iv= Iv->getNextIvar()) |
372 | trackIvar(Iv, Ivars, &FirstIvarDecl); |
373 | |
374 | |
375 | |
376 | MethToIvarMapTy PropSetterToIvarMap; |
377 | MethToIvarMapTy PropGetterToIvarMap; |
378 | PropToIvarMapTy PropertyToIvarMap; |
379 | IvarToPropMapTy IvarToPopertyMap; |
380 | |
381 | ObjCInterfaceDecl::PropertyMap PropMap; |
382 | ObjCInterfaceDecl::PropertyDeclOrder PropOrder; |
383 | InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); |
384 | |
385 | for (ObjCInterfaceDecl::PropertyMap::iterator |
386 | I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { |
387 | const ObjCPropertyDecl *PD = I->second; |
388 | if (PD->isClassProperty()) |
389 | continue; |
390 | |
391 | const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars, |
392 | &FirstIvarDecl); |
393 | if (!ID) |
394 | continue; |
395 | |
396 | |
397 | PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); |
398 | PropertyToIvarMap[PD] = ID; |
399 | IvarToPopertyMap[ID] = PD; |
400 | |
401 | |
402 | const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); |
403 | if (SetterD) { |
404 | SetterD = SetterD->getCanonicalDecl(); |
405 | PropSetterToIvarMap[SetterD] = ID; |
406 | } |
407 | |
408 | const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl(); |
409 | if (GetterD) { |
410 | GetterD = GetterD->getCanonicalDecl(); |
411 | PropGetterToIvarMap[GetterD] = ID; |
412 | } |
413 | } |
414 | |
415 | |
416 | if (Ivars.empty()) |
417 | return; |
418 | |
419 | |
420 | InvalidationInfo PartialInfo; |
421 | containsInvalidationMethod(InterfaceD, PartialInfo, true); |
422 | |
423 | |
424 | |
425 | bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; |
426 | for (MethodSet::iterator |
427 | I = PartialInfo.InvalidationMethods.begin(), |
428 | E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { |
429 | const ObjCMethodDecl *InterfD = *I; |
430 | |
431 | |
432 | const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), |
433 | InterfD->isInstanceMethod()); |
434 | if (D && D->hasBody()) { |
435 | AtImplementationContainsAtLeastOnePartialInvalidationMethod = true; |
436 | |
437 | bool CalledAnotherInvalidationMethod = false; |
438 | |
439 | MethodCrawler(Ivars, |
440 | CalledAnotherInvalidationMethod, |
441 | PropSetterToIvarMap, |
442 | PropGetterToIvarMap, |
443 | PropertyToIvarMap, |
444 | BR.getContext()).VisitStmt(D->getBody()); |
445 | |
446 | |
447 | if (CalledAnotherInvalidationMethod) |
448 | Ivars.clear(); |
449 | } |
450 | } |
451 | |
452 | |
453 | |
454 | if (Ivars.empty()) |
455 | return; |
456 | |
457 | |
458 | InvalidationInfo Info; |
459 | containsInvalidationMethod(InterfaceD, Info, false); |
460 | |
461 | |
462 | if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { |
463 | if (Filter.check_MissingInvalidationMethod) |
464 | reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod, |
465 | FirstIvarDecl, IvarToPopertyMap, InterfaceD, |
466 | true); |
467 | |
468 | |
469 | return; |
470 | } |
471 | |
472 | |
473 | |
474 | if (!Filter.check_InstanceVariableInvalidation) |
475 | return; |
476 | |
477 | |
478 | bool AtImplementationContainsAtLeastOneInvalidationMethod = false; |
479 | for (MethodSet::iterator I = Info.InvalidationMethods.begin(), |
480 | E = Info.InvalidationMethods.end(); I != E; ++I) { |
481 | const ObjCMethodDecl *InterfD = *I; |
482 | |
483 | |
484 | const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), |
485 | InterfD->isInstanceMethod()); |
486 | if (D && D->hasBody()) { |
487 | AtImplementationContainsAtLeastOneInvalidationMethod = true; |
488 | |
489 | |
490 | IvarSet IvarsI = Ivars; |
491 | |
492 | bool CalledAnotherInvalidationMethod = false; |
493 | MethodCrawler(IvarsI, |
494 | CalledAnotherInvalidationMethod, |
495 | PropSetterToIvarMap, |
496 | PropGetterToIvarMap, |
497 | PropertyToIvarMap, |
498 | BR.getContext()).VisitStmt(D->getBody()); |
499 | |
500 | |
501 | if (CalledAnotherInvalidationMethod) |
502 | continue; |
503 | |
504 | |
505 | for (IvarSet::const_iterator |
506 | I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I) |
507 | reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D); |
508 | } |
509 | } |
510 | |
511 | |
512 | if (!AtImplementationContainsAtLeastOneInvalidationMethod) { |
513 | if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { |
514 | |
515 | |
516 | for (IvarSet::const_iterator |
517 | I = Ivars.begin(), E = Ivars.end(); I != E; ++I) |
518 | reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr); |
519 | } else { |
520 | |
521 | reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation, |
522 | FirstIvarDecl, IvarToPopertyMap, InterfaceD, |
523 | false); |
524 | } |
525 | } |
526 | } |
527 | |
528 | void IvarInvalidationCheckerImpl::reportNoInvalidationMethod( |
529 | CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl, |
530 | const IvarToPropMapTy &IvarToPopertyMap, |
531 | const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const { |
532 | SmallString<128> sbuf; |
533 | llvm::raw_svector_ostream os(sbuf); |
534 | assert(FirstIvarDecl); |
535 | printIvar(os, FirstIvarDecl, IvarToPopertyMap); |
536 | os << "needs to be invalidated; "; |
537 | if (MissingDeclaration) |
538 | os << "no invalidation method is declared for "; |
539 | else |
540 | os << "no invalidation method is defined in the @implementation for "; |
541 | os << InterfaceD->getName(); |
542 | |
543 | PathDiagnosticLocation IvarDecLocation = |
544 | PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager()); |
545 | |
546 | BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation", |
547 | categories::CoreFoundationObjectiveC, os.str(), |
548 | IvarDecLocation); |
549 | } |
550 | |
551 | void IvarInvalidationCheckerImpl:: |
552 | reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, |
553 | const IvarToPropMapTy &IvarToPopertyMap, |
554 | const ObjCMethodDecl *MethodD) const { |
555 | SmallString<128> sbuf; |
556 | llvm::raw_svector_ostream os(sbuf); |
557 | printIvar(os, IvarD, IvarToPopertyMap); |
558 | os << "needs to be invalidated or set to nil"; |
559 | if (MethodD) { |
560 | PathDiagnosticLocation MethodDecLocation = |
561 | PathDiagnosticLocation::createEnd(MethodD->getBody(), |
562 | BR.getSourceManager(), |
563 | Mgr.getAnalysisDeclContext(MethodD)); |
564 | BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation, |
565 | "Incomplete invalidation", |
566 | categories::CoreFoundationObjectiveC, os.str(), |
567 | MethodDecLocation); |
568 | } else { |
569 | BR.EmitBasicReport( |
570 | IvarD, Filter.checkName_InstanceVariableInvalidation, |
571 | "Incomplete invalidation", categories::CoreFoundationObjectiveC, |
572 | os.str(), |
573 | PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager())); |
574 | } |
575 | } |
576 | |
577 | void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( |
578 | const ObjCIvarDecl *Iv) { |
579 | IvarSet::iterator I = IVars.find(Iv); |
580 | if (I != IVars.end()) { |
581 | |
582 | |
583 | |
584 | if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod)) |
585 | IVars.erase(I); |
586 | } |
587 | } |
588 | |
589 | const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const { |
590 | E = E->IgnoreParenCasts(); |
591 | if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) |
592 | E = POE->getSyntacticForm()->IgnoreParenCasts(); |
593 | if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) |
594 | E = OVE->getSourceExpr()->IgnoreParenCasts(); |
595 | return E; |
596 | } |
597 | |
598 | void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr( |
599 | const ObjCIvarRefExpr *IvarRef) { |
600 | if (const Decl *D = IvarRef->getDecl()) |
601 | markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); |
602 | } |
603 | |
604 | void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr( |
605 | const ObjCMessageExpr *ME) { |
606 | const ObjCMethodDecl *MD = ME->getMethodDecl(); |
607 | if (MD) { |
608 | MD = MD->getCanonicalDecl(); |
609 | MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD); |
610 | if (IvI != PropertyGetterToIvarMap.end()) |
611 | markInvalidated(IvI->second); |
612 | } |
613 | } |
614 | |
615 | void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr( |
616 | const ObjCPropertyRefExpr *PA) { |
617 | |
618 | if (PA->isExplicitProperty()) { |
619 | const ObjCPropertyDecl *PD = PA->getExplicitProperty(); |
620 | if (PD) { |
621 | PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); |
622 | PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD); |
623 | if (IvI != PropertyToIvarMap.end()) |
624 | markInvalidated(IvI->second); |
625 | return; |
626 | } |
627 | } |
628 | |
629 | if (PA->isImplicitProperty()) { |
630 | const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); |
631 | if (MD) { |
632 | MD = MD->getCanonicalDecl(); |
633 | MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD); |
634 | if (IvI != PropertyGetterToIvarMap.end()) |
635 | markInvalidated(IvI->second); |
636 | return; |
637 | } |
638 | } |
639 | } |
640 | |
641 | bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const { |
642 | E = peel(E); |
643 | |
644 | return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull) |
645 | != Expr::NPCK_NotNull); |
646 | } |
647 | |
648 | void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) { |
649 | E = peel(E); |
650 | |
651 | if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { |
652 | checkObjCIvarRefExpr(IvarRef); |
653 | return; |
654 | } |
655 | |
656 | if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) { |
657 | checkObjCPropertyRefExpr(PropRef); |
658 | return; |
659 | } |
660 | |
661 | if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) { |
662 | checkObjCMessageExpr(MsgExpr); |
663 | return; |
664 | } |
665 | } |
666 | |
667 | void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator( |
668 | const BinaryOperator *BO) { |
669 | VisitStmt(BO); |
670 | |
671 | |
672 | |
673 | BinaryOperatorKind Opcode = BO->getOpcode(); |
674 | if (Opcode != BO_Assign && |
675 | Opcode != BO_EQ && |
676 | Opcode != BO_NE) |
677 | return; |
678 | |
679 | if (isZero(BO->getRHS())) { |
680 | check(BO->getLHS()); |
681 | return; |
682 | } |
683 | |
684 | if (Opcode != BO_Assign && isZero(BO->getLHS())) { |
685 | check(BO->getRHS()); |
686 | return; |
687 | } |
688 | } |
689 | |
690 | void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr( |
691 | const ObjCMessageExpr *ME) { |
692 | const ObjCMethodDecl *MD = ME->getMethodDecl(); |
693 | const Expr *Receiver = ME->getInstanceReceiver(); |
694 | |
695 | |
696 | if (Receiver && isInvalidationMethod(MD, false)) |
697 | if (Receiver->isObjCSelfExpr()) { |
698 | CalledAnotherInvalidationMethod = true; |
699 | return; |
700 | } |
701 | |
702 | |
703 | if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) { |
704 | MD = MD->getCanonicalDecl(); |
705 | MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD); |
706 | if (IvI != PropertySetterToIvarMap.end()) { |
707 | markInvalidated(IvI->second); |
708 | return; |
709 | } |
710 | } |
711 | |
712 | |
713 | if (Receiver) { |
714 | InvalidationMethod = MD; |
715 | check(Receiver->IgnoreParenCasts()); |
716 | InvalidationMethod = nullptr; |
717 | } |
718 | |
719 | VisitStmt(ME); |
720 | } |
721 | } |
722 | |
723 | |
724 | namespace { |
725 | class IvarInvalidationChecker : |
726 | public Checker<check::ASTDecl<ObjCImplementationDecl> > { |
727 | public: |
728 | ChecksFilter Filter; |
729 | public: |
730 | void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, |
731 | BugReporter &BR) const { |
732 | IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter); |
733 | Walker.visit(D); |
734 | } |
735 | }; |
736 | } |
737 | |
738 | void ento::registerIvarInvalidationModeling(CheckerManager &mgr) { |
739 | mgr.registerChecker<IvarInvalidationChecker>(); |
740 | } |
741 | |
742 | bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) { |
743 | return true; |
744 | } |
745 | |
746 | #define REGISTER_CHECKER(name) \ |
747 | void ento::register##name(CheckerManager &mgr) { \ |
748 | IvarInvalidationChecker *checker = \ |
749 | mgr.getChecker<IvarInvalidationChecker>(); \ |
750 | checker->Filter.check_##name = true; \ |
751 | checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \ |
752 | } \ |
753 | \ |
754 | bool ento::shouldRegister##name(const LangOptions &LO) { \ |
755 | return true; \ |
756 | } |
757 | |
758 | REGISTER_CHECKER(InstanceVariableInvalidation) |
759 | REGISTER_CHECKER(MissingInvalidationMethod) |
760 | |