]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
MFC r244628:
[FreeBSD/stable/9.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / IvarInvalidationChecker.cpp
1 //=- IvarInvalidationChecker.cpp - -*- 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 checker implements annotation driven invalidation checking. If a class
11 //  contains a method annotated with 'objc_instance_variable_invalidator',
12 //  - (void) foo
13 //           __attribute__((annotate("objc_instance_variable_invalidator")));
14 //  all the "ivalidatable" instance variables of this class should be
15 //  invalidated. We call an instance variable ivalidatable if it is an object of
16 //  a class which contains an invalidation method. There could be multiple
17 //  methods annotated with such annotations per class, either one can be used
18 //  to invalidate the ivar. An ivar or property are considered to be
19 //  invalidated if they are being assigned 'nil' or an invalidation method has
20 //  been called on them. An invalidation method should either invalidate all
21 //  the ivars or call another invalidation method (on self).
22 //
23 //===----------------------------------------------------------------------===//
24
25 #include "ClangSACheckers.h"
26 #include "clang/StaticAnalyzer/Core/Checker.h"
27 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
29 #include "clang/AST/DeclObjC.h"
30 #include "clang/AST/StmtVisitor.h"
31 #include "llvm/ADT/DenseMap.h"
32 #include "llvm/ADT/SmallString.h"
33
34 using namespace clang;
35 using namespace ento;
36
37 namespace {
38 class IvarInvalidationChecker :
39   public Checker<check::ASTDecl<ObjCMethodDecl> > {
40
41   typedef llvm::DenseSet<const ObjCMethodDecl*> MethodSet;
42   typedef llvm::DenseMap<const ObjCMethodDecl*,
43                          const ObjCIvarDecl*> MethToIvarMapTy;
44   typedef llvm::DenseMap<const ObjCPropertyDecl*,
45                          const ObjCIvarDecl*> PropToIvarMapTy;
46   typedef llvm::DenseMap<const ObjCIvarDecl*,
47                          const ObjCPropertyDecl*> IvarToPropMapTy;
48
49
50   struct IvarInfo {
51     /// Has the ivar been invalidated?
52     bool IsInvalidated;
53
54     /// The methods which can be used to invalidate the ivar.
55     MethodSet InvalidationMethods;
56
57     IvarInfo() : IsInvalidated(false) {}
58     void addInvalidationMethod(const ObjCMethodDecl *MD) {
59       InvalidationMethods.insert(MD);
60     }
61
62     bool needsInvalidation() const {
63       return !InvalidationMethods.empty();
64     }
65
66     void markInvalidated() {
67       IsInvalidated = true;
68     }
69
70     bool markInvalidated(const ObjCMethodDecl *MD) {
71       if (IsInvalidated)
72         return true;
73       for (MethodSet::iterator I = InvalidationMethods.begin(),
74           E = InvalidationMethods.end(); I != E; ++I) {
75         if (*I == MD) {
76           IsInvalidated = true;
77           return true;
78         }
79       }
80       return false;
81     }
82
83     bool isInvalidated() const {
84       return IsInvalidated;
85     }
86   };
87
88   typedef llvm::DenseMap<const ObjCIvarDecl*, IvarInfo> IvarSet;
89
90   /// Statement visitor, which walks the method body and flags the ivars
91   /// referenced in it (either directly or via property).
92   class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
93     /// The set of Ivars which need to be invalidated.
94     IvarSet &IVars;
95
96     /// Flag is set as the result of a message send to another
97     /// invalidation method.
98     bool &CalledAnotherInvalidationMethod;
99
100     /// Property setter to ivar mapping.
101     const MethToIvarMapTy &PropertySetterToIvarMap;
102
103     /// Property getter to ivar mapping.
104     const MethToIvarMapTy &PropertyGetterToIvarMap;
105
106     /// Property to ivar mapping.
107     const PropToIvarMapTy &PropertyToIvarMap;
108
109     /// The invalidation method being currently processed.
110     const ObjCMethodDecl *InvalidationMethod;
111
112     ASTContext &Ctx;
113
114     /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
115     const Expr *peel(const Expr *E) const;
116
117     /// Does this expression represent zero: '0'?
118     bool isZero(const Expr *E) const;
119
120     /// Mark the given ivar as invalidated.
121     void markInvalidated(const ObjCIvarDecl *Iv);
122
123     /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
124     /// invalidated.
125     void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
126
127     /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
128     /// it as invalidated.
129     void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
130
131     /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
132     /// if yes, marks it as invalidated.
133     void checkObjCMessageExpr(const ObjCMessageExpr *ME);
134
135     /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
136     void check(const Expr *E);
137
138   public:
139     MethodCrawler(IvarSet &InIVars,
140                   bool &InCalledAnotherInvalidationMethod,
141                   const MethToIvarMapTy &InPropertySetterToIvarMap,
142                   const MethToIvarMapTy &InPropertyGetterToIvarMap,
143                   const PropToIvarMapTy &InPropertyToIvarMap,
144                   ASTContext &InCtx)
145     : IVars(InIVars),
146       CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
147       PropertySetterToIvarMap(InPropertySetterToIvarMap),
148       PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
149       PropertyToIvarMap(InPropertyToIvarMap),
150       InvalidationMethod(0),
151       Ctx(InCtx) {}
152
153     void VisitStmt(const Stmt *S) { VisitChildren(S); }
154
155     void VisitBinaryOperator(const BinaryOperator *BO);
156
157     void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
158
159     void VisitChildren(const Stmt *S) {
160       for (Stmt::const_child_range I = S->children(); I; ++I) {
161         if (*I)
162           this->Visit(*I);
163         if (CalledAnotherInvalidationMethod)
164           return;
165       }
166     }
167   };
168
169   /// Check if the any of the methods inside the interface are annotated with
170   /// the invalidation annotation, update the IvarInfo accordingly.
171   static void containsInvalidationMethod(const ObjCContainerDecl *D,
172                                          IvarInfo &Out);
173
174   /// Check if ivar should be tracked and add to TrackedIvars if positive.
175   /// Returns true if ivar should be tracked.
176   static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars);
177
178   /// Given the property declaration, and the list of tracked ivars, finds
179   /// the ivar backing the property when possible. Returns '0' when no such
180   /// ivar could be found.
181   static const ObjCIvarDecl *findPropertyBackingIvar(
182       const ObjCPropertyDecl *Prop,
183       const ObjCInterfaceDecl *InterfaceD,
184       IvarSet &TrackedIvars);
185
186 public:
187   void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
188                     BugReporter &BR) const;
189
190   // TODO: We are currently ignoring the ivars coming from class extensions.
191 };
192
193 static bool isInvalidationMethod(const ObjCMethodDecl *M) {
194   for (specific_attr_iterator<AnnotateAttr>
195        AI = M->specific_attr_begin<AnnotateAttr>(),
196        AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
197     const AnnotateAttr *Ann = *AI;
198     if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
199       return true;
200   }
201   return false;
202 }
203
204 void IvarInvalidationChecker::containsInvalidationMethod(
205     const ObjCContainerDecl *D, IvarInfo &OutInfo) {
206
207   // TODO: Cache the results.
208
209   if (!D)
210     return;
211
212   // Check all methods.
213   for (ObjCContainerDecl::method_iterator
214       I = D->meth_begin(),
215       E = D->meth_end(); I != E; ++I) {
216       const ObjCMethodDecl *MDI = *I;
217       if (isInvalidationMethod(MDI))
218         OutInfo.addInvalidationMethod(
219                                cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
220   }
221
222   // If interface, check all parent protocols and super.
223   // TODO: Visit all categories in case the invalidation method is declared in
224   // a category.
225   if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) {
226     for (ObjCInterfaceDecl::protocol_iterator
227         I = InterfaceD->protocol_begin(),
228         E = InterfaceD->protocol_end(); I != E; ++I) {
229       containsInvalidationMethod(*I, OutInfo);
230     }
231     containsInvalidationMethod(InterfaceD->getSuperClass(), OutInfo);
232     return;
233   }
234
235   // If protocol, check all parent protocols.
236   if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
237     for (ObjCInterfaceDecl::protocol_iterator
238         I = ProtD->protocol_begin(),
239         E = ProtD->protocol_end(); I != E; ++I) {
240       containsInvalidationMethod(*I, OutInfo);
241     }
242     return;
243   }
244
245   llvm_unreachable("One of the casts above should have succeeded.");
246 }
247
248 bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv,
249                                         IvarSet &TrackedIvars) {
250   QualType IvQTy = Iv->getType();
251   const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
252   if (!IvTy)
253     return false;
254   const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
255
256   IvarInfo Info;
257   containsInvalidationMethod(IvInterf, Info);
258   if (Info.needsInvalidation()) {
259     TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = Info;
260     return true;
261   }
262   return false;
263 }
264
265 const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
266                         const ObjCPropertyDecl *Prop,
267                         const ObjCInterfaceDecl *InterfaceD,
268                         IvarSet &TrackedIvars) {
269   const ObjCIvarDecl *IvarD = 0;
270
271   // Lookup for the synthesized case.
272   IvarD = Prop->getPropertyIvarDecl();
273   if (IvarD) {
274     if (TrackedIvars.count(IvarD)) {
275       return IvarD;
276     }
277     // If the ivar is synthesized we still want to track it.
278     if (trackIvar(IvarD, TrackedIvars))
279       return IvarD;
280   }
281
282   // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
283   StringRef PropName = Prop->getIdentifier()->getName();
284   for (IvarSet::const_iterator I = TrackedIvars.begin(),
285                                E = TrackedIvars.end(); I != E; ++I) {
286     const ObjCIvarDecl *Iv = I->first;
287     StringRef IvarName = Iv->getName();
288
289     if (IvarName == PropName)
290       return Iv;
291
292     SmallString<128> PropNameWithUnderscore;
293     {
294       llvm::raw_svector_ostream os(PropNameWithUnderscore);
295       os << '_' << PropName;
296     }
297     if (IvarName == PropNameWithUnderscore.str())
298       return Iv;
299   }
300
301   // Note, this is a possible source of false positives. We could look at the
302   // getter implementation to find the ivar when its name is not derived from
303   // the property name.
304   return 0;
305 }
306
307 void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
308                                           AnalysisManager& Mgr,
309                                           BugReporter &BR) const {
310   // We are only interested in checking the cleanup methods.
311   if (!D->hasBody() || !isInvalidationMethod(D))
312     return;
313
314   // Collect all ivars that need cleanup.
315   IvarSet Ivars;
316   const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
317
318   // Collect ivars declared in this class, its extensions and its implementation
319   ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
320   for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
321        Iv= Iv->getNextIvar())
322     trackIvar(Iv, Ivars);
323
324   // Construct Property/Property Accessor to Ivar maps to assist checking if an
325   // ivar which is backing a property has been reset.
326   MethToIvarMapTy PropSetterToIvarMap;
327   MethToIvarMapTy PropGetterToIvarMap;
328   PropToIvarMapTy PropertyToIvarMap;
329   IvarToPropMapTy IvarToPopertyMap;
330
331   ObjCInterfaceDecl::PropertyMap PropMap;
332   InterfaceD->collectPropertiesToImplement(PropMap);
333
334   for (ObjCInterfaceDecl::PropertyMap::iterator
335       I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
336     const ObjCPropertyDecl *PD = I->second;
337
338     const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars);
339     if (!ID) {
340       continue;
341     }
342
343     // Store the mappings.
344     PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
345     PropertyToIvarMap[PD] = ID;
346     IvarToPopertyMap[ID] = PD;
347
348     // Find the setter and the getter.
349     const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
350     if (SetterD) {
351       SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
352       PropSetterToIvarMap[SetterD] = ID;
353     }
354
355     const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
356     if (GetterD) {
357       GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
358       PropGetterToIvarMap[GetterD] = ID;
359     }
360   }
361
362
363   // Check which ivars have been invalidated in the method body.
364   bool CalledAnotherInvalidationMethod = false;
365   MethodCrawler(Ivars,
366                 CalledAnotherInvalidationMethod,
367                 PropSetterToIvarMap,
368                 PropGetterToIvarMap,
369                 PropertyToIvarMap,
370                 BR.getContext()).VisitStmt(D->getBody());
371
372   if (CalledAnotherInvalidationMethod)
373     return;
374
375   // Warn on the ivars that were not accessed by the method.
376   for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){
377     if (!I->second.isInvalidated()) {
378       const ObjCIvarDecl *IvarDecl = I->first;
379
380       PathDiagnosticLocation IvarDecLocation =
381           PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(),
382                                             Mgr.getAnalysisDeclContext(D));
383
384       SmallString<128> sbuf;
385       llvm::raw_svector_ostream os(sbuf);
386
387       // Construct the warning message.
388       if (IvarDecl->getSynthesize()) {
389         const ObjCPropertyDecl *PD = IvarToPopertyMap[IvarDecl];
390         assert(PD &&
391                "Do we synthesize ivars for something other than properties?");
392         os << "Property "<< PD->getName() <<
393               " needs to be invalidated or set to nil";
394       } else {
395         os << "Instance variable "<< IvarDecl->getName()
396              << " needs to be invalidated or set to nil";
397       }
398
399       BR.EmitBasicReport(D,
400           "Incomplete invalidation",
401           categories::CoreFoundationObjectiveC, os.str(),
402           IvarDecLocation);
403     }
404   }
405 }
406
407 void IvarInvalidationChecker::MethodCrawler::markInvalidated(
408     const ObjCIvarDecl *Iv) {
409   IvarSet::iterator I = IVars.find(Iv);
410   if (I != IVars.end()) {
411     // If InvalidationMethod is present, we are processing the message send and
412     // should ensure we are invalidating with the appropriate method,
413     // otherwise, we are processing setting to 'nil'.
414     if (InvalidationMethod)
415       I->second.markInvalidated(InvalidationMethod);
416     else
417       I->second.markInvalidated();
418   }
419 }
420
421 const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const {
422   E = E->IgnoreParenCasts();
423   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
424     E = POE->getSyntacticForm()->IgnoreParenCasts();
425   if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
426     E = OVE->getSourceExpr()->IgnoreParenCasts();
427   return E;
428 }
429
430 void IvarInvalidationChecker::MethodCrawler::checkObjCIvarRefExpr(
431     const ObjCIvarRefExpr *IvarRef) {
432   if (const Decl *D = IvarRef->getDecl())
433     markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
434 }
435
436 void IvarInvalidationChecker::MethodCrawler::checkObjCMessageExpr(
437     const ObjCMessageExpr *ME) {
438   const ObjCMethodDecl *MD = ME->getMethodDecl();
439   if (MD) {
440     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
441     MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
442     if (IvI != PropertyGetterToIvarMap.end())
443       markInvalidated(IvI->second);
444   }
445 }
446
447 void IvarInvalidationChecker::MethodCrawler::checkObjCPropertyRefExpr(
448     const ObjCPropertyRefExpr *PA) {
449
450   if (PA->isExplicitProperty()) {
451     const ObjCPropertyDecl *PD = PA->getExplicitProperty();
452     if (PD) {
453       PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
454       PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
455       if (IvI != PropertyToIvarMap.end())
456         markInvalidated(IvI->second);
457       return;
458     }
459   }
460
461   if (PA->isImplicitProperty()) {
462     const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
463     if (MD) {
464       MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
465       MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
466       if (IvI != PropertyGetterToIvarMap.end())
467         markInvalidated(IvI->second);
468       return;
469     }
470   }
471 }
472
473 bool IvarInvalidationChecker::MethodCrawler::isZero(const Expr *E) const {
474   E = peel(E);
475
476   return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
477            != Expr::NPCK_NotNull);
478 }
479
480 void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) {
481   E = peel(E);
482
483   if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
484     checkObjCIvarRefExpr(IvarRef);
485     return;
486   }
487
488   if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
489     checkObjCPropertyRefExpr(PropRef);
490     return;
491   }
492
493   if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
494     checkObjCMessageExpr(MsgExpr);
495     return;
496   }
497 }
498
499 void IvarInvalidationChecker::MethodCrawler::VisitBinaryOperator(
500     const BinaryOperator *BO) {
501   VisitStmt(BO);
502
503   if (BO->getOpcode() != BO_Assign)
504     return;
505
506   // Do we assign zero?
507   if (!isZero(BO->getRHS()))
508     return;
509
510   // Check the variable we are assigning to.
511   check(BO->getLHS());
512 }
513
514 void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
515     const ObjCMessageExpr *ME) {
516   const ObjCMethodDecl *MD = ME->getMethodDecl();
517   const Expr *Receiver = ME->getInstanceReceiver();
518
519   // Stop if we are calling '[self invalidate]'.
520   if (Receiver && isInvalidationMethod(MD))
521     if (Receiver->isObjCSelfExpr()) {
522       CalledAnotherInvalidationMethod = true;
523       return;
524     }
525
526   // Check if we call a setter and set the property to 'nil'.
527   if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
528     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
529     MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
530     if (IvI != PropertySetterToIvarMap.end()) {
531       markInvalidated(IvI->second);
532       return;
533     }
534   }
535
536   // Check if we call the 'invalidation' routine on the ivar.
537   if (Receiver) {
538     InvalidationMethod = MD;
539     check(Receiver->IgnoreParenCasts());
540     InvalidationMethod = 0;
541   }
542
543   VisitStmt(ME);
544 }
545 }
546
547 // Register the checker.
548 void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
549   mgr.registerChecker<IvarInvalidationChecker>();
550 }