1 //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
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.
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'.
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.
28 //===----------------------------------------------------------------------===//
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"
50 using namespace clang
;
53 /// Indicates whether an instance variable is required to be released in
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.
60 /// The instance variable must not be directly released with -release.
61 MustNotReleaseDirectly
,
63 /// The requirement for the instance variable could not be determined.
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 const ObjCPropertyDecl
**PD
) {
73 if (I
->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize
)
76 (*ID
) = I
->getPropertyIvarDecl();
80 QualType T
= (*ID
)->getType();
81 if (!T
->isObjCRetainableType())
84 (*PD
) = I
->getPropertyDecl();
85 // Shouldn't be able to synthesize a property that doesn't exist.
93 class ObjCDeallocChecker
94 : public Checker
<check::ASTDecl
<ObjCImplementationDecl
>,
95 check::PreObjCMessage
, check::PostObjCMessage
,
97 check::BeginFunction
, check::EndFunction
,
100 check::PreStmt
<ReturnStmt
>> {
102 mutable IdentifierInfo
*NSObjectII
, *SenTestCaseII
, *XCTestCaseII
,
103 *Block_releaseII
, *CIFilterII
;
105 mutable Selector DeallocSel
, ReleaseSel
;
107 std::unique_ptr
<BugType
> MissingReleaseBugType
;
108 std::unique_ptr
<BugType
> ExtraReleaseBugType
;
109 std::unique_ptr
<BugType
> MistakenDeallocBugType
;
112 ObjCDeallocChecker();
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;
121 ProgramStateRef
evalAssume(ProgramStateRef State
, SVal Cond
,
122 bool Assumption
) const;
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;
132 void diagnoseMissingReleases(CheckerContext
&C
) const;
134 bool diagnoseExtraRelease(SymbolRef ReleasedValue
, const ObjCMethodCall
&M
,
135 CheckerContext
&C
) const;
137 bool diagnoseMistakenDealloc(SymbolRef DeallocedValue
,
138 const ObjCMethodCall
&M
,
139 CheckerContext
&C
) const;
141 SymbolRef
getValueReleasedByNillingOut(const ObjCMethodCall
&M
,
142 CheckerContext
&C
) const;
144 const ObjCIvarRegion
*getIvarRegionForIvarSymbol(SymbolRef IvarSym
) const;
145 SymbolRef
getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym
) const;
147 const ObjCPropertyImplDecl
*
148 findPropertyOnDeallocatingInstance(SymbolRef IvarSym
,
149 CheckerContext
&C
) const;
152 getDeallocReleaseRequirement(const ObjCPropertyImplDecl
*PropImpl
) const;
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;
160 bool isSuperDeallocMessage(const ObjCMethodCall
&M
) const;
162 const ObjCImplDecl
*getContainingObjCImpl(const LocationContext
*LCtx
) const;
164 const ObjCPropertyDecl
*
165 findShadowedPropertyDecl(const ObjCPropertyImplDecl
*PropImpl
) const;
167 void transitionToReleaseValue(CheckerContext
&C
, SymbolRef Value
) const;
168 ProgramStateRef
removeValueRequiringRelease(ProgramStateRef State
,
169 SymbolRef InstanceSym
,
170 SymbolRef ValueSym
) const;
172 void initIdentifierInfoAndSelectors(ASTContext
&Ctx
) const;
174 bool classHasSeparateTeardown(const ObjCInterfaceDecl
*ID
) const;
176 bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl
*PropImpl
) const;
177 bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl
*PropImpl
) const;
179 } // End anonymous namespace.
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
)
188 /// An AST check that diagnose when the class requires a -dealloc method and
190 void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl
*D
,
191 AnalysisManager
&Mgr
,
192 BugReporter
&BR
) const {
193 assert(Mgr
.getLangOpts().getGC() != LangOptions::GCOnly
);
194 assert(!Mgr
.getLangOpts().ObjCAutoRefCount
);
195 initIdentifierInfoAndSelectors(Mgr
.getASTContext());
197 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 if (classHasSeparateTeardown(ID
))
203 // Does the class contain any synthesized properties that are retainable?
204 // If not, skip the check entirely.
205 const ObjCPropertyImplDecl
*PropImplRequiringRelease
= nullptr;
206 bool HasOthers
= false;
207 for (const auto *I
: D
->property_impls()) {
208 if (getDeallocReleaseRequirement(I
) == ReleaseRequirement::MustRelease
) {
209 if (!PropImplRequiringRelease
)
210 PropImplRequiringRelease
= I
;
218 if (!PropImplRequiringRelease
)
221 const ObjCMethodDecl
*MD
= nullptr;
223 // Scan the instance methods for "dealloc".
224 for (const auto *I
: D
->instance_methods()) {
225 if (I
->getSelector() == DeallocSel
) {
231 if (!MD
) { // No dealloc found.
232 const char* Name
= "Missing -dealloc";
235 llvm::raw_string_ostream
OS(Buf
);
236 OS
<< "'" << *D
<< "' lacks a 'dealloc' instance method but "
237 << "must release '" << *PropImplRequiringRelease
->getPropertyIvarDecl()
242 PathDiagnosticLocation DLoc
=
243 PathDiagnosticLocation::createBegin(D
, BR
.getSourceManager());
245 BR
.EmitBasicReport(D
, this, Name
, categories::CoreFoundationObjectiveC
,
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 CheckerContext
&C
) const {
256 initIdentifierInfoAndSelectors(C
.getASTContext());
258 // Only do this if the current method is -dealloc.
260 if (!isInInstanceDealloc(C
, SelfVal
))
263 SymbolRef SelfSymbol
= SelfVal
.getAsSymbol();
265 const LocationContext
*LCtx
= C
.getLocationContext();
266 ProgramStateRef InitialState
= C
.getState();
268 ProgramStateRef State
= InitialState
;
270 SymbolSet::Factory
&F
= State
->getStateManager().get_context
<SymbolSet
>();
272 // Symbols that must be released by the end of the -dealloc;
273 SymbolSet RequiredReleases
= F
.getEmptySet();
275 // If we're an inlined -dealloc, we should add our symbols to the existing
276 // set from our subclass.
277 if (const SymbolSet
*CurrSet
= State
->get
<UnreleasedIvarMap
>(SelfSymbol
))
278 RequiredReleases
= *CurrSet
;
280 for (auto *PropImpl
: getContainingObjCImpl(LCtx
)->property_impls()) {
281 ReleaseRequirement Requirement
= getDeallocReleaseRequirement(PropImpl
);
282 if (Requirement
!= ReleaseRequirement::MustRelease
)
285 SVal LVal
= State
->getLValue(PropImpl
->getPropertyIvarDecl(), SelfVal
);
286 std::optional
<Loc
> LValLoc
= LVal
.getAs
<Loc
>();
290 SVal InitialVal
= State
->getSVal(*LValLoc
);
291 SymbolRef Symbol
= InitialVal
.getAsSymbol();
292 if (!Symbol
|| !isa
<SymbolRegionValue
>(Symbol
))
295 // Mark the value as requiring a release.
296 RequiredReleases
= F
.add(RequiredReleases
, Symbol
);
299 if (!RequiredReleases
.isEmpty()) {
300 State
= State
->set
<UnreleasedIvarMap
>(SelfSymbol
, RequiredReleases
);
303 if (State
!= InitialState
) {
304 C
.addTransition(State
);
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 ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym
) const {
312 return dyn_cast_or_null
<ObjCIvarRegion
>(IvarSym
->getOriginRegion());
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.
318 ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym
) const {
320 const ObjCIvarRegion
*IvarRegion
= getIvarRegionForIvarSymbol(IvarSym
);
324 const SymbolicRegion
*SR
= IvarRegion
->getSymbolicBase();
325 assert(SR
&& "Symbolic base should not be nullptr");
326 return SR
->getSymbol();
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 const ObjCMethodCall
&M
, CheckerContext
&C
) const {
333 // Only run if -dealloc is on the stack.
334 SVal DeallocedInstance
;
335 if (!instanceDeallocIsOnStack(C
, DeallocedInstance
))
338 SymbolRef ReleasedValue
= nullptr;
340 if (M
.getSelector() == ReleaseSel
) {
341 ReleasedValue
= M
.getReceiverSVal().getAsSymbol();
342 } else if (M
.getSelector() == DeallocSel
&& !M
.isReceiverSelfOrSuper()) {
343 if (diagnoseMistakenDealloc(M
.getReceiverSVal().getAsSymbol(), M
, C
))
348 // An instance variable symbol was released with -release:
349 // [_property release];
350 if (diagnoseExtraRelease(ReleasedValue
,M
, C
))
353 // An instance variable symbol was released nilling out its property:
354 // self.property = nil;
355 ReleasedValue
= getValueReleasedByNillingOut(M
, C
);
361 transitionToReleaseValue(C
, ReleasedValue
);
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 CheckerContext
&C
) const {
368 const IdentifierInfo
*II
= Call
.getCalleeIdentifier();
369 if (II
!= Block_releaseII
)
372 if (Call
.getNumArgs() != 1)
375 SymbolRef ReleasedValue
= Call
.getArgSVal(0).getAsSymbol();
379 transitionToReleaseValue(C
, ReleasedValue
);
381 /// If the message was a call to '[super dealloc]', diagnose any missing
383 void ObjCDeallocChecker::checkPostObjCMessage(
384 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 if (isSuperDeallocMessage(M
))
389 diagnoseMissingReleases(C
);
392 /// Check for missing releases even when -dealloc does not call
393 /// '[super dealloc]'.
394 void ObjCDeallocChecker::checkEndFunction(
395 const ReturnStmt
*RS
, CheckerContext
&C
) const {
396 diagnoseMissingReleases(C
);
399 /// Check for missing releases on early return.
400 void ObjCDeallocChecker::checkPreStmt(
401 const ReturnStmt
*RS
, CheckerContext
&C
) const {
402 diagnoseMissingReleases(C
);
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 bool Assumption
) const {
409 if (State
->get
<UnreleasedIvarMap
>().isEmpty())
412 auto *CondBSE
= dyn_cast_or_null
<BinarySymExpr
>(Cond
.getAsSymbol());
416 BinaryOperator::Opcode OpCode
= CondBSE
->getOpcode();
425 SymbolRef NullSymbol
= nullptr;
426 if (auto *SIE
= dyn_cast
<SymIntExpr
>(CondBSE
)) {
427 const llvm::APInt
&RHS
= SIE
->getRHS();
430 NullSymbol
= SIE
->getLHS();
431 } else if (auto *SIE
= dyn_cast
<IntSymExpr
>(CondBSE
)) {
432 const llvm::APInt
&LHS
= SIE
->getLHS();
435 NullSymbol
= SIE
->getRHS();
440 SymbolRef InstanceSymbol
= getInstanceSymbolFromIvarSymbol(NullSymbol
);
444 State
= removeValueRequiringRelease(State
, InstanceSymbol
, NullSymbol
);
449 /// If a symbol escapes conservatively assume unseen code released it.
450 ProgramStateRef
ObjCDeallocChecker::checkPointerEscape(
451 ProgramStateRef State
, const InvalidatedSymbols
&Escaped
,
452 const CallEvent
*Call
, PointerEscapeKind Kind
) const {
454 if (State
->get
<UnreleasedIvarMap
>().isEmpty())
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 auto *OMC
= dyn_cast_or_null
<ObjCMethodCall
>(Call
);
462 if (OMC
&& isSuperDeallocMessage(*OMC
))
465 for (const auto &Sym
: Escaped
) {
466 if (!Call
|| (Call
&& !Call
->isInSystemHeader())) {
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
474 State
= State
->remove
<UnreleasedIvarMap
>(Sym
);
478 SymbolRef InstanceSymbol
= getInstanceSymbolFromIvarSymbol(Sym
);
482 State
= removeValueRequiringRelease(State
, InstanceSymbol
, Sym
);
488 /// Report any unreleased instance variables for the current instance being
490 void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext
&C
) const {
491 ProgramStateRef State
= C
.getState();
494 if (!isInInstanceDealloc(C
, SelfVal
))
497 const MemRegion
*SelfRegion
= SelfVal
.castAs
<loc::MemRegionVal
>().getRegion();
498 const LocationContext
*LCtx
= C
.getLocationContext();
500 ExplodedNode
*ErrNode
= nullptr;
502 SymbolRef SelfSym
= SelfVal
.getAsSymbol();
506 const SymbolSet
*OldUnreleased
= State
->get
<UnreleasedIvarMap
>(SelfSym
);
510 SymbolSet NewUnreleased
= *OldUnreleased
;
511 SymbolSet::Factory
&F
= State
->getStateManager().get_context
<SymbolSet
>();
513 ProgramStateRef InitialState
= State
;
515 for (auto *IvarSymbol
: *OldUnreleased
) {
516 const TypedValueRegion
*TVR
=
517 cast
<SymbolRegionValue
>(IvarSymbol
)->getRegion();
518 const ObjCIvarRegion
*IvarRegion
= cast
<ObjCIvarRegion
>(TVR
);
520 // Don't warn if the ivar is not for this instance.
521 if (SelfRegion
!= IvarRegion
->getSuperRegion())
524 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 if (IvarDecl
->getContainingInterface() !=
528 cast
<ObjCMethodDecl
>(LCtx
->getDecl())->getClassInterface())
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 NewUnreleased
= F
.remove(NewUnreleased
, IvarSymbol
);
535 if (State
->getStateManager()
536 .getConstraintManager()
537 .isNull(State
, IvarSymbol
)
538 .isConstrainedTrue()) {
542 // A missing release manifests as a leak, so treat as a non-fatal error.
544 ErrNode
= C
.generateNonFatalErrorNode();
545 // If we've already reached this node on another path, return without
551 llvm::raw_string_ostream
OS(Buf
);
553 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 if (classHasSeparateTeardown(Interface
))
561 ObjCImplDecl
*ImplDecl
= Interface
->getImplementation();
563 const ObjCPropertyImplDecl
*PropImpl
=
564 ImplDecl
->FindPropertyImplIvarDecl(IvarDecl
->getIdentifier());
566 const ObjCPropertyDecl
*PropDecl
= PropImpl
->getPropertyDecl();
568 assert(PropDecl
->getSetterKind() == ObjCPropertyDecl::Copy
||
569 PropDecl
->getSetterKind() == ObjCPropertyDecl::Retain
);
571 OS
<< "The '" << *IvarDecl
<< "' ivar in '" << *ImplDecl
574 if (PropDecl
->getSetterKind() == ObjCPropertyDecl::Retain
)
579 OS
<< " by a synthesized property but not released"
580 " before '[super dealloc]'";
582 auto BR
= std::make_unique
<PathSensitiveBugReport
>(*MissingReleaseBugType
,
584 C
.emitReport(std::move(BR
));
587 if (NewUnreleased
.isEmpty()) {
588 State
= State
->remove
<UnreleasedIvarMap
>(SelfSym
);
590 State
= State
->set
<UnreleasedIvarMap
>(SelfSym
, NewUnreleased
);
594 C
.addTransition(State
, ErrNode
);
595 } else if (State
!= InitialState
) {
596 C
.addTransition(State
);
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 assert(!LCtx
->inTopFrame() || State
->get
<UnreleasedIvarMap
>().isEmpty());
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 SymbolRef IvarSym
, CheckerContext
&C
) const {
611 SVal DeallocedInstance
;
612 if (!isInInstanceDealloc(C
, DeallocedInstance
))
615 // Try to get the region from which the ivar value was loaded.
616 auto *IvarRegion
= getIvarRegionForIvarSymbol(IvarSym
);
620 // Don't try to find the property if the ivar was not loaded from the
622 if (DeallocedInstance
.castAs
<loc::MemRegionVal
>().getRegion() !=
623 IvarRegion
->getSuperRegion())
626 const LocationContext
*LCtx
= C
.getLocationContext();
627 const ObjCIvarDecl
*IvarDecl
= IvarRegion
->getDecl();
629 const ObjCImplDecl
*Container
= getContainingObjCImpl(LCtx
);
630 const ObjCPropertyImplDecl
*PropImpl
=
631 Container
->FindPropertyImplIvarDecl(IvarDecl
->getIdentifier());
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
638 bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue
,
639 const ObjCMethodCall
&M
,
640 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 const ObjCPropertyImplDecl
*PropImpl
=
647 findPropertyOnDeallocatingInstance(ReleasedValue
, C
);
652 // If the ivar belongs to a property that must not be released directly
653 // in dealloc, emit a warning.
654 if (getDeallocReleaseRequirement(PropImpl
) !=
655 ReleaseRequirement::MustNotReleaseDirectly
) {
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 const ObjCPropertyDecl
*PropDecl
= findShadowedPropertyDecl(PropImpl
);
667 if (PropDecl
->isReadOnly())
670 PropDecl
= PropImpl
->getPropertyDecl();
673 ExplodedNode
*ErrNode
= C
.generateNonFatalErrorNode();
678 llvm::raw_string_ostream
OS(Buf
);
680 assert(PropDecl
->getSetterKind() == ObjCPropertyDecl::Weak
||
681 (PropDecl
->getSetterKind() == ObjCPropertyDecl::Assign
&&
682 !PropDecl
->isReadOnly()) ||
683 isReleasedByCIFilterDealloc(PropImpl
)
686 const ObjCImplDecl
*Container
= getContainingObjCImpl(C
.getLocationContext());
687 OS
<< "The '" << *PropImpl
->getPropertyIvarDecl()
688 << "' ivar in '" << *Container
;
691 if (isReleasedByCIFilterDealloc(PropImpl
)) {
692 OS
<< "' will be released by '-[CIFilter dealloc]' but also released here";
694 OS
<< "' was synthesized for ";
696 if (PropDecl
->getSetterKind() == ObjCPropertyDecl::Weak
)
699 OS
<< "an assign, readwrite";
701 OS
<< " property but was released in 'dealloc'";
704 auto BR
= std::make_unique
<PathSensitiveBugReport
>(*ExtraReleaseBugType
,
706 BR
->addRange(M
.getOriginExpr()->getSourceRange());
708 C
.emitReport(std::move(BR
));
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
716 bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue
,
717 const ObjCMethodCall
&M
,
718 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?
724 // Find the property backing the instance variable that M
726 const ObjCPropertyImplDecl
*PropImpl
=
727 findPropertyOnDeallocatingInstance(DeallocedValue
, C
);
731 if (getDeallocReleaseRequirement(PropImpl
) !=
732 ReleaseRequirement::MustRelease
) {
736 ExplodedNode
*ErrNode
= C
.generateErrorNode();
741 llvm::raw_string_ostream
OS(Buf
);
743 OS
<< "'" << *PropImpl
->getPropertyIvarDecl()
744 << "' should be released rather than deallocated";
746 auto BR
= std::make_unique
<PathSensitiveBugReport
>(*MistakenDeallocBugType
,
748 BR
->addRange(M
.getOriginExpr()->getSourceRange());
750 C
.emitReport(std::move(BR
));
755 ObjCDeallocChecker::ObjCDeallocChecker()
756 : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr),
757 Block_releaseII(nullptr), CIFilterII(nullptr) {
759 MissingReleaseBugType
.reset(
760 new BugType(this, "Missing ivar release (leak)",
761 categories::MemoryRefCount
));
763 ExtraReleaseBugType
.reset(
764 new BugType(this, "Extra ivar release",
765 categories::MemoryRefCount
));
767 MistakenDeallocBugType
.reset(
768 new BugType(this, "Mistaken dealloc",
769 categories::MemoryRefCount
));
772 void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
773 ASTContext
&Ctx
) const {
777 NSObjectII
= &Ctx
.Idents
.get("NSObject");
778 SenTestCaseII
= &Ctx
.Idents
.get("SenTestCase");
779 XCTestCaseII
= &Ctx
.Idents
.get("XCTestCase");
780 Block_releaseII
= &Ctx
.Idents
.get("_Block_release");
781 CIFilterII
= &Ctx
.Idents
.get("CIFilter");
783 IdentifierInfo
*DeallocII
= &Ctx
.Idents
.get("dealloc");
784 IdentifierInfo
*ReleaseII
= &Ctx
.Idents
.get("release");
785 DeallocSel
= Ctx
.Selectors
.getSelector(0, &DeallocII
);
786 ReleaseSel
= Ctx
.Selectors
.getSelector(0, &ReleaseII
);
789 /// Returns true if M is a call to '[super dealloc]'.
790 bool ObjCDeallocChecker::isSuperDeallocMessage(
791 const ObjCMethodCall
&M
) const {
792 if (M
.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance
)
795 return M
.getSelector() == DeallocSel
;
798 /// Returns the ObjCImplDecl containing the method declaration in LCtx.
800 ObjCDeallocChecker::getContainingObjCImpl(const LocationContext
*LCtx
) const {
801 auto *MD
= cast
<ObjCMethodDecl
>(LCtx
->getDecl());
802 return cast
<ObjCImplDecl
>(MD
->getDeclContext());
805 /// Returns the property that shadowed by PropImpl if one exists and
806 /// nullptr otherwise.
807 const ObjCPropertyDecl
*ObjCDeallocChecker::findShadowedPropertyDecl(
808 const ObjCPropertyImplDecl
*PropImpl
) const {
809 const ObjCPropertyDecl
*PropDecl
= PropImpl
->getPropertyDecl();
811 // Only readwrite properties can shadow.
812 if (PropDecl
->isReadOnly())
815 auto *CatDecl
= dyn_cast
<ObjCCategoryDecl
>(PropDecl
->getDeclContext());
817 // Only class extensions can contain shadowing properties.
818 if (!CatDecl
|| !CatDecl
->IsClassExtension())
821 IdentifierInfo
*ID
= PropDecl
->getIdentifier();
822 DeclContext::lookup_result R
= CatDecl
->getClassInterface()->lookup(ID
);
823 for (const NamedDecl
*D
: R
) {
824 auto *ShadowedPropDecl
= dyn_cast
<ObjCPropertyDecl
>(D
);
825 if (!ShadowedPropDecl
)
828 if (ShadowedPropDecl
->isInstanceProperty()) {
829 assert(ShadowedPropDecl
->isReadOnly());
830 return ShadowedPropDecl
;
837 /// Add a transition noting the release of the given value.
838 void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext
&C
,
839 SymbolRef Value
) const {
841 SymbolRef InstanceSym
= getInstanceSymbolFromIvarSymbol(Value
);
844 ProgramStateRef InitialState
= C
.getState();
846 ProgramStateRef ReleasedState
=
847 removeValueRequiringRelease(InitialState
, InstanceSym
, Value
);
849 if (ReleasedState
!= InitialState
) {
850 C
.addTransition(ReleasedState
);
854 /// Remove the Value requiring a release from the tracked set for
855 /// Instance and return the resultant state.
856 ProgramStateRef
ObjCDeallocChecker::removeValueRequiringRelease(
857 ProgramStateRef State
, SymbolRef Instance
, SymbolRef Value
) const {
860 const ObjCIvarRegion
*RemovedRegion
= getIvarRegionForIvarSymbol(Value
);
864 const SymbolSet
*Unreleased
= State
->get
<UnreleasedIvarMap
>(Instance
);
868 // Mark the value as no longer requiring a release.
869 SymbolSet::Factory
&F
= State
->getStateManager().get_context
<SymbolSet
>();
870 SymbolSet NewUnreleased
= *Unreleased
;
871 for (auto &Sym
: *Unreleased
) {
872 const ObjCIvarRegion
*UnreleasedRegion
= getIvarRegionForIvarSymbol(Sym
);
873 assert(UnreleasedRegion
);
874 if (RemovedRegion
->getDecl() == UnreleasedRegion
->getDecl()) {
875 NewUnreleased
= F
.remove(NewUnreleased
, Sym
);
879 if (NewUnreleased
.isEmpty()) {
880 return State
->remove
<UnreleasedIvarMap
>(Instance
);
883 return State
->set
<UnreleasedIvarMap
>(Instance
, NewUnreleased
);
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 const ObjCPropertyImplDecl
*PropImpl
) const {
890 const ObjCIvarDecl
*IvarDecl
;
891 const ObjCPropertyDecl
*PropDecl
;
892 if (!isSynthesizedRetainableProperty(PropImpl
, &IvarDecl
, &PropDecl
))
893 return ReleaseRequirement::Unknown
;
895 ObjCPropertyDecl::SetterKind SK
= PropDecl
->getSetterKind();
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 case ObjCPropertyDecl::Retain
:
901 case ObjCPropertyDecl::Copy
:
902 if (isReleasedByCIFilterDealloc(PropImpl
))
903 return ReleaseRequirement::MustNotReleaseDirectly
;
905 if (isNibLoadedIvarWithoutRetain(PropImpl
))
906 return ReleaseRequirement::Unknown
;
908 return ReleaseRequirement::MustRelease
;
910 case ObjCPropertyDecl::Weak
:
911 return ReleaseRequirement::MustNotReleaseDirectly
;
913 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
917 if (PropDecl
->isReadOnly())
918 return ReleaseRequirement::Unknown
;
920 return ReleaseRequirement::MustNotReleaseDirectly
;
922 llvm_unreachable("Unrecognized setter kind");
925 /// Returns the released value if M is a call a setter that releases
926 /// and nils out its underlying instance variable.
928 ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall
&M
,
929 CheckerContext
&C
) const {
930 SVal ReceiverVal
= M
.getReceiverSVal();
931 if (!ReceiverVal
.isValid())
934 if (M
.getNumArgs() == 0)
937 if (!M
.getArgExpr(0)->getType()->isObjCRetainableType())
940 // Is the first argument nil?
941 SVal Arg
= M
.getArgSVal(0);
942 ProgramStateRef notNilState
, nilState
;
943 std::tie(notNilState
, nilState
) =
944 M
.getState()->assume(Arg
.castAs
<DefinedOrUnknownSVal
>());
945 if (!(nilState
&& !notNilState
))
948 const ObjCPropertyDecl
*Prop
= M
.getAccessedProperty();
952 ObjCIvarDecl
*PropIvarDecl
= Prop
->getPropertyIvarDecl();
956 ProgramStateRef State
= C
.getState();
958 SVal LVal
= State
->getLValue(PropIvarDecl
, ReceiverVal
);
959 std::optional
<Loc
> LValLoc
= LVal
.getAs
<Loc
>();
963 SVal CurrentValInIvar
= State
->getSVal(*LValLoc
);
964 return CurrentValInIvar
.getAsSymbol();
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
970 bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext
&C
,
971 SVal
&SelfValOut
) const {
972 return isInInstanceDealloc(C
, C
.getLocationContext(), SelfValOut
);
975 /// Returns true if LCtx is a call to -dealloc and false
976 /// otherwise. If true, it also sets SelfValOut to the value of
978 bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext
&C
,
979 const LocationContext
*LCtx
,
980 SVal
&SelfValOut
) const {
981 auto *MD
= dyn_cast
<ObjCMethodDecl
>(LCtx
->getDecl());
982 if (!MD
|| !MD
->isInstanceMethod() || MD
->getSelector() != DeallocSel
)
985 const ImplicitParamDecl
*SelfDecl
= LCtx
->getSelfDecl();
986 assert(SelfDecl
&& "No self in -dealloc?");
988 ProgramStateRef State
= C
.getState();
989 SelfValOut
= State
->getSVal(State
->getRegion(SelfDecl
, LCtx
));
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 SVal
&InstanceValOut
) const {
998 const LocationContext
*LCtx
= C
.getLocationContext();
1001 if (isInInstanceDealloc(C
, LCtx
, InstanceValOut
))
1004 LCtx
= LCtx
->getParent();
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 const ObjCInterfaceDecl
*ID
) const {
1015 // Suppress if the class is not a subclass of NSObject.
1016 for ( ; ID
; ID
= ID
->getSuperClass()) {
1017 IdentifierInfo
*II
= ID
->getIdentifier();
1019 if (II
== NSObjectII
)
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 if (II
== XCTestCaseII
|| II
== SenTestCaseII
)
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.
1039 /// This method returns true if the property will be released by
1040 /// -[CIFilter dealloc].
1041 bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1042 const ObjCPropertyImplDecl
*PropImpl
) const {
1043 assert(PropImpl
->getPropertyIvarDecl());
1044 StringRef PropName
= PropImpl
->getPropertyDecl()->getName();
1045 StringRef IvarName
= PropImpl
->getPropertyIvarDecl()->getName();
1047 const char *ReleasePrefix
= "input";
1048 if (!(PropName
.startswith(ReleasePrefix
) ||
1049 IvarName
.startswith(ReleasePrefix
))) {
1053 const ObjCInterfaceDecl
*ID
=
1054 PropImpl
->getPropertyIvarDecl()->getContainingInterface();
1055 for ( ; ID
; ID
= ID
->getSuperClass()) {
1056 IdentifierInfo
*II
= ID
->getIdentifier();
1057 if (II
== CIFilterII
)
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.
1067 /// On macOS, if there is no setter, the nib-loading code sets the ivar
1068 /// directly, without retaining the value,
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 const ObjCPropertyImplDecl
*PropImpl
) const {
1074 const ObjCIvarDecl
*IvarDecl
= PropImpl
->getPropertyIvarDecl();
1075 if (!IvarDecl
->hasAttr
<IBOutletAttr
>())
1078 const llvm::Triple
&Target
=
1079 IvarDecl
->getASTContext().getTargetInfo().getTriple();
1081 if (!Target
.isMacOSX())
1084 if (PropImpl
->getPropertyDecl()->getSetterMethodDecl())
1090 void ento::registerObjCDeallocChecker(CheckerManager
&Mgr
) {
1091 Mgr
.registerChecker
<ObjCDeallocChecker
>();
1094 bool ento::shouldRegisterObjCDeallocChecker(const CheckerManager
&mgr
) {
1095 // These checker only makes sense under MRR.
1096 const LangOptions
&LO
= mgr
.getLangOpts();
1097 return LO
.getGC() != LangOptions::GCOnly
&& !LO
.ObjCAutoRefCount
;