Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
Line
Count
Source (jump to first uncovered line)
1
//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- 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 analyzes Objective-C -dealloc methods and their callees
10
//  to warn about improper releasing of instance variables that back synthesized
11
// properties. It warns about missing releases in the following cases:
12
//  - When a class has a synthesized instance variable for a 'retain' or 'copy'
13
//    property and lacks a -dealloc method in its implementation.
14
//  - When a class has a synthesized instance variable for a 'retain'/'copy'
15
//   property but the ivar is not released in -dealloc by either -release
16
//   or by nilling out the property.
17
//
18
//  It warns about extra releases in -dealloc (but not in callees) when a
19
//  synthesized instance variable is released in the following cases:
20
//  - When the property is 'assign' and is not 'readonly'.
21
//  - When the property is 'weak'.
22
//
23
//  This checker only warns for instance variables synthesized to back
24
//  properties. Handling the more general case would require inferring whether
25
//  an instance variable is stored retained or not. For synthesized properties,
26
//  this is specified in the property declaration itself.
27
//
28
//===----------------------------------------------------------------------===//
29
30
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
31
#include "clang/Analysis/PathDiagnostic.h"
32
#include "clang/AST/Attr.h"
33
#include "clang/AST/DeclObjC.h"
34
#include "clang/AST/Expr.h"
35
#include "clang/AST/ExprObjC.h"
36
#include "clang/Basic/LangOptions.h"
37
#include "clang/Basic/TargetInfo.h"
38
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
39
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
40
#include "clang/StaticAnalyzer/Core/Checker.h"
41
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
42
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
43
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
44
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
45
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
46
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
47
#include "llvm/Support/raw_ostream.h"
48
#include <optional>
49
50
using namespace clang;
51
using namespace ento;
52
53
/// Indicates whether an instance variable is required to be released in
54
/// -dealloc.
55
enum class ReleaseRequirement {
56
  /// The instance variable must be released, either by calling
57
  /// -release on it directly or by nilling it out with a property setter.
58
  MustRelease,
59
60
  /// The instance variable must not be directly released with -release.
61
  MustNotReleaseDirectly,
62
63
  /// The requirement for the instance variable could not be determined.
64
  Unknown
65
};
66
67
/// Returns true if the property implementation is synthesized and the
68
/// type of the property is retainable.
69
static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I,
70
                                            const ObjCIvarDecl **ID,
71
274
                                            const ObjCPropertyDecl **PD) {
72
73
274
  if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
74
0
    return false;
75
76
274
  (*ID) = I->getPropertyIvarDecl();
77
274
  if (!(*ID))
78
0
    return false;
79
80
274
  QualType T = (*ID)->getType();
81
274
  if (!T->isObjCRetainableType())
82
19
    return false;
83
84
255
  (*PD) = I->getPropertyDecl();
85
  // Shouldn't be able to synthesize a property that doesn't exist.
86
255
  assert(*PD);
87
88
255
  return true;
89
255
}
90
91
namespace {
92
93
class ObjCDeallocChecker
94
    : public Checker<check::ASTDecl<ObjCImplementationDecl>,
95
                     check::PreObjCMessage, check::PostObjCMessage,
96
                     check::PreCall,
97
                     check::BeginFunction, check::EndFunction,
98
                     eval::Assume,
99
                     check::PointerEscape,
100
                     check::PreStmt<ReturnStmt>> {
101
102
  mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII,
103
      *Block_releaseII, *CIFilterII;
104
105
  mutable Selector DeallocSel, ReleaseSel;
106
107
  std::unique_ptr<BugType> MissingReleaseBugType;
108
  std::unique_ptr<BugType> ExtraReleaseBugType;
109
  std::unique_ptr<BugType> MistakenDeallocBugType;
110
111
public:
112
  ObjCDeallocChecker();
113
114
  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
115
                    BugReporter &BR) const;
116
  void checkBeginFunction(CheckerContext &Ctx) const;
117
  void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
118
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
119
  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
120
121
  ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
122
                             bool Assumption) const;
123
124
  ProgramStateRef checkPointerEscape(ProgramStateRef State,
125
                                     const InvalidatedSymbols &Escaped,
126
                                     const CallEvent *Call,
127
                                     PointerEscapeKind Kind) const;
128
  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
129
  void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
130
131
private:
132
  void diagnoseMissingReleases(CheckerContext &C) const;
133
134
  bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
135
                            CheckerContext &C) const;
136
137
  bool diagnoseMistakenDealloc(SymbolRef DeallocedValue,
138
                               const ObjCMethodCall &M,
139
                               CheckerContext &C) const;
140
141
  SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M,
142
                                         CheckerContext &C) const;
143
144
  const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const;
145
  SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const;
146
147
  const ObjCPropertyImplDecl*
148
  findPropertyOnDeallocatingInstance(SymbolRef IvarSym,
149
                                     CheckerContext &C) const;
150
151
  ReleaseRequirement
152
  getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const;
153
154
  bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const;
155
  bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx,
156
                           SVal &SelfValOut) const;
157
  bool instanceDeallocIsOnStack(const CheckerContext &C,
158
                                SVal &InstanceValOut) const;
159
160
  bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
161
162
  const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const;
163
164
  const ObjCPropertyDecl *
165
  findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const;
166
167
  void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const;
168
  ProgramStateRef removeValueRequiringRelease(ProgramStateRef State,
169
                                              SymbolRef InstanceSym,
170
                                              SymbolRef ValueSym) const;
171
172
  void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
173
174
  bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const;
175
176
  bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const;
177
  bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl *PropImpl) const;
178
};
179
} // End anonymous namespace.
180
181
182
/// Maps from the symbol for a class instance to the set of
183
/// symbols remaining that must be released in -dealloc.
184
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
185
REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet)
186
187
188
/// An AST check that diagnose when the class requires a -dealloc method and
189
/// is missing one.
190
void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D,
191
                                      AnalysisManager &Mgr,
192
128
                                      BugReporter &BR) const {
193
128
  assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly);
194
128
  assert(!Mgr.getLangOpts().ObjCAutoRefCount);
195
128
  initIdentifierInfoAndSelectors(Mgr.getASTContext());
196
197
128
  const ObjCInterfaceDecl *ID = D->getClassInterface();
198
  // If the class is known to have a lifecycle with a separate teardown method
199
  // then it may not require a -dealloc method.
200
128
  if (classHasSeparateTeardown(ID))
201
24
    return;
202
203
  // Does the class contain any synthesized properties that are retainable?
204
  // If not, skip the check entirely.
205
104
  const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr;
206
104
  bool HasOthers = false;
207
109
  for (const auto *I : D->property_impls()) {
208
109
    if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) {
209
74
      if (!PropImplRequiringRelease)
210
64
        PropImplRequiringRelease = I;
211
10
      else {
212
10
        HasOthers = true;
213
10
        break;
214
10
      }
215
74
    }
216
109
  }
217
218
104
  if (!PropImplRequiringRelease)
219
40
    return;
220
221
64
  const ObjCMethodDecl *MD = nullptr;
222
223
  // Scan the instance methods for "dealloc".
224
81
  for (const auto *I : D->instance_methods()) {
225
81
    if (I->getSelector() == DeallocSel) {
226
59
      MD = I;
227
59
      break;
228
59
    }
229
81
  }
230
231
64
  if (!MD) { // No dealloc found.
232
5
    const char* Name = "Missing -dealloc";
233
234
5
    std::string Buf;
235
5
    llvm::raw_string_ostream OS(Buf);
236
5
    OS << "'" << *D << "' lacks a 'dealloc' instance method but "
237
5
       << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl()
238
5
       << "'";
239
240
5
    if (HasOthers)
241
1
      OS << " and others";
242
5
    PathDiagnosticLocation DLoc =
243
5
        PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
244
245
5
    BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC,
246
5
                       OS.str(), DLoc);
247
5
    return;
248
5
  }
249
64
}
250
251
/// If this is the beginning of -dealloc, mark the values initially stored in
252
/// instance variables that must be released by the end of -dealloc
253
/// as unreleased in the state.
254
void ObjCDeallocChecker::checkBeginFunction(
255
625
    CheckerContext &C) const {
256
625
  initIdentifierInfoAndSelectors(C.getASTContext());
257
258
  // Only do this if the current method is -dealloc.
259
625
  SVal SelfVal;
260
625
  if (!isInInstanceDealloc(C, SelfVal))
261
532
    return;
262
263
93
  SymbolRef SelfSymbol = SelfVal.getAsSymbol();
264
265
93
  const LocationContext *LCtx = C.getLocationContext();
266
93
  ProgramStateRef InitialState = C.getState();
267
268
93
  ProgramStateRef State = InitialState;
269
270
93
  SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
271
272
  // Symbols that must be released by the end of the -dealloc;
273
93
  SymbolSet RequiredReleases = F.getEmptySet();
274
275
  // If we're an inlined -dealloc, we should add our symbols to the existing
276
  // set from our subclass.
277
93
  if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
278
4
    RequiredReleases = *CurrSet;
279
280
133
  for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
281
133
    ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl);
282
133
    if (Requirement != ReleaseRequirement::MustRelease)
283
33
      continue;
284
285
100
    SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
286
100
    std::optional<Loc> LValLoc = LVal.getAs<Loc>();
287
100
    if (!LValLoc)
288
0
      continue;
289
290
100
    SVal InitialVal = State->getSVal(*LValLoc);
291
100
    SymbolRef Symbol = InitialVal.getAsSymbol();
292
100
    if (!Symbol || !isa<SymbolRegionValue>(Symbol))
293
2
      continue;
294
295
    // Mark the value as requiring a release.
296
98
    RequiredReleases = F.add(RequiredReleases, Symbol);
297
98
  }
298
299
93
  if (!RequiredReleases.isEmpty()) {
300
69
    State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
301
69
  }
302
303
93
  if (State != InitialState) {
304
69
    C.addTransition(State);
305
69
  }
306
93
}
307
308
/// Given a symbol for an ivar, return the ivar region it was loaded from.
309
/// Returns nullptr if the instance symbol cannot be found.
310
const ObjCIvarRegion *
311
926
ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const {
312
926
  return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion());
313
926
}
314
315
/// Given a symbol for an ivar, return a symbol for the instance containing
316
/// the ivar. Returns nullptr if the instance symbol cannot be found.
317
SymbolRef
318
335
ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const {
319
320
335
  const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
321
335
  if (!IvarRegion)
322
184
    return nullptr;
323
324
151
  const SymbolicRegion *SR = IvarRegion->getSymbolicBase();
325
151
  assert(SR && "Symbolic base should not be nullptr");
326
151
  return SR->getSymbol();
327
151
}
328
329
/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
330
/// a release or a nilling-out property setter.
331
void ObjCDeallocChecker::checkPreObjCMessage(
332
592
    const ObjCMethodCall &M, CheckerContext &C) const {
333
  // Only run if -dealloc is on the stack.
334
592
  SVal DeallocedInstance;
335
592
  if (!instanceDeallocIsOnStack(C, DeallocedInstance))
336
352
    return;
337
338
240
  SymbolRef ReleasedValue = nullptr;
339
340
240
  if (M.getSelector() == ReleaseSel) {
341
61
    ReleasedValue = M.getReceiverSVal().getAsSymbol();
342
179
  } else if (M.getSelector() == DeallocSel && 
!M.isReceiverSelfOrSuper()102
) {
343
5
    if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C))
344
2
      return;
345
5
  }
346
347
238
  if (ReleasedValue) {
348
    // An instance variable symbol was released with -release:
349
    //    [_property release];
350
61
    if (diagnoseExtraRelease(ReleasedValue,M, C))
351
5
      return;
352
177
  } else {
353
    // An instance variable symbol was released nilling out its property:
354
    //    self.property = nil;
355
177
    ReleasedValue = getValueReleasedByNillingOut(M, C);
356
177
  }
357
358
233
  if (!ReleasedValue)
359
162
    return;
360
361
71
  transitionToReleaseValue(C, ReleasedValue);
362
71
}
363
364
/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
365
/// call to Block_release().
366
void ObjCDeallocChecker::checkPreCall(const CallEvent &Call,
367
1.05k
                                      CheckerContext &C) const {
368
1.05k
  const IdentifierInfo *II = Call.getCalleeIdentifier();
369
1.05k
  if (II != Block_releaseII)
370
1.04k
    return;
371
372
2
  if (Call.getNumArgs() != 1)
373
0
    return;
374
375
2
  SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol();
376
2
  if (!ReleasedValue)
377
0
    return;
378
379
2
  transitionToReleaseValue(C, ReleasedValue);
380
2
}
381
/// If the message was a call to '[super dealloc]', diagnose any missing
382
/// releases.
383
void ObjCDeallocChecker::checkPostObjCMessage(
384
589
    const ObjCMethodCall &M, CheckerContext &C) const {
385
  // We perform this check post-message so that if the super -dealloc
386
  // calls a helper method and that this class overrides, any ivars released in
387
  // the helper method will be recorded before checking.
388
589
  if (isSuperDeallocMessage(M))
389
97
    diagnoseMissingReleases(C);
390
589
}
391
392
/// Check for missing releases even when -dealloc does not call
393
/// '[super dealloc]'.
394
void ObjCDeallocChecker::checkEndFunction(
395
631
    const ReturnStmt *RS, CheckerContext &C) const {
396
631
  diagnoseMissingReleases(C);
397
631
}
398
399
/// Check for missing releases on early return.
400
void ObjCDeallocChecker::checkPreStmt(
401
227
    const ReturnStmt *RS, CheckerContext &C) const {
402
227
  diagnoseMissingReleases(C);
403
227
}
404
405
/// When a symbol is assumed to be nil, remove it from the set of symbols
406
/// require to be nil.
407
ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond,
408
3.66k
                                               bool Assumption) const {
409
3.66k
  if (State->get<UnreleasedIvarMap>().isEmpty())
410
3.34k
    return State;
411
412
318
  auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol());
413
318
  if (!CondBSE)
414
43
    return State;
415
416
275
  BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
417
275
  if (Assumption) {
418
198
    if (OpCode != BO_EQ)
419
197
      return State;
420
198
  } else {
421
77
    if (OpCode != BO_NE)
422
1
      return State;
423
77
  }
424
425
77
  SymbolRef NullSymbol = nullptr;
426
77
  if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
427
77
    const llvm::APInt &RHS = SIE->getRHS();
428
77
    if (RHS != 0)
429
0
      return State;
430
77
    NullSymbol = SIE->getLHS();
431
77
  } else 
if (auto *0
SIE0
= dyn_cast<IntSymExpr>(CondBSE)) {
432
0
    const llvm::APInt &LHS = SIE->getLHS();
433
0
    if (LHS != 0)
434
0
      return State;
435
0
    NullSymbol = SIE->getRHS();
436
0
  } else {
437
0
    return State;
438
0
  }
439
440
77
  SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
441
77
  if (!InstanceSymbol)
442
22
    return State;
443
444
55
  State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
445
446
55
  return State;
447
77
}
448
449
/// If a symbol escapes conservatively assume unseen code released it.
450
ProgramStateRef ObjCDeallocChecker::checkPointerEscape(
451
    ProgramStateRef State, const InvalidatedSymbols &Escaped,
452
915
    const CallEvent *Call, PointerEscapeKind Kind) const {
453
454
915
  if (State->get<UnreleasedIvarMap>().isEmpty())
455
704
    return State;
456
457
  // Don't treat calls to '[super dealloc]' as escaping for the purposes
458
  // of this checker. Because the checker diagnoses missing releases in the
459
  // post-message handler for '[super dealloc], escaping here would cause
460
  // the checker to never warn.
461
211
  auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
462
211
  if (OMC && 
isSuperDeallocMessage(*OMC)202
)
463
67
    return State;
464
465
185
  
for (const auto &Sym : Escaped)144
{
466
185
    if (!Call || 
(183
Call183
&&
!Call->isInSystemHeader()183
)) {
467
      // If Sym is a symbol for an object with instance variables that
468
      // must be released, remove these obligations when the object escapes
469
      // unless via a call to a system function. System functions are
470
      // very unlikely to release instance variables on objects passed to them,
471
      // and are frequently called on 'self' in -dealloc (e.g., to remove
472
      // observers) -- we want to avoid false negatives from escaping on
473
      // them.
474
55
      State = State->remove<UnreleasedIvarMap>(Sym);
475
55
    }
476
477
478
185
    SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
479
185
    if (!InstanceSymbol)
480
146
      continue;
481
482
39
    State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
483
39
  }
484
485
144
  return State;
486
211
}
487
488
/// Report any unreleased instance variables for the current instance being
489
/// dealloced.
490
955
void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
491
955
  ProgramStateRef State = C.getState();
492
493
955
  SVal SelfVal;
494
955
  if (!isInInstanceDealloc(C, SelfVal))
495
765
    return;
496
497
190
  const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
498
190
  const LocationContext *LCtx = C.getLocationContext();
499
500
190
  ExplodedNode *ErrNode = nullptr;
501
502
190
  SymbolRef SelfSym = SelfVal.getAsSymbol();
503
190
  if (!SelfSym)
504
0
    return;
505
506
190
  const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
507
190
  if (!OldUnreleased)
508
130
    return;
509
510
60
  SymbolSet NewUnreleased = *OldUnreleased;
511
60
  SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
512
513
60
  ProgramStateRef InitialState = State;
514
515
64
  for (auto *IvarSymbol : *OldUnreleased) {
516
64
    const TypedValueRegion *TVR =
517
64
        cast<SymbolRegionValue>(IvarSymbol)->getRegion();
518
64
    const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
519
520
    // Don't warn if the ivar is not for this instance.
521
64
    if (SelfRegion != IvarRegion->getSuperRegion())
522
0
      continue;
523
524
64
    const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
525
    // Prevent an inlined call to -dealloc in a super class from warning
526
    // about the values the subclass's -dealloc should release.
527
64
    if (IvarDecl->getContainingInterface() !=
528
64
        cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface())
529
4
      continue;
530
531
    // Prevents diagnosing multiple times for the same instance variable
532
    // at, for example, both a return and at the end of the function.
533
60
    NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
534
535
60
    if (State->getStateManager()
536
60
            .getConstraintManager()
537
60
            .isNull(State, IvarSymbol)
538
60
            .isConstrainedTrue()) {
539
0
      continue;
540
0
    }
541
542
    // A missing release manifests as a leak, so treat as a non-fatal error.
543
60
    if (!ErrNode)
544
58
      ErrNode = C.generateNonFatalErrorNode();
545
    // If we've already reached this node on another path, return without
546
    // diagnosing.
547
60
    if (!ErrNode)
548
0
      return;
549
550
60
    std::string Buf;
551
60
    llvm::raw_string_ostream OS(Buf);
552
553
60
    const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface();
554
    // If the class is known to have a lifecycle with teardown that is
555
    // separate from -dealloc, do not warn about missing releases. We
556
    // suppress here (rather than not tracking for instance variables in
557
    // such classes) because these classes are rare.
558
60
    if (classHasSeparateTeardown(Interface))
559
10
      return;
560
561
50
    ObjCImplDecl *ImplDecl = Interface->getImplementation();
562
563
50
    const ObjCPropertyImplDecl *PropImpl =
564
50
        ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
565
566
50
    const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
567
568
50
    assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy ||
569
50
           PropDecl->getSetterKind() == ObjCPropertyDecl::Retain);
570
571
50
    OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl
572
50
       << "' was ";
573
574
50
    if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain)
575
46
      OS << "retained";
576
4
    else
577
4
      OS << "copied";
578
579
50
    OS << " by a synthesized property but not released"
580
50
          " before '[super dealloc]'";
581
582
50
    auto BR = std::make_unique<PathSensitiveBugReport>(*MissingReleaseBugType,
583
50
                                                       OS.str(), ErrNode);
584
50
    C.emitReport(std::move(BR));
585
50
  }
586
587
50
  if (NewUnreleased.isEmpty()) {
588
46
    State = State->remove<UnreleasedIvarMap>(SelfSym);
589
46
  } else {
590
4
    State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
591
4
  }
592
593
50
  if (ErrNode) {
594
48
    C.addTransition(State, ErrNode);
595
48
  } else 
if (2
State != InitialState2
) {
596
0
    C.addTransition(State);
597
0
  }
598
599
  // Make sure that after checking in the top-most frame the list of
600
  // tracked ivars is empty. This is intended to detect accidental leaks in
601
  // the UnreleasedIvarMap program state.
602
50
  assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
603
50
}
604
605
/// Given a symbol, determine whether the symbol refers to an ivar on
606
/// the top-most deallocating instance. If so, find the property for that
607
/// ivar, if one exists. Otherwise return null.
608
const ObjCPropertyImplDecl *
609
ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
610
65
    SymbolRef IvarSym, CheckerContext &C) const {
611
65
  SVal DeallocedInstance;
612
65
  if (!isInInstanceDealloc(C, DeallocedInstance))
613
13
    return nullptr;
614
615
  // Try to get the region from which the ivar value was loaded.
616
52
  auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
617
52
  if (!IvarRegion)
618
16
    return nullptr;
619
620
  // Don't try to find the property if the ivar was not loaded from the
621
  // given instance.
622
36
  if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
623
36
      IvarRegion->getSuperRegion())
624
1
    return nullptr;
625
626
35
  const LocationContext *LCtx = C.getLocationContext();
627
35
  const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
628
629
35
  const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
630
35
  const ObjCPropertyImplDecl *PropImpl =
631
35
      Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
632
35
  return PropImpl;
633
36
}
634
635
/// Emits a warning if the current context is -dealloc and ReleasedValue
636
/// must not be directly released in a -dealloc. Returns true if a diagnostic
637
/// was emitted.
638
bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
639
                                              const ObjCMethodCall &M,
640
61
                                              CheckerContext &C) const {
641
  // Try to get the region from which the released value was loaded.
642
  // Note that, unlike diagnosing for missing releases, here we don't track
643
  // values that must not be released in the state. This is because even if
644
  // these values escape, it is still an error under the rules of MRR to
645
  // release them in -dealloc.
646
61
  const ObjCPropertyImplDecl *PropImpl =
647
61
      findPropertyOnDeallocatingInstance(ReleasedValue, C);
648
649
61
  if (!PropImpl)
650
31
    return false;
651
652
  // If the ivar belongs to a property that must not be released directly
653
  // in dealloc, emit a warning.
654
30
  if (getDeallocReleaseRequirement(PropImpl) !=
655
30
      ReleaseRequirement::MustNotReleaseDirectly) {
656
24
    return false;
657
24
  }
658
659
  // If the property is readwrite but it shadows a read-only property in its
660
  // external interface, treat the property a read-only. If the outside
661
  // world cannot write to a property then the internal implementation is free
662
  // to make its own convention about whether the value is stored retained
663
  // or not. We look up the shadow here rather than in
664
  // getDeallocReleaseRequirement() because doing so can be expensive.
665
6
  const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl);
666
6
  if (PropDecl) {
667
1
    if (PropDecl->isReadOnly())
668
1
      return false;
669
5
  } else {
670
5
    PropDecl = PropImpl->getPropertyDecl();
671
5
  }
672
673
5
  ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
674
5
  if (!ErrNode)
675
0
    return false;
676
677
5
  std::string Buf;
678
5
  llvm::raw_string_ostream OS(Buf);
679
680
5
  assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak ||
681
5
         (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
682
5
          !PropDecl->isReadOnly()) ||
683
5
         isReleasedByCIFilterDealloc(PropImpl)
684
5
         );
685
686
5
  const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
687
5
  OS << "The '" << *PropImpl->getPropertyIvarDecl()
688
5
     << "' ivar in '" << *Container;
689
690
691
5
  if (isReleasedByCIFilterDealloc(PropImpl)) {
692
2
    OS << "' will be released by '-[CIFilter dealloc]' but also released here";
693
3
  } else {
694
3
    OS << "' was synthesized for ";
695
696
3
    if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
697
2
      OS << "a weak";
698
1
    else
699
1
      OS << "an assign, readwrite";
700
701
3
    OS <<  " property but was released in 'dealloc'";
702
3
  }
703
704
5
  auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType,
705
5
                                                     OS.str(), ErrNode);
706
5
  BR->addRange(M.getOriginExpr()->getSourceRange());
707
708
5
  C.emitReport(std::move(BR));
709
710
5
  return true;
711
5
}
712
713
/// Emits a warning if the current context is -dealloc and DeallocedValue
714
/// must not be directly dealloced in a -dealloc. Returns true if a diagnostic
715
/// was emitted.
716
bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
717
                                                 const ObjCMethodCall &M,
718
5
                                                 CheckerContext &C) const {
719
  // TODO: Apart from unknown/undefined receivers, this may happen when
720
  // dealloc is called as a class method. Should we warn?
721
5
  if (!DeallocedValue)
722
1
    return false;
723
724
  // Find the property backing the instance variable that M
725
  // is dealloc'ing.
726
4
  const ObjCPropertyImplDecl *PropImpl =
727
4
      findPropertyOnDeallocatingInstance(DeallocedValue, C);
728
4
  if (!PropImpl)
729
2
    return false;
730
731
2
  if (getDeallocReleaseRequirement(PropImpl) !=
732
2
      ReleaseRequirement::MustRelease) {
733
0
    return false;
734
0
  }
735
736
2
  ExplodedNode *ErrNode = C.generateErrorNode();
737
2
  if (!ErrNode)
738
0
    return false;
739
740
2
  std::string Buf;
741
2
  llvm::raw_string_ostream OS(Buf);
742
743
2
  OS << "'" << *PropImpl->getPropertyIvarDecl()
744
2
     << "' should be released rather than deallocated";
745
746
2
  auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType,
747
2
                                                     OS.str(), ErrNode);
748
2
  BR->addRange(M.getOriginExpr()->getSourceRange());
749
750
2
  C.emitReport(std::move(BR));
751
752
2
  return true;
753
2
}
754
755
ObjCDeallocChecker::ObjCDeallocChecker()
756
50
    : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr),
757
50
      Block_releaseII(nullptr), CIFilterII(nullptr) {
758
759
50
  MissingReleaseBugType.reset(
760
50
      new BugType(this, "Missing ivar release (leak)",
761
50
                  categories::MemoryRefCount));
762
763
50
  ExtraReleaseBugType.reset(
764
50
      new BugType(this, "Extra ivar release",
765
50
                  categories::MemoryRefCount));
766
767
50
  MistakenDeallocBugType.reset(
768
50
      new BugType(this, "Mistaken dealloc",
769
50
                  categories::MemoryRefCount));
770
50
}
771
772
void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
773
753
    ASTContext &Ctx) const {
774
753
  if (NSObjectII)
775
703
    return;
776
777
50
  NSObjectII = &Ctx.Idents.get("NSObject");
778
50
  SenTestCaseII = &Ctx.Idents.get("SenTestCase");
779
50
  XCTestCaseII = &Ctx.Idents.get("XCTestCase");
780
50
  Block_releaseII = &Ctx.Idents.get("_Block_release");
781
50
  CIFilterII = &Ctx.Idents.get("CIFilter");
782
783
50
  IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc");
784
50
  IdentifierInfo *ReleaseII = &Ctx.Idents.get("release");
785
50
  DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII);
786
50
  ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII);
787
50
}
788
789
/// Returns true if M is a call to '[super dealloc]'.
790
bool ObjCDeallocChecker::isSuperDeallocMessage(
791
791
    const ObjCMethodCall &M) const {
792
791
  if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
793
614
    return false;
794
795
177
  return M.getSelector() == DeallocSel;
796
791
}
797
798
/// Returns the ObjCImplDecl containing the method declaration in LCtx.
799
const ObjCImplDecl *
800
133
ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const {
801
133
  auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl());
802
133
  return cast<ObjCImplDecl>(MD->getDeclContext());
803
133
}
804
805
/// Returns the property that shadowed by PropImpl if one exists and
806
/// nullptr otherwise.
807
const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl(
808
6
    const ObjCPropertyImplDecl *PropImpl) const {
809
6
  const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
810
811
  // Only readwrite properties can shadow.
812
6
  if (PropDecl->isReadOnly())
813
1
    return nullptr;
814
815
5
  auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext());
816
817
  // Only class extensions can contain shadowing properties.
818
5
  if (!CatDecl || 
!CatDecl->IsClassExtension()1
)
819
4
    return nullptr;
820
821
1
  IdentifierInfo *ID = PropDecl->getIdentifier();
822
1
  DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID);
823
1
  for (const NamedDecl *D : R) {
824
1
    auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(D);
825
1
    if (!ShadowedPropDecl)
826
0
      continue;
827
828
1
    if (ShadowedPropDecl->isInstanceProperty()) {
829
1
      assert(ShadowedPropDecl->isReadOnly());
830
1
      return ShadowedPropDecl;
831
1
    }
832
1
  }
833
834
0
  return nullptr;
835
1
}
836
837
/// Add a transition noting the release of the given value.
838
void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C,
839
73
                                                  SymbolRef Value) const {
840
73
  assert(Value);
841
73
  SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
842
73
  if (!InstanceSym)
843
16
    return;
844
57
  ProgramStateRef InitialState = C.getState();
845
846
57
  ProgramStateRef ReleasedState =
847
57
      removeValueRequiringRelease(InitialState, InstanceSym, Value);
848
849
57
  if (ReleasedState != InitialState) {
850
48
    C.addTransition(ReleasedState);
851
48
  }
852
57
}
853
854
/// Remove the Value requiring a release from the tracked set for
855
/// Instance and return the resultant state.
856
ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease(
857
151
    ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const {
858
151
  assert(Instance);
859
151
  assert(Value);
860
151
  const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
861
151
  if (!RemovedRegion)
862
0
    return State;
863
864
151
  const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
865
151
  if (!Unreleased)
866
11
    return State;
867
868
  // Mark the value as no longer requiring a release.
869
140
  SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
870
140
  SymbolSet NewUnreleased = *Unreleased;
871
388
  for (auto &Sym : *Unreleased) {
872
388
    const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
873
388
    assert(UnreleasedRegion);
874
388
    if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
875
90
      NewUnreleased = F.remove(NewUnreleased, Sym);
876
90
    }
877
388
  }
878
879
140
  if (NewUnreleased.isEmpty()) {
880
24
    return State->remove<UnreleasedIvarMap>(Instance);
881
24
  }
882
883
116
  return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
884
140
}
885
886
/// Determines whether the instance variable for \p PropImpl must or must not be
887
/// released in -dealloc or whether it cannot be determined.
888
ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement(
889
274
    const ObjCPropertyImplDecl *PropImpl) const {
890
274
  const ObjCIvarDecl *IvarDecl;
891
274
  const ObjCPropertyDecl *PropDecl;
892
274
  if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl))
893
19
    return ReleaseRequirement::Unknown;
894
895
255
  ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind();
896
897
255
  switch (SK) {
898
  // Retain and copy setters retain/copy their values before storing and so
899
  // the value in their instance variables must be released in -dealloc.
900
212
  case ObjCPropertyDecl::Retain:
901
229
  case ObjCPropertyDecl::Copy:
902
229
    if (isReleasedByCIFilterDealloc(PropImpl))
903
28
      return ReleaseRequirement::MustNotReleaseDirectly;
904
905
201
    if (isNibLoadedIvarWithoutRetain(PropImpl))
906
2
      return ReleaseRequirement::Unknown;
907
908
199
    return ReleaseRequirement::MustRelease;
909
910
10
  case ObjCPropertyDecl::Weak:
911
10
    return ReleaseRequirement::MustNotReleaseDirectly;
912
913
16
  case ObjCPropertyDecl::Assign:
914
    // It is common for the ivars for read-only assign properties to
915
    // always be stored retained, so their release requirement cannot be
916
    // be determined.
917
16
    if (PropDecl->isReadOnly())
918
3
      return ReleaseRequirement::Unknown;
919
920
13
    return ReleaseRequirement::MustNotReleaseDirectly;
921
255
  }
922
0
  llvm_unreachable("Unrecognized setter kind");
923
0
}
924
925
/// Returns the released value if M is a call a setter that releases
926
/// and nils out its underlying instance variable.
927
SymbolRef
928
ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
929
177
                                                 CheckerContext &C) const {
930
177
  SVal ReceiverVal = M.getReceiverSVal();
931
177
  if (!ReceiverVal.isValid())
932
20
    return nullptr;
933
934
157
  if (M.getNumArgs() == 0)
935
117
    return nullptr;
936
937
40
  if (!M.getArgExpr(0)->getType()->isObjCRetainableType())
938
4
    return nullptr;
939
940
  // Is the first argument nil?
941
36
  SVal Arg = M.getArgSVal(0);
942
36
  ProgramStateRef notNilState, nilState;
943
36
  std::tie(notNilState, nilState) =
944
36
      M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
945
36
  if (!(nilState && 
!notNilState19
))
946
18
    return nullptr;
947
948
18
  const ObjCPropertyDecl *Prop = M.getAccessedProperty();
949
18
  if (!Prop)
950
1
    return nullptr;
951
952
17
  ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl();
953
17
  if (!PropIvarDecl)
954
2
    return nullptr;
955
956
15
  ProgramStateRef State = C.getState();
957
958
15
  SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
959
15
  std::optional<Loc> LValLoc = LVal.getAs<Loc>();
960
15
  if (!LValLoc)
961
0
    return nullptr;
962
963
15
  SVal CurrentValInIvar = State->getSVal(*LValLoc);
964
15
  return CurrentValInIvar.getAsSymbol();
965
15
}
966
967
/// Returns true if the current context is a call to -dealloc and false
968
/// otherwise. If true, it also sets SelfValOut to the value of
969
/// 'self'.
970
bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
971
1.64k
                                             SVal &SelfValOut) const {
972
1.64k
  return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
973
1.64k
}
974
975
/// Returns true if LCtx is a call to -dealloc and false
976
/// otherwise. If true, it also sets SelfValOut to the value of
977
/// 'self'.
978
bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
979
                                             const LocationContext *LCtx,
980
2.26k
                                             SVal &SelfValOut) const {
981
2.26k
  auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl());
982
2.26k
  if (!MD || 
!MD->isInstanceMethod()1.58k
||
MD->getSelector() != DeallocSel1.53k
)
983
1.69k
    return false;
984
985
575
  const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
986
575
  assert(SelfDecl && "No self in -dealloc?");
987
988
575
  ProgramStateRef State = C.getState();
989
575
  SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
990
575
  return true;
991
575
}
992
993
/// Returns true if there is a call to -dealloc anywhere on the stack and false
994
/// otherwise. If true, it also sets InstanceValOut to the value of
995
/// 'self' in the frame for -dealloc.
996
bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
997
592
                                                  SVal &InstanceValOut) const {
998
592
  const LocationContext *LCtx = C.getLocationContext();
999
1000
972
  while (LCtx) {
1001
620
    if (isInInstanceDealloc(C, LCtx, InstanceValOut))
1002
240
      return true;
1003
1004
380
    LCtx = LCtx->getParent();
1005
380
  }
1006
1007
352
  return false;
1008
592
}
1009
1010
/// Returns true if the ID is a class in which is known to have
1011
/// a separate teardown lifecycle. In this case, -dealloc warnings
1012
/// about missing releases should be suppressed.
1013
bool ObjCDeallocChecker::classHasSeparateTeardown(
1014
188
    const ObjCInterfaceDecl *ID) const {
1015
  // Suppress if the class is not a subclass of NSObject.
1016
399
  for ( ; ID ; 
ID = ID->getSuperClass()211
) {
1017
378
    IdentifierInfo *II = ID->getIdentifier();
1018
1019
378
    if (II == NSObjectII)
1020
154
      return false;
1021
1022
    // FIXME: For now, ignore classes that subclass SenTestCase and XCTestCase,
1023
    // as these don't need to implement -dealloc.  They implement tear down in
1024
    // another way, which we should try and catch later.
1025
    //  http://llvm.org/bugs/show_bug.cgi?id=3187
1026
224
    if (II == XCTestCaseII || 
II == SenTestCaseII218
)
1027
13
      return true;
1028
224
  }
1029
1030
21
  return true;
1031
188
}
1032
1033
/// The -dealloc method in CIFilter highly unusual in that is will release
1034
/// instance variables belonging to its *subclasses* if the variable name
1035
/// starts with "input" or backs a property whose name starts with "input".
1036
/// Subclasses should not release these ivars in their own -dealloc method --
1037
/// doing so could result in an over release.
1038
///
1039
/// This method returns true if the property will be released by
1040
/// -[CIFilter dealloc].
1041
bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1042
236
    const ObjCPropertyImplDecl *PropImpl) const {
1043
236
  assert(PropImpl->getPropertyIvarDecl());
1044
236
  StringRef PropName = PropImpl->getPropertyDecl()->getName();
1045
236
  StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName();
1046
1047
236
  const char *ReleasePrefix = "input";
1048
236
  if (!(PropName.startswith(ReleasePrefix) ||
1049
236
        
IvarName.startswith(ReleasePrefix)208
)) {
1050
204
    return false;
1051
204
  }
1052
1053
32
  const ObjCInterfaceDecl *ID =
1054
32
      PropImpl->getPropertyIvarDecl()->getContainingInterface();
1055
64
  for ( ; ID ; 
ID = ID->getSuperClass()32
) {
1056
64
    IdentifierInfo *II = ID->getIdentifier();
1057
64
    if (II == CIFilterII)
1058
32
      return true;
1059
64
  }
1060
1061
0
  return false;
1062
32
}
1063
1064
/// Returns whether the ivar backing the property is an IBOutlet that
1065
/// has its value set by nib loading code without retaining the value.
1066
///
1067
/// On macOS, if there is no setter, the nib-loading code sets the ivar
1068
/// directly, without retaining the value,
1069
///
1070
/// On iOS and its derivatives, the nib-loading code will call
1071
/// -setValue:forKey:, which retains the value before directly setting the ivar.
1072
bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1073
201
    const ObjCPropertyImplDecl *PropImpl) const {
1074
201
  const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl();
1075
201
  if (!IvarDecl->hasAttr<IBOutletAttr>())
1076
193
    return false;
1077
1078
8
  const llvm::Triple &Target =
1079
8
      IvarDecl->getASTContext().getTargetInfo().getTriple();
1080
1081
8
  if (!Target.isMacOSX())
1082
4
    return false;
1083
1084
4
  if (PropImpl->getPropertyDecl()->getSetterMethodDecl())
1085
2
    return false;
1086
1087
2
  return true;
1088
4
}
1089
1090
50
void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
1091
50
  Mgr.registerChecker<ObjCDeallocChecker>();
1092
50
}
1093
1094
110
bool ento::shouldRegisterObjCDeallocChecker(const CheckerManager &mgr) {
1095
  // These checker only makes sense under MRR.
1096
110
  const LangOptions &LO = mgr.getLangOpts();
1097
110
  return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount;
1098
110
}