Coverage Report

Created: 2023-09-30 09:22

/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/AST/Attr.h"
31
#include "clang/AST/DeclObjC.h"
32
#include "clang/AST/StmtVisitor.h"
33
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/STLExtras.h"
39
#include "llvm/ADT/SetVector.h"
40
#include "llvm/ADT/SmallString.h"
41
42
using namespace clang;
43
using namespace ento;
44
45
namespace {
46
struct ChecksFilter {
47
  /// Check for missing invalidation method declarations.
48
  bool check_MissingInvalidationMethod = false;
49
  /// Check that all ivars are invalidated.
50
  bool check_InstanceVariableInvalidation = false;
51
52
  CheckerNameRef checkName_MissingInvalidationMethod;
53
  CheckerNameRef checkName_InstanceVariableInvalidation;
54
};
55
56
class IvarInvalidationCheckerImpl {
57
  typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
58
  typedef llvm::DenseMap<const ObjCMethodDecl*,
59
                         const ObjCIvarDecl*> MethToIvarMapTy;
60
  typedef llvm::DenseMap<const ObjCPropertyDecl*,
61
                         const ObjCIvarDecl*> PropToIvarMapTy;
62
  typedef llvm::DenseMap<const ObjCIvarDecl*,
63
                         const ObjCPropertyDecl*> IvarToPropMapTy;
64
65
  struct InvalidationInfo {
66
    /// Has the ivar been invalidated?
67
    bool IsInvalidated = false;
68
69
    /// The methods which can be used to invalidate the ivar.
70
    MethodSet InvalidationMethods;
71
72
216
    InvalidationInfo() = default;
73
236
    void addInvalidationMethod(const ObjCMethodDecl *MD) {
74
236
      InvalidationMethods.insert(MD);
75
236
    }
76
77
120
    bool needsInvalidation() const {
78
120
      return !InvalidationMethods.empty();
79
120
    }
80
81
26
    bool hasMethod(const ObjCMethodDecl *MD) {
82
26
      if (IsInvalidated)
83
0
        return true;
84
35
      
for (const ObjCMethodDecl *Curr : InvalidationMethods)26
{
85
35
        if (Curr == MD) {
86
23
          IsInvalidated = true;
87
23
          return true;
88
23
        }
89
35
      }
90
3
      return false;
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
25
    : IVars(InIVars),
152
25
      CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
153
25
      PropertySetterToIvarMap(InPropertySetterToIvarMap),
154
25
      PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
155
25
      PropertyToIvarMap(InPropertyToIvarMap),
156
25
      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.22k
  return false;
236
1.48k
}
237
238
void IvarInvalidationCheckerImpl::containsInvalidationMethod(
239
938
    const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
240
241
938
  if (!D)
242
138
    return;
243
244
800
  assert(!isa<ObjCImplementationDecl>(D));
245
  // TODO: Cache the results.
246
247
  // 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
253
  // If interface, check all parent protocols and super.
254
800
  if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
255
256
    // Visit all protocols.
257
276
    for (const auto *I : InterfD->protocols())
258
322
      containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
259
260
    // Visit all categories in case the invalidation method is declared in
261
    // a category.
262
276
    for (const auto *Ext : InterfD->visible_extensions())
263
6
      containsInvalidationMethod(Ext, OutInfo, Partial);
264
265
276
    containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
266
276
    return;
267
276
  }
268
269
  // 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
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
94
}
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
306
  // Lookup for the synthesized case.
307
36
  IvarD = Prop->getPropertyIvarDecl();
308
  // We only track the ivars/properties that are defined in the current
309
  // class (not the parent).
310
36
  if (IvarD && 
IvarD->getContainingInterface() == InterfaceD34
) {
311
32
    if (TrackedIvars.count(IvarD)) {
312
26
      return IvarD;
313
26
    }
314
    // If the ivar is synthesized we still want to track it.
315
6
    if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
316
0
      return IvarD;
317
6
  }
318
319
  // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
320
10
  StringRef PropName = Prop->getIdentifier()->getName();
321
156
  for (const ObjCIvarDecl *Iv : llvm::make_first_range(TrackedIvars)) {
322
156
    StringRef IvarName = Iv->getName();
323
324
156
    if (IvarName == PropName)
325
0
      return Iv;
326
327
156
    SmallString<128> PropNameWithUnderscore;
328
156
    {
329
156
      llvm::raw_svector_ostream os(PropNameWithUnderscore);
330
156
      os << '_' << PropName;
331
156
    }
332
156
    if (IvarName == PropNameWithUnderscore)
333
2
      return Iv;
334
156
  }
335
336
  // Note, this is a possible source of false positives. We could look at the
337
  // getter implementation to find the ivar when its name is not derived from
338
  // the property name.
339
8
  return nullptr;
340
10
}
341
342
void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
343
                                      const ObjCIvarDecl *IvarDecl,
344
33
                                      const IvarToPropMapTy &IvarToPopertyMap) {
345
33
  if (IvarDecl->getSynthesize()) {
346
4
    const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
347
4
    assert(PD &&"Do we synthesize ivars for something other than properties?");
348
4
    os << "Property "<< PD->getName() << " ";
349
29
  } else {
350
29
    os << "Instance variable "<< IvarDecl->getName() << " ";
351
29
  }
352
33
}
353
354
// Check that the invalidatable interfaces with ivars/properties implement the
355
// invalidation methods.
356
void IvarInvalidationCheckerImpl::
357
28
visit(const ObjCImplementationDecl *ImplD) const {
358
  // Collect all ivars that need cleanup.
359
28
  IvarSet Ivars;
360
  // Record the first Ivar needing invalidation; used in reporting when only
361
  // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
362
  // deterministic output.
363
28
  const ObjCIvarDecl *FirstIvarDecl = nullptr;
364
28
  const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
365
366
  // Collect ivars declared in this class, its extensions and its implementation
367
28
  ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
368
116
  for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
369
88
       Iv= Iv->getNextIvar())
370
88
    trackIvar(Iv, Ivars, &FirstIvarDecl);
371
372
  // Construct Property/Property Accessor to Ivar maps to assist checking if an
373
  // ivar which is backing a property has been reset.
374
28
  MethToIvarMapTy PropSetterToIvarMap;
375
28
  MethToIvarMapTy PropGetterToIvarMap;
376
28
  PropToIvarMapTy PropertyToIvarMap;
377
28
  IvarToPropMapTy IvarToPopertyMap;
378
379
28
  ObjCInterfaceDecl::PropertyMap PropMap;
380
28
  InterfaceD->collectPropertiesToImplement(PropMap);
381
382
36
  for (const ObjCPropertyDecl *PD : llvm::make_second_range(PropMap)) {
383
36
    if (PD->isClassProperty())
384
0
      continue;
385
386
36
    const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
387
36
                                                     &FirstIvarDecl);
388
36
    if (!ID)
389
8
      continue;
390
391
    // Store the mappings.
392
28
    PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
393
28
    PropertyToIvarMap[PD] = ID;
394
28
    IvarToPopertyMap[ID] = PD;
395
396
    // Find the setter and the getter.
397
28
    const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
398
28
    if (SetterD) {
399
26
      SetterD = SetterD->getCanonicalDecl();
400
26
      PropSetterToIvarMap[SetterD] = ID;
401
26
    }
402
403
28
    const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
404
28
    if (GetterD) {
405
28
      GetterD = GetterD->getCanonicalDecl();
406
28
      PropGetterToIvarMap[GetterD] = ID;
407
28
    }
408
28
  }
409
410
  // If no ivars need invalidation, there is nothing to check here.
411
28
  if (Ivars.empty())
412
4
    return;
413
414
  // Find all partial invalidation methods.
415
24
  InvalidationInfo PartialInfo;
416
24
  containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
417
418
  // Remove ivars invalidated by the partial invalidation methods. They do not
419
  // need to be invalidated in the regular invalidation methods.
420
24
  bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
421
24
  for (const ObjCMethodDecl *InterfD : PartialInfo.InvalidationMethods) {
422
    // Get the corresponding method in the @implementation.
423
18
    const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
424
18
                                               InterfD->isInstanceMethod());
425
18
    if (D && 
D->hasBody()16
) {
426
16
      AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
427
428
16
      bool CalledAnotherInvalidationMethod = false;
429
      // The MethodCrowler is going to remove the invalidated ivars.
430
16
      MethodCrawler(Ivars,
431
16
                    CalledAnotherInvalidationMethod,
432
16
                    PropSetterToIvarMap,
433
16
                    PropGetterToIvarMap,
434
16
                    PropertyToIvarMap,
435
16
                    BR.getContext()).VisitStmt(D->getBody());
436
      // If another invalidation method was called, trust that full invalidation
437
      // has occurred.
438
16
      if (CalledAnotherInvalidationMethod)
439
2
        Ivars.clear();
440
16
    }
441
18
  }
442
443
  // If all ivars have been invalidated by partial invalidators, there is
444
  // nothing to check here.
445
24
  if (Ivars.empty())
446
4
    return;
447
448
  // Find all invalidation methods in this @interface declaration and parents.
449
20
  InvalidationInfo Info;
450
20
  containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
451
452
  // Report an error in case none of the invalidation methods are declared.
453
20
  if (!Info.needsInvalidation() && 
!PartialInfo.needsInvalidation()6
) {
454
4
    if (Filter.check_MissingInvalidationMethod)
455
2
      reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
456
2
                                 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
457
2
                                 /*MissingDeclaration*/ true);
458
    // If there are no invalidation methods, there is no ivar validation work
459
    // to be done.
460
4
    return;
461
4
  }
462
463
  // Only check if Ivars are invalidated when InstanceVariableInvalidation
464
  // has been requested.
465
16
  if (!Filter.check_InstanceVariableInvalidation)
466
8
    return;
467
468
  // Check that all ivars are invalidated by the invalidation methods.
469
8
  bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
470
16
  for (const ObjCMethodDecl *InterfD : Info.InvalidationMethods) {
471
    // Get the corresponding method in the @implementation.
472
16
    const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
473
16
                                               InterfD->isInstanceMethod());
474
16
    if (D && 
D->hasBody()9
) {
475
9
      AtImplementationContainsAtLeastOneInvalidationMethod = true;
476
477
      // Get a copy of ivars needing invalidation.
478
9
      IvarSet IvarsI = Ivars;
479
480
9
      bool CalledAnotherInvalidationMethod = false;
481
9
      MethodCrawler(IvarsI,
482
9
                    CalledAnotherInvalidationMethod,
483
9
                    PropSetterToIvarMap,
484
9
                    PropGetterToIvarMap,
485
9
                    PropertyToIvarMap,
486
9
                    BR.getContext()).VisitStmt(D->getBody());
487
      // If another invalidation method was called, trust that full invalidation
488
      // has occurred.
489
9
      if (CalledAnotherInvalidationMethod)
490
1
        continue;
491
492
      // Warn on the ivars that were not invalidated by the method.
493
8
      for (const ObjCIvarDecl *Ivar : llvm::make_first_range(IvarsI))
494
26
        reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, D);
495
8
    }
496
16
  }
497
498
  // Report an error in case none of the invalidation methods are implemented.
499
8
  if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
500
4
    if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
501
      // Warn on the ivars that were not invalidated by the prrtial
502
      // invalidation methods.
503
1
      for (const ObjCIvarDecl *Ivar : llvm::make_first_range(Ivars))
504
2
        reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, nullptr);
505
3
    } else {
506
      // Otherwise, no invalidation methods were implemented.
507
3
      reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
508
3
                                 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
509
3
                                 /*MissingDeclaration*/ false);
510
3
    }
511
4
  }
512
8
}
513
514
void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
515
    CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl,
516
    const IvarToPropMapTy &IvarToPopertyMap,
517
5
    const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
518
5
  SmallString<128> sbuf;
519
5
  llvm::raw_svector_ostream os(sbuf);
520
5
  assert(FirstIvarDecl);
521
5
  printIvar(os, FirstIvarDecl, IvarToPopertyMap);
522
5
  os << "needs to be invalidated; ";
523
5
  if (MissingDeclaration)
524
2
    os << "no invalidation method is declared for ";
525
3
  else
526
3
    os << "no invalidation method is defined in the @implementation for ";
527
5
  os << InterfaceD->getName();
528
529
5
  PathDiagnosticLocation IvarDecLocation =
530
5
    PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
531
532
5
  BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
533
5
                     categories::CoreFoundationObjectiveC, os.str(),
534
5
                     IvarDecLocation);
535
5
}
536
537
void IvarInvalidationCheckerImpl::
538
reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
539
                            const IvarToPropMapTy &IvarToPopertyMap,
540
28
                            const ObjCMethodDecl *MethodD) const {
541
28
  SmallString<128> sbuf;
542
28
  llvm::raw_svector_ostream os(sbuf);
543
28
  printIvar(os, IvarD, IvarToPopertyMap);
544
28
  os << "needs to be invalidated or set to nil";
545
28
  if (MethodD) {
546
26
    PathDiagnosticLocation MethodDecLocation =
547
26
                           PathDiagnosticLocation::createEnd(MethodD->getBody(),
548
26
                           BR.getSourceManager(),
549
26
                           Mgr.getAnalysisDeclContext(MethodD));
550
26
    BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
551
26
                       "Incomplete invalidation",
552
26
                       categories::CoreFoundationObjectiveC, os.str(),
553
26
                       MethodDecLocation);
554
26
  } else {
555
2
    BR.EmitBasicReport(
556
2
        IvarD, Filter.checkName_InstanceVariableInvalidation,
557
2
        "Incomplete invalidation", categories::CoreFoundationObjectiveC,
558
2
        os.str(),
559
2
        PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
560
2
  }
561
28
}
562
563
void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
564
60
    const ObjCIvarDecl *Iv) {
565
60
  IvarSet::iterator I = IVars.find(Iv);
566
60
  if (I != IVars.end()) {
567
    // If InvalidationMethod is present, we are processing the message send and
568
    // should ensure we are invalidating with the appropriate method,
569
    // otherwise, we are processing setting to 'nil'.
570
51
    if (!InvalidationMethod || 
I->second.hasMethod(InvalidationMethod)26
)
571
48
      IVars.erase(I);
572
51
  }
573
60
}
574
575
96
const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
576
96
  E = E->IgnoreParenCasts();
577
96
  if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
578
6
    E = POE->getSyntacticForm()->IgnoreParenCasts();
579
96
  if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
580
24
    E = OVE->getSourceExpr()->IgnoreParenCasts();
581
96
  return E;
582
96
}
583
584
void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
585
24
    const ObjCIvarRefExpr *IvarRef) {
586
24
  if (const Decl *D = IvarRef->getDecl())
587
24
    markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
588
24
}
589
590
void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
591
6
    const ObjCMessageExpr *ME) {
592
6
  const ObjCMethodDecl *MD = ME->getMethodDecl();
593
6
  if (MD) {
594
6
    MD = MD->getCanonicalDecl();
595
6
    MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
596
6
    if (IvI != PropertyGetterToIvarMap.end())
597
6
      markInvalidated(IvI->second);
598
6
  }
599
6
}
600
601
void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
602
15
    const ObjCPropertyRefExpr *PA) {
603
604
15
  if (PA->isExplicitProperty()) {
605
15
    const ObjCPropertyDecl *PD = PA->getExplicitProperty();
606
15
    if (PD) {
607
15
      PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
608
15
      PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
609
15
      if (IvI != PropertyToIvarMap.end())
610
15
        markInvalidated(IvI->second);
611
15
      return;
612
15
    }
613
15
  }
614
615
0
  if (PA->isImplicitProperty()) {
616
0
    const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
617
0
    if (MD) {
618
0
      MD = MD->getCanonicalDecl();
619
0
      MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
620
0
      if (IvI != PropertyGetterToIvarMap.end())
621
0
        markInvalidated(IvI->second);
622
0
      return;
623
0
    }
624
0
  }
625
0
}
626
627
35
bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
628
35
  E = peel(E);
629
630
35
  return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
631
35
           != Expr::NPCK_NotNull);
632
35
}
633
634
61
void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
635
61
  E = peel(E);
636
637
61
  if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
638
24
    checkObjCIvarRefExpr(IvarRef);
639
24
    return;
640
24
  }
641
642
37
  if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
643
15
    checkObjCPropertyRefExpr(PropRef);
644
15
    return;
645
15
  }
646
647
22
  if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
648
6
    checkObjCMessageExpr(MsgExpr);
649
6
    return;
650
6
  }
651
22
}
652
653
void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
654
19
    const BinaryOperator *BO) {
655
19
  VisitStmt(BO);
656
657
  // Do we assign/compare against zero? If yes, check the variable we are
658
  // assigning to.
659
19
  BinaryOperatorKind Opcode = BO->getOpcode();
660
19
  if (Opcode != BO_Assign &&
661
19
      
Opcode != BO_EQ3
&&
662
19
      
Opcode != BO_NE0
)
663
0
    return;
664
665
19
  if (isZero(BO->getRHS())) {
666
18
      check(BO->getLHS());
667
18
      return;
668
18
  }
669
670
1
  if (Opcode != BO_Assign && isZero(BO->getLHS())) {
671
1
    check(BO->getRHS());
672
1
    return;
673
1
  }
674
1
}
675
676
void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
677
63
  const ObjCMessageExpr *ME) {
678
63
  const ObjCMethodDecl *MD = ME->getMethodDecl();
679
63
  const Expr *Receiver = ME->getInstanceReceiver();
680
681
  // Stop if we are calling '[self invalidate]'.
682
63
  if (Receiver && 
isInvalidationMethod(MD, /*LookForPartial*/ false)60
)
683
26
    if (Receiver->isObjCSelfExpr()) {
684
3
      CalledAnotherInvalidationMethod = true;
685
3
      return;
686
3
    }
687
688
  // Check if we call a setter and set the property to 'nil'.
689
60
  if (MD && (ME->getNumArgs() == 1) && 
isZero(ME->getArg(0))15
) {
690
15
    MD = MD->getCanonicalDecl();
691
15
    MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
692
15
    if (IvI != PropertySetterToIvarMap.end()) {
693
15
      markInvalidated(IvI->second);
694
15
      return;
695
15
    }
696
15
  }
697
698
  // Check if we call the 'invalidation' routine on the ivar.
699
45
  if (Receiver) {
700
42
    InvalidationMethod = MD;
701
42
    check(Receiver->IgnoreParenCasts());
702
42
    InvalidationMethod = nullptr;
703
42
  }
704
705
45
  VisitStmt(ME);
706
45
}
707
} // end anonymous namespace
708
709
// Register the checkers.
710
namespace {
711
class IvarInvalidationChecker :
712
  public Checker<check::ASTDecl<ObjCImplementationDecl> > {
713
public:
714
  ChecksFilter Filter;
715
public:
716
  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
717
28
                    BugReporter &BR) const {
718
28
    IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
719
28
    Walker.visit(D);
720
28
  }
721
};
722
} // end anonymous namespace
723
724
2
void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
725
2
  mgr.registerChecker<IvarInvalidationChecker>();
726
2
}
727
728
4
bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) {
729
4
  return true;
730
4
}
731
732
#define REGISTER_CHECKER(name)                                                 \
733
2
  void ento::register##name(CheckerManager &mgr) {                             \
734
2
    IvarInvalidationChecker *checker =                                         \
735
2
        mgr.getChecker<IvarInvalidationChecker>();                             \
736
2
    checker->Filter.check_##name = true;                                       \
737
2
    checker->Filter.checkName_##name = mgr.getCurrentCheckerName();            \
738
2
  }                                                                            \
clang::ento::registerInstanceVariableInvalidation(clang::ento::CheckerManager&)
Line
Count
Source
733
1
  void ento::register##name(CheckerManager &mgr) {                             \
734
1
    IvarInvalidationChecker *checker =                                         \
735
1
        mgr.getChecker<IvarInvalidationChecker>();                             \
736
1
    checker->Filter.check_##name = true;                                       \
737
1
    checker->Filter.checkName_##name = mgr.getCurrentCheckerName();            \
738
1
  }                                                                            \
clang::ento::registerMissingInvalidationMethod(clang::ento::CheckerManager&)
Line
Count
Source
733
1
  void ento::register##name(CheckerManager &mgr) {                             \
734
1
    IvarInvalidationChecker *checker =                                         \
735
1
        mgr.getChecker<IvarInvalidationChecker>();                             \
736
1
    checker->Filter.check_##name = true;                                       \
737
1
    checker->Filter.checkName_##name = mgr.getCurrentCheckerName();            \
738
1
  }                                                                            \
739
                                                                               \
740
4
  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
clang::ento::shouldRegisterInstanceVariableInvalidation(clang::ento::CheckerManager const&)
Line
Count
Source
740
2
  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
clang::ento::shouldRegisterMissingInvalidationMethod(clang::ento::CheckerManager const&)
Line
Count
Source
740
2
  bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
741
742
REGISTER_CHECKER(InstanceVariableInvalidation)
743
REGISTER_CHECKER(MissingInvalidationMethod)