Coverage Report

Created: 2020-03-31 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
//  This checker implements annotation driven invalidation checking. If a class
10
//  contains a method annotated with 'objc_instance_variable_invalidator',
11
//  - (void) foo
12
//           __attribute__((annotate("objc_instance_variable_invalidator")));
13
//  all the "ivalidatable" instance variables of this class should be
14
//  invalidated. We call an instance variable ivalidatable if it is an object of
15
//  a class which contains an invalidation method. There could be multiple
16
//  methods annotated with such annotations per class, either one can be used
17
//  to invalidate the ivar. An ivar or property are considered to be
18
//  invalidated if they are being assigned 'nil' or an invalidation method has
19
//  been called on them. An invalidation method should either invalidate all
20
//  the ivars or call another invalidation method (on self).
21
//
22
//  Partial invalidor annotation allows to address cases when ivars are
23
//  invalidated by other methods, which might or might not be called from
24
//  the invalidation method. The checker checks that each invalidation
25
//  method and all the partial methods cumulatively invalidate all ivars.
26
//    __attribute__((annotate("objc_instance_variable_invalidator_partial")));
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
  /// Check for missing invalidation method declarations.
47
  DefaultBool check_MissingInvalidationMethod;
48
  /// Check that all ivars are invalidated.
49
  DefaultBool check_InstanceVariableInvalidation;
50
51
  CheckerNameRef checkName_MissingInvalidationMethod;
52
  CheckerNameRef 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
    /// Has the ivar been invalidated?
66
    bool IsInvalidated;
67
68
    /// The methods which can be used to invalidate the ivar.
69
    MethodSet InvalidationMethods;
70
71
216
    InvalidationInfo() : IsInvalidated(false) {}
72
236
    void addInvalidationMethod(const ObjCMethodDecl *MD) {
73
236
      InvalidationMethods.insert(MD);
74
236
    }
75
76
120
    bool needsInvalidation() const {
77
120
      return !InvalidationMethods.empty();
78
120
    }
79
80
26
    bool hasMethod(const ObjCMethodDecl *MD) {
81
26
      if (IsInvalidated)
82
0
        return true;
83
26
      for (MethodSet::iterator I = InvalidationMethods.begin(),
84
38
          E = InvalidationMethods.end(); I != E; 
++I12
) {
85
35
        if (*I == MD) {
86
23
          IsInvalidated = true;
87
23
          return true;
88
23
        }
89
35
      }
90
26
      
return false3
;
91
26
    }
92
  };
93
94
  typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
95
96
  /// Statement visitor, which walks the method body and flags the ivars
97
  /// referenced in it (either directly or via property).
98
  class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
99
    /// The set of Ivars which need to be invalidated.
100
    IvarSet &IVars;
101
102
    /// Flag is set as the result of a message send to another
103
    /// invalidation method.
104
    bool &CalledAnotherInvalidationMethod;
105
106
    /// Property setter to ivar mapping.
107
    const MethToIvarMapTy &PropertySetterToIvarMap;
108
109
    /// Property getter to ivar mapping.
110
    const MethToIvarMapTy &PropertyGetterToIvarMap;
111
112
    /// Property to ivar mapping.
113
    const PropToIvarMapTy &PropertyToIvarMap;
114
115
    /// The invalidation method being currently processed.
116
    const ObjCMethodDecl *InvalidationMethod;
117
118
    ASTContext &Ctx;
119
120
    /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
121
    const Expr *peel(const Expr *E) const;
122
123
    /// Does this expression represent zero: '0'?
124
    bool isZero(const Expr *E) const;
125
126
    /// Mark the given ivar as invalidated.
127
    void markInvalidated(const ObjCIvarDecl *Iv);
128
129
    /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
130
    /// invalidated.
131
    void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
132
133
    /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
134
    /// it as invalidated.
135
    void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
136
137
    /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
138
    /// if yes, marks it as invalidated.
139
    void checkObjCMessageExpr(const ObjCMessageExpr *ME);
140
141
    /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
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
25
      Ctx(InCtx) {}
158
159
415
    void VisitStmt(const Stmt *S) { VisitChildren(S); }
160
161
    void VisitBinaryOperator(const BinaryOperator *BO);
162
163
    void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
164
165
415
    void VisitChildren(const Stmt *S) {
166
415
      for (const auto *Child : S->children()) {
167
408
        if (Child)
168
408
          this->Visit(Child);
169
408
        if (CalledAnotherInvalidationMethod)
170
3
          return;
171
408
      }
172
415
    }
173
  };
174
175
  /// Check if the any of the methods inside the interface are annotated with
176
  /// the invalidation annotation, update the IvarInfo accordingly.
177
  /// \param LookForPartial is set when we are searching for partial
178
  ///        invalidators.
179
  static void containsInvalidationMethod(const ObjCContainerDecl *D,
180
                                         InvalidationInfo &Out,
181
                                         bool LookForPartial);
182
183
  /// Check if ivar should be tracked and add to TrackedIvars if positive.
184
  /// Returns true if ivar should be tracked.
185
  static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
186
                        const ObjCIvarDecl **FirstIvarDecl);
187
188
  /// Given the property declaration, and the list of tracked ivars, finds
189
  /// the ivar backing the property when possible. Returns '0' when no such
190
  /// ivar could be found.
191
  static const ObjCIvarDecl *findPropertyBackingIvar(
192
      const ObjCPropertyDecl *Prop,
193
      const ObjCInterfaceDecl *InterfaceD,
194
      IvarSet &TrackedIvars,
195
      const ObjCIvarDecl **FirstIvarDecl);
196
197
  /// Print ivar name or the property if the given ivar backs a property.
198
  static void printIvar(llvm::raw_svector_ostream &os,
199
                        const ObjCIvarDecl *IvarDecl,
200
                        const IvarToPropMapTy &IvarToPopertyMap);
201
202
  void reportNoInvalidationMethod(CheckerNameRef 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
  /// Filter on the checks performed.
215
  const ChecksFilter &Filter;
216
217
public:
218
  IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
219
                              BugReporter &InBR,
220
                              const ChecksFilter &InFilter) :
221
28
    Mgr (InMgr), BR(InBR), Filter(InFilter) {}
222
223
  void visit(const ObjCImplementationDecl *D) const;
224
};
225
226
1.48k
static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
227
1.48k
  for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
228
324
    if (!LookForPartial &&
229
324
        
Ann->getAnnotation() == "objc_instance_variable_invalidator"262
)
230
244
      return true;
231
80
    if (LookForPartial &&
232
80
        
Ann->getAnnotation() == "objc_instance_variable_invalidator_partial"62
)
233
18
      return true;
234
80
  }
235
1.48k
  
return false1.22k
;
236
1.48k
}
237
238
void IvarInvalidationCheckerImpl::containsInvalidationMethod(
239
938
    const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
240
938
241
938
  if (!D)
242
138
    return;
243
800
244
800
  assert(!isa<ObjCImplementationDecl>(D));
245
800
  // TODO: Cache the results.
246
800
247
800
  // Check all methods.
248
800
  for (const auto *MDI : D->methods())
249
1.42k
    if (isInvalidationMethod(MDI, Partial))
250
236
      OutInfo.addInvalidationMethod(
251
236
          cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
252
800
253
800
  // If interface, check all parent protocols and super.
254
800
  if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
255
276
256
276
    // Visit all protocols.
257
276
    for (const auto *I : InterfD->protocols())
258
322
      containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
259
276
260
276
    // Visit all categories in case the invalidation method is declared in
261
276
    // a category.
262
276
    for (const auto *Ext : InterfD->visible_extensions())
263
6
      containsInvalidationMethod(Ext, OutInfo, Partial);
264
276
265
276
    containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
266
276
    return;
267
276
  }
268
524
269
524
  // If protocol, check all parent protocols.
270
524
  if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
271
518
    for (const auto *I : ProtD->protocols()) {
272
196
      containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
273
196
    }
274
518
    return;
275
518
  }
276
524
}
277
278
bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
279
                                        IvarSet &TrackedIvars,
280
94
                                        const ObjCIvarDecl **FirstIvarDecl) {
281
94
  QualType IvQTy = Iv->getType();
282
94
  const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
283
94
  if (!IvTy)
284
0
    return false;
285
94
  const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
286
94
287
94
  InvalidationInfo Info;
288
94
  containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
289
94
  if (Info.needsInvalidation()) {
290
78
    const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
291
78
    TrackedIvars[I] = Info;
292
78
    if (!*FirstIvarDecl)
293
24
      *FirstIvarDecl = I;
294
78
    return true;
295
78
  }
296
16
  return false;
297
16
}
298
299
const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
300
                        const ObjCPropertyDecl *Prop,
301
                        const ObjCInterfaceDecl *InterfaceD,
302
                        IvarSet &TrackedIvars,
303
36
                        const ObjCIvarDecl **FirstIvarDecl) {
304
36
  const ObjCIvarDecl *IvarD = nullptr;
305
36
306
36
  // Lookup for the synthesized case.
307
36
  IvarD = Prop->getPropertyIvarDecl();
308
36
  // We only track the ivars/properties that are defined in the current
309
36
  // class (not the parent).
310
36
  if (IvarD && 
IvarD->getContainingInterface() == InterfaceD34
) {
311
32
    if (TrackedIvars.count(IvarD)) {
312
26
      return IvarD;
313
26
    }
314
6
    // If the ivar is synthesized we still want to track it.
315
6
    if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
316
0
      return IvarD;
317
10
  }
318
10
319
10
  // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
320
10
  StringRef PropName = Prop->getIdentifier()->getName();
321
10
  for (IvarSet::const_iterator I = TrackedIvars.begin(),
322
144
                               E = TrackedIvars.end(); I != E; 
++I134
) {
323
136
    const ObjCIvarDecl *Iv = I->first;
324
136
    StringRef IvarName = Iv->getName();
325
136
326
136
    if (IvarName == PropName)
327
0
      return Iv;
328
136
329
136
    SmallString<128> PropNameWithUnderscore;
330
136
    {
331
136
      llvm::raw_svector_ostream os(PropNameWithUnderscore);
332
136
      os << '_' << PropName;
333
136
    }
334
136
    if (IvarName == PropNameWithUnderscore)
335
2
      return Iv;
336
136
  }
337
10
338
10
  // Note, this is a possible source of false positives. We could look at the
339
10
  // getter implementation to find the ivar when its name is not derived from
340
10
  // the property name.
341
10
  
return nullptr8
;
342
10
}
343
344
void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
345
                                      const ObjCIvarDecl *IvarDecl,
346
33
                                      const IvarToPropMapTy &IvarToPopertyMap) {
347
33
  if (IvarDecl->getSynthesize()) {
348
4
    const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
349
4
    assert(PD &&"Do we synthesize ivars for something other than properties?");
350
4
    os << "Property "<< PD->getName() << " ";
351
29
  } else {
352
29
    os << "Instance variable "<< IvarDecl->getName() << " ";
353
29
  }
354
33
}
355
356
// Check that the invalidatable interfaces with ivars/properties implement the
357
// invalidation methods.
358
void IvarInvalidationCheckerImpl::
359
28
visit(const ObjCImplementationDecl *ImplD) const {
360
28
  // Collect all ivars that need cleanup.
361
28
  IvarSet Ivars;
362
28
  // Record the first Ivar needing invalidation; used in reporting when only
363
28
  // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
364
28
  // deterministic output.
365
28
  const ObjCIvarDecl *FirstIvarDecl = nullptr;
366
28
  const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
367
28
368
28
  // Collect ivars declared in this class, its extensions and its implementation
369
28
  ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
370
116
  for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
371
88
       Iv= Iv->getNextIvar())
372
88
    trackIvar(Iv, Ivars, &FirstIvarDecl);
373
28
374
28
  // Construct Property/Property Accessor to Ivar maps to assist checking if an
375
28
  // ivar which is backing a property has been reset.
376
28
  MethToIvarMapTy PropSetterToIvarMap;
377
28
  MethToIvarMapTy PropGetterToIvarMap;
378
28
  PropToIvarMapTy PropertyToIvarMap;
379
28
  IvarToPropMapTy IvarToPopertyMap;
380
28
381
28
  ObjCInterfaceDecl::PropertyMap PropMap;
382
28
  ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
383
28
  InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
384
28
385
28
  for (ObjCInterfaceDecl::PropertyMap::iterator
386
64
      I = PropMap.begin(), E = PropMap.end(); I != E; 
++I36
) {
387
36
    const ObjCPropertyDecl *PD = I->second;
388
36
    if (PD->isClassProperty())
389
0
      continue;
390
36
391
36
    const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
392
36
                                                     &FirstIvarDecl);
393
36
    if (!ID)
394
8
      continue;
395
28
396
28
    // Store the mappings.
397
28
    PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
398
28
    PropertyToIvarMap[PD] = ID;
399
28
    IvarToPopertyMap[ID] = PD;
400
28
401
28
    // Find the setter and the getter.
402
28
    const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
403
28
    if (SetterD) {
404
26
      SetterD = SetterD->getCanonicalDecl();
405
26
      PropSetterToIvarMap[SetterD] = ID;
406
26
    }
407
28
408
28
    const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
409
28
    if (GetterD) {
410
28
      GetterD = GetterD->getCanonicalDecl();
411
28
      PropGetterToIvarMap[GetterD] = ID;
412
28
    }
413
28
  }
414
28
415
28
  // If no ivars need invalidation, there is nothing to check here.
416
28
  if (Ivars.empty())
417
4
    return;
418
24
419
24
  // Find all partial invalidation methods.
420
24
  InvalidationInfo PartialInfo;
421
24
  containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
422
24
423
24
  // Remove ivars invalidated by the partial invalidation methods. They do not
424
24
  // need to be invalidated in the regular invalidation methods.
425
24
  bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
426
24
  for (MethodSet::iterator
427
24
      I = PartialInfo.InvalidationMethods.begin(),
428
42
      E = PartialInfo.InvalidationMethods.end(); I != E; 
++I18
) {
429
18
    const ObjCMethodDecl *InterfD = *I;
430
18
431
18
    // Get the corresponding method in the @implementation.
432
18
    const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
433
18
                                               InterfD->isInstanceMethod());
434
18
    if (D && 
D->hasBody()16
) {
435
16
      AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
436
16
437
16
      bool CalledAnotherInvalidationMethod = false;
438
16
      // The MethodCrowler is going to remove the invalidated ivars.
439
16
      MethodCrawler(Ivars,
440
16
                    CalledAnotherInvalidationMethod,
441
16
                    PropSetterToIvarMap,
442
16
                    PropGetterToIvarMap,
443
16
                    PropertyToIvarMap,
444
16
                    BR.getContext()).VisitStmt(D->getBody());
445
16
      // If another invalidation method was called, trust that full invalidation
446
16
      // has occurred.
447
16
      if (CalledAnotherInvalidationMethod)
448
2
        Ivars.clear();
449
16
    }
450
18
  }
451
24
452
24
  // If all ivars have been invalidated by partial invalidators, there is
453
24
  // nothing to check here.
454
24
  if (Ivars.empty())
455
4
    return;
456
20
457
20
  // Find all invalidation methods in this @interface declaration and parents.
458
20
  InvalidationInfo Info;
459
20
  containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
460
20
461
20
  // Report an error in case none of the invalidation methods are declared.
462
20
  if (!Info.needsInvalidation() && 
!PartialInfo.needsInvalidation()6
) {
463
4
    if (Filter.check_MissingInvalidationMethod)
464
2
      reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
465
2
                                 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
466
2
                                 /*MissingDeclaration*/ true);
467
4
    // If there are no invalidation methods, there is no ivar validation work
468
4
    // to be done.
469
4
    return;
470
4
  }
471
16
472
16
  // Only check if Ivars are invalidated when InstanceVariableInvalidation
473
16
  // has been requested.
474
16
  if (!Filter.check_InstanceVariableInvalidation)
475
8
    return;
476
8
477
8
  // Check that all ivars are invalidated by the invalidation methods.
478
8
  bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
479
8
  for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
480
24
                           E = Info.InvalidationMethods.end(); I != E; 
++I16
) {
481
16
    const ObjCMethodDecl *InterfD = *I;
482
16
483
16
    // Get the corresponding method in the @implementation.
484
16
    const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
485
16
                                               InterfD->isInstanceMethod());
486
16
    if (D && 
D->hasBody()9
) {
487
9
      AtImplementationContainsAtLeastOneInvalidationMethod = true;
488
9
489
9
      // Get a copy of ivars needing invalidation.
490
9
      IvarSet IvarsI = Ivars;
491
9
492
9
      bool CalledAnotherInvalidationMethod = false;
493
9
      MethodCrawler(IvarsI,
494
9
                    CalledAnotherInvalidationMethod,
495
9
                    PropSetterToIvarMap,
496
9
                    PropGetterToIvarMap,
497
9
                    PropertyToIvarMap,
498
9
                    BR.getContext()).VisitStmt(D->getBody());
499
9
      // If another invalidation method was called, trust that full invalidation
500
9
      // has occurred.
501
9
      if (CalledAnotherInvalidationMethod)
502
1
        continue;
503
8
504
8
      // Warn on the ivars that were not invalidated by the method.
505
8
      for (IvarSet::const_iterator
506
34
          I = IvarsI.begin(), E = IvarsI.end(); I != E; 
++I26
)
507
26
        reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
508
8
    }
509
16
  }
510
8
511
8
  // Report an error in case none of the invalidation methods are implemented.
512
8
  if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
513
4
    if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
514
1
      // Warn on the ivars that were not invalidated by the prrtial
515
1
      // invalidation methods.
516
1
      for (IvarSet::const_iterator
517
3
           I = Ivars.begin(), E = Ivars.end(); I != E; 
++I2
)
518
2
        reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr);
519
3
    } else {
520
3
      // Otherwise, no invalidation methods were implemented.
521
3
      reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
522
3
                                 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
523
3
                                 /*MissingDeclaration*/ false);
524
3
    }
525
4
  }
526
8
}
527
528
void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
529
    CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl,
530
    const IvarToPropMapTy &IvarToPopertyMap,
531
5
    const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
532
5
  SmallString<128> sbuf;
533
5
  llvm::raw_svector_ostream os(sbuf);
534
5
  assert(FirstIvarDecl);
535
5
  printIvar(os, FirstIvarDecl, IvarToPopertyMap);
536
5
  os << "needs to be invalidated; ";
537
5
  if (MissingDeclaration)
538
2
    os << "no invalidation method is declared for ";
539
3
  else
540
3
    os << "no invalidation method is defined in the @implementation for ";
541
5
  os << InterfaceD->getName();
542
5
543
5
  PathDiagnosticLocation IvarDecLocation =
544
5
    PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
545
5
546
5
  BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
547
5
                     categories::CoreFoundationObjectiveC, os.str(),
548
5
                     IvarDecLocation);
549
5
}
550
551
void IvarInvalidationCheckerImpl::
552
reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
553
                            const IvarToPropMapTy &IvarToPopertyMap,
554
28
                            const ObjCMethodDecl *MethodD) const {
555
28
  SmallString<128> sbuf;
556
28
  llvm::raw_svector_ostream os(sbuf);
557
28
  printIvar(os, IvarD, IvarToPopertyMap);
558
28
  os << "needs to be invalidated or set to nil";
559
28
  if (MethodD) {
560
26
    PathDiagnosticLocation MethodDecLocation =
561
26
                           PathDiagnosticLocation::createEnd(MethodD->getBody(),
562
26
                           BR.getSourceManager(),
563
26
                           Mgr.getAnalysisDeclContext(MethodD));
564
26
    BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
565
26
                       "Incomplete invalidation",
566
26
                       categories::CoreFoundationObjectiveC, os.str(),
567
26
                       MethodDecLocation);
568
26
  } else {
569
2
    BR.EmitBasicReport(
570
2
        IvarD, Filter.checkName_InstanceVariableInvalidation,
571
2
        "Incomplete invalidation", categories::CoreFoundationObjectiveC,
572
2
        os.str(),
573
2
        PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
574
2
  }
575
28
}
576
577
void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
578
60
    const ObjCIvarDecl *Iv) {
579
60
  IvarSet::iterator I = IVars.find(Iv);
580
60
  if (I != IVars.end()) {
581
51
    // If InvalidationMethod is present, we are processing the message send and
582
51
    // should ensure we are invalidating with the appropriate method,
583
51
    // otherwise, we are processing setting to 'nil'.
584
51
    if (!InvalidationMethod || 
I->second.hasMethod(InvalidationMethod)26
)
585
48
      IVars.erase(I);
586
51
  }
587
60
}
588
589
96
const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
590
96
  E = E->IgnoreParenCasts();
591
96
  if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
592
6
    E = POE->getSyntacticForm()->IgnoreParenCasts();
593
96
  if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
594
24
    E = OVE->getSourceExpr()->IgnoreParenCasts();
595
96
  return E;
596
96
}
597
598
void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
599
24
    const ObjCIvarRefExpr *IvarRef) {
600
24
  if (const Decl *D = IvarRef->getDecl())
601
24
    markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
602
24
}
603
604
void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
605
6
    const ObjCMessageExpr *ME) {
606
6
  const ObjCMethodDecl *MD = ME->getMethodDecl();
607
6
  if (MD) {
608
6
    MD = MD->getCanonicalDecl();
609
6
    MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
610
6
    if (IvI != PropertyGetterToIvarMap.end())
611
6
      markInvalidated(IvI->second);
612
6
  }
613
6
}
614
615
void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
616
15
    const ObjCPropertyRefExpr *PA) {
617
15
618
15
  if (PA->isExplicitProperty()) {
619
15
    const ObjCPropertyDecl *PD = PA->getExplicitProperty();
620
15
    if (PD) {
621
15
      PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
622
15
      PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
623
15
      if (IvI != PropertyToIvarMap.end())
624
15
        markInvalidated(IvI->second);
625
15
      return;
626
15
    }
627
0
  }
628
0
629
0
  if (PA->isImplicitProperty()) {
630
0
    const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
631
0
    if (MD) {
632
0
      MD = MD->getCanonicalDecl();
633
0
      MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
634
0
      if (IvI != PropertyGetterToIvarMap.end())
635
0
        markInvalidated(IvI->second);
636
0
      return;
637
0
    }
638
0
  }
639
0
}
640
641
35
bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
642
35
  E = peel(E);
643
35
644
35
  return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
645
35
           != Expr::NPCK_NotNull);
646
35
}
647
648
61
void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
649
61
  E = peel(E);
650
61
651
61
  if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
652
24
    checkObjCIvarRefExpr(IvarRef);
653
24
    return;
654
24
  }
655
37
656
37
  if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
657
15
    checkObjCPropertyRefExpr(PropRef);
658
15
    return;
659
15
  }
660
22
661
22
  if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
662
6
    checkObjCMessageExpr(MsgExpr);
663
6
    return;
664
6
  }
665
22
}
666
667
void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
668
19
    const BinaryOperator *BO) {
669
19
  VisitStmt(BO);
670
19
671
19
  // Do we assign/compare against zero? If yes, check the variable we are
672
19
  // assigning to.
673
19
  BinaryOperatorKind Opcode = BO->getOpcode();
674
19
  if (Opcode != BO_Assign &&
675
19
      
Opcode != BO_EQ3
&&
676
19
      
Opcode != BO_NE0
)
677
0
    return;
678
19
679
19
  if (isZero(BO->getRHS())) {
680
18
      check(BO->getLHS());
681
18
      return;
682
18
  }
683
1
684
1
  if (Opcode != BO_Assign && isZero(BO->getLHS())) {
685
1
    check(BO->getRHS());
686
1
    return;
687
1
  }
688
1
}
689
690
void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
691
63
  const ObjCMessageExpr *ME) {
692
63
  const ObjCMethodDecl *MD = ME->getMethodDecl();
693
63
  const Expr *Receiver = ME->getInstanceReceiver();
694
63
695
63
  // Stop if we are calling '[self invalidate]'.
696
63
  if (Receiver && 
isInvalidationMethod(MD, /*LookForPartial*/ false)60
)
697
26
    if (Receiver->isObjCSelfExpr()) {
698
3
      CalledAnotherInvalidationMethod = true;
699
3
      return;
700
3
    }
701
60
702
60
  // Check if we call a setter and set the property to 'nil'.
703
60
  if (MD && (ME->getNumArgs() == 1) && 
isZero(ME->getArg(0))15
) {
704
15
    MD = MD->getCanonicalDecl();
705
15
    MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
706
15
    if (IvI != PropertySetterToIvarMap.end()) {
707
15
      markInvalidated(IvI->second);
708
15
      return;
709
15
    }
710
45
  }
711
45
712
45
  // Check if we call the 'invalidation' routine on the ivar.
713
45
  if (Receiver) {
714
42
    InvalidationMethod = MD;
715
42
    check(Receiver->IgnoreParenCasts());
716
42
    InvalidationMethod = nullptr;
717
42
  }
718
45
719
45
  VisitStmt(ME);
720
45
}
721
} // end anonymous namespace
722
723
// Register the checkers.
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
28
                    BugReporter &BR) const {
732
28
    IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
733
28
    Walker.visit(D);
734
28
  }
735
};
736
} // end anonymous namespace
737
738
2
void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
739
2
  mgr.registerChecker<IvarInvalidationChecker>();
740
2
}
741
742
2
bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) {
743
2
  return true;
744
2
}
745
746
#define REGISTER_CHECKER(name)                                                 \
747
2
  void ento::register##name(CheckerManager &mgr) {                             \
748
2
    IvarInvalidationChecker *checker =                                         \
749
2
        mgr.getChecker<IvarInvalidationChecker>();                             \
750
2
    checker->Filter.check_##name = true;                                       \
751
2
    checker->Filter.checkName_##name = mgr.getCurrentCheckerName();            \
752
2
  }                                                                            \
clang::ento::registerInstanceVariableInvalidation(clang::ento::CheckerManager&)
Line
Count
Source
747
1
  void ento::register##name(CheckerManager &mgr) {                             \
748
1
    IvarInvalidationChecker *checker =                                         \
749
1
        mgr.getChecker<IvarInvalidationChecker>();                             \
750
1
    checker->Filter.check_##name = true;                                       \
751
1
    checker->Filter.checkName_##name = mgr.getCurrentCheckerName();            \
752
1
  }                                                                            \
clang::ento::registerMissingInvalidationMethod(clang::ento::CheckerManager&)
Line
Count
Source
747
1
  void ento::register##name(CheckerManager &mgr) {                             \
748
1
    IvarInvalidationChecker *checker =                                         \
749
1
        mgr.getChecker<IvarInvalidationChecker>();                             \
750
1
    checker->Filter.check_##name = true;                                       \
751
1
    checker->Filter.checkName_##name = mgr.getCurrentCheckerName();            \
752
1
  }                                                                            \
753
                                                                               \
754
2
  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
clang::ento::shouldRegisterInstanceVariableInvalidation(clang::ento::CheckerManager const&)
Line
Count
Source
754
1
  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
clang::ento::shouldRegisterMissingInvalidationMethod(clang::ento::CheckerManager const&)
Line
Count
Source
754
1
  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
755
756
REGISTER_CHECKER(InstanceVariableInvalidation)
757
REGISTER_CHECKER(MissingInvalidationMethod)