47#include "llvm/Support/raw_ostream.h"
93class 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();
122 bool Assumption)
const;
137 bool diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
148 findPropertyOnDeallocatingInstance(
SymbolRef IvarSym,
156 SVal &SelfValOut)
const;
158 SVal &InstanceValOut)
const;
172 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
200 if (classHasSeparateTeardown(ID))
206 bool HasOthers =
false;
209 if (!PropImplRequiringRelease)
210 PropImplRequiringRelease = I;
218 if (!PropImplRequiringRelease)
225 if (I->getSelector() == DeallocSel) {
232 const char* Name =
"Missing -dealloc";
235 llvm::raw_string_ostream OS(Buf);
236 OS <<
"'" << *D <<
"' lacks a 'dealloc' instance method but "
254void ObjCDeallocChecker::checkBeginFunction(
256 initIdentifierInfoAndSelectors(
C.getASTContext());
260 if (!isInInstanceDealloc(
C, SelfVal))
270 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
273 SymbolSet RequiredReleases = F.getEmptySet();
277 if (
const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
278 RequiredReleases = *CurrSet;
280 for (
auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
285 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
286 std::optional<Loc> LValLoc = LVal.
getAs<
Loc>();
290 SVal InitialVal = State->getSVal(*LValLoc);
292 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
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);
311ObjCDeallocChecker::getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const {
318ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(
SymbolRef IvarSym)
const {
320 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
325 assert(SR &&
"Symbolic base should not be nullptr");
331void ObjCDeallocChecker::checkPreObjCMessage(
334 SVal DeallocedInstance;
335 if (!instanceDeallocIsOnStack(
C, DeallocedInstance))
350 if (diagnoseExtraRelease(ReleasedValue,M,
C))
355 ReleasedValue = getValueReleasedByNillingOut(M,
C);
361 transitionToReleaseValue(
C, ReleasedValue);
369 if (II != Block_releaseII)
372 if (
Call.getNumArgs() != 1)
379 transitionToReleaseValue(
C, ReleasedValue);
383void ObjCDeallocChecker::checkPostObjCMessage(
388 if (isSuperDeallocMessage(M))
389 diagnoseMissingReleases(
C);
394void ObjCDeallocChecker::checkEndFunction(
396 diagnoseMissingReleases(
C);
400void ObjCDeallocChecker::checkPreStmt(
402 diagnoseMissingReleases(
C);
408 bool Assumption)
const {
409 if (State->get<UnreleasedIvarMap>().isEmpty())
412 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.
getAsSymbol());
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);
454 if (State->get<UnreleasedIvarMap>().isEmpty())
461 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(
Call);
462 if (OMC && isSuperDeallocMessage(*OMC))
465 for (
const auto &Sym : Escaped) {
474 State = State->remove<UnreleasedIvarMap>(Sym);
478 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
482 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
490void ObjCDeallocChecker::diagnoseMissingReleases(
CheckerContext &
C)
const {
494 if (!isInInstanceDealloc(
C, SelfVal))
506 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
510 SymbolSet NewUnreleased = *OldUnreleased;
511 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
515 for (
auto *IvarSymbol : *OldUnreleased) {
517 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
528 cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
533 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
535 if (State->getStateManager()
536 .getConstraintManager()
537 .isNull(State, IvarSymbol)
538 .isConstrainedTrue()) {
544 ErrNode =
C.generateNonFatalErrorNode();
551 llvm::raw_string_ostream OS(Buf);
571 OS <<
"The '" << *IvarDecl <<
"' ivar in '" << *ImplDecl
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);
602 assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
609ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
611 SVal DeallocedInstance;
612 if (!isInInstanceDealloc(
C, DeallocedInstance))
616 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
629 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
631 Container->FindPropertyImplIvarDecl(IvarDecl->
getIdentifier());
638bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
647 findPropertyOnDeallocatingInstance(ReleasedValue,
C);
654 if (getDeallocReleaseRequirement(PropImpl) !=
678 llvm::raw_string_ostream OS(Buf);
683 isReleasedByCIFilterDealloc(PropImpl)
686 const ObjCImplDecl *Container = getContainingObjCImpl(
C.getLocationContext());
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 ";
699 OS <<
"an assign, readwrite";
701 OS <<
" property but was released in 'dealloc'";
704 auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType,
708 C.emitReport(std::move(BR));
716bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
727 findPropertyOnDeallocatingInstance(DeallocedValue,
C);
731 if (getDeallocReleaseRequirement(PropImpl) !=
741 llvm::raw_string_ostream OS(Buf);
744 <<
"' should be released rather than deallocated";
746 auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType,
750 C.emitReport(std::move(BR));
755ObjCDeallocChecker::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)",
763 ExtraReleaseBugType.reset(
764 new BugType(
this,
"Extra ivar release",
767 MistakenDeallocBugType.reset(
768 new BugType(
this,
"Mistaken dealloc",
772void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
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");
790bool ObjCDeallocChecker::isSuperDeallocMessage(
800ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
801 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
815 auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->
getDeclContext());
818 if (!CatDecl || !CatDecl->IsClassExtension())
824 auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(D);
825 if (!ShadowedPropDecl)
828 if (ShadowedPropDecl->isInstanceProperty()) {
829 assert(ShadowedPropDecl->isReadOnly());
830 return ShadowedPropDecl;
847 removeValueRequiringRelease(InitialState, InstanceSym,
Value);
849 if (ReleasedState != InitialState) {
850 C.addTransition(ReleasedState);
864 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
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);
875 NewUnreleased = F.remove(NewUnreleased, Sym);
879 if (NewUnreleased.isEmpty()) {
880 return State->remove<UnreleasedIvarMap>(Instance);
883 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
902 if (isReleasedByCIFilterDealloc(PropImpl))
905 if (isNibLoadedIvarWithoutRetain(PropImpl))
922 llvm_unreachable(
"Unrecognized setter kind");
928ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
943 std::tie(notNilState, nilState) =
945 if (!(nilState && !notNilState))
958 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
959 std::optional<Loc> LValLoc = LVal.
getAs<
Loc>();
963 SVal CurrentValInIvar = State->getSVal(*LValLoc);
971 SVal &SelfValOut)
const {
972 return isInInstanceDealloc(
C,
C.getLocationContext(), SelfValOut);
980 SVal &SelfValOut)
const {
981 auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->
getDecl());
986 assert(SelfDecl &&
"No self in -dealloc?");
989 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
996bool ObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext &
C,
997 SVal &InstanceValOut)
const {
1001 if (isInInstanceDealloc(
C, LCtx, InstanceValOut))
1013bool ObjCDeallocChecker::classHasSeparateTeardown(
1016 for ( ;
ID ;
ID =
ID->getSuperClass()) {
1019 if (II == NSObjectII)
1026 if (II == XCTestCaseII || II == SenTestCaseII)
1041bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1047 const char *ReleasePrefix =
"input";
1048 if (!(PropName.startswith(ReleasePrefix) ||
1049 IvarName.startswith(ReleasePrefix))) {
1055 for ( ;
ID ;
ID =
ID->getSuperClass()) {
1057 if (II == CIFilterII)
1072bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1075 if (!IvarDecl->
hasAttr<IBOutletAttr>())
1078 const llvm::Triple &
Target =
1094bool ento::shouldRegisterObjCDeallocChecker(
const CheckerManager &mgr) {
ReleaseRequirement
Indicates whether an instance variable is required to be released in -dealloc.
@ MustNotReleaseDirectly
The instance variable must not be directly released with -release.
@ Unknown
The requirement for the instance variable could not be determined.
@ MustRelease
The instance variable must be released, either by calling -release on it directly or by nilling it ou...
static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, const ObjCIvarDecl **ID, const ObjCPropertyDecl **PD)
Returns true if the property implementation is synthesized and the type of the property is retainable...
Defines the clang::LangOptions interface.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set type Name and registers the factory for such sets in the program state,...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
SelectorTable & Selectors
const TargetInfo & getTargetInfo() const
The results of name lookup within a DeclContext.
ASTContext & getASTContext() const LLVM_READONLY
DeclContext * getDeclContext()
One of these records is kept for each identifier that is lexed.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
const LocationContext * getParent() const
It might return null.
virtual bool inTopFrame() const
const ImplicitParamDecl * getSelfDecl() const
This represents a decl that may have a name.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
instmeth_range instance_methods() const
propimpl_range property_impls() const
const ObjCInterfaceDecl * getClassInterface() const
ObjCPropertyImplDecl * FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const
FindPropertyImplIvarDecl - This method lookup the ivar in the list of properties implemented in this ...
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Represents an ObjC class declaration.
ObjCIvarDecl - Represents an ObjC instance variable.
ObjCInterfaceDecl * getContainingInterface()
Return the class interface that this ivar is logically contained in; this is either the interface whe...
@ SuperInstance
The receiver is the instance of the superclass object.
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
ObjCMethodDecl - Represents an instance or class method declaration.
Selector getSelector() const
bool isInstanceMethod() const
Represents one property declaration in an Objective-C interface.
ObjCMethodDecl * getSetterMethodDecl() const
bool isReadOnly() const
isReadOnly - Return true iff the property has a setter.
ObjCIvarDecl * getPropertyIvarDecl() const
SetterKind getSetterKind() const
getSetterKind - Return the method used for doing assignment in the property setter.
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
ObjCIvarDecl * getPropertyIvarDecl() const
Kind getPropertyImplementation() const
ObjCPropertyDecl * getPropertyDecl() const
A (possibly-)qualified type.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Selector getSelector(unsigned NumArgs, IdentifierInfo **IIV)
Can create any sort of selector.
Smart pointer class that efficiently represents Objective-C method names.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isObjCRetainableType() const
const LangOptions & getLangOpts() const
ASTContext & getASTContext() override
BugReporter is a utility class for generating PathDiagnostics for analysis.
const SourceManager & getSourceManager()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
Represents an abstract call to a function or method along a particular path.
const ProgramStateRef & getState() const
The state in which the call is being evaluated.
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
const LangOptions & getLangOpts() const
MemRegion - The root abstract class for all memory regions.
const SymbolicRegion * getSymbolicBase() const
If this is a symbolic region, returns the region.
LLVM_ATTRIBUTE_RETURNS_NONNULL const ObjCIvarDecl * getDecl() const override
Represents any expression that calls an Objective-C method.
const Expr * getArgExpr(unsigned Index) const override
Returns the expression associated with a given argument.
unsigned getNumArgs() const override
Returns the number of arguments (explicit and implicit).
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
Selector getSelector() const
const ObjCPropertyDecl * getAccessedProperty() const
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getSuperRegion() const
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
SymbolicRegion - A special, "non-concrete" region.
SymbolRef getSymbol() const
It might return null.
TypedValueRegion - An abstract class representing regions having a typed value.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getRegion() const
Get the underlining region.
Defines the clang::TargetInfo interface.
const char *const CoreFoundationObjectiveC
const char *const MemoryRefCount
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.