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);
329void ObjCDeallocChecker::checkPreObjCMessage(
332 SVal DeallocedInstance;
333 if (!instanceDeallocIsOnStack(
C, DeallocedInstance))
348 if (diagnoseExtraRelease(ReleasedValue,M,
C))
353 ReleasedValue = getValueReleasedByNillingOut(M,
C);
359 transitionToReleaseValue(
C, ReleasedValue);
364void ObjCDeallocChecker::checkPreCall(
const CallEvent &Call,
367 if (II != Block_releaseII)
370 if (
Call.getNumArgs() != 1)
377 transitionToReleaseValue(
C, ReleasedValue);
381void ObjCDeallocChecker::checkPostObjCMessage(
386 if (isSuperDeallocMessage(M))
387 diagnoseMissingReleases(
C);
392void ObjCDeallocChecker::checkEndFunction(
394 diagnoseMissingReleases(
C);
398void ObjCDeallocChecker::checkPreStmt(
400 diagnoseMissingReleases(
C);
406 bool Assumption)
const {
407 if (State->get<UnreleasedIvarMap>().isEmpty())
410 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.
getAsSymbol());
424 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
425 const llvm::APInt &RHS = SIE->getRHS();
428 NullSymbol = SIE->getLHS();
429 }
else if (
auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
430 const llvm::APInt &LHS = SIE->getLHS();
433 NullSymbol = SIE->getRHS();
438 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
442 State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
452 if (State->get<UnreleasedIvarMap>().isEmpty())
459 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
460 if (OMC && isSuperDeallocMessage(*OMC))
463 for (
const auto &Sym : Escaped) {
464 if (!Call || (Call && !
Call->isInSystemHeader())) {
472 State = State->remove<UnreleasedIvarMap>(Sym);
476 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
480 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
488void ObjCDeallocChecker::diagnoseMissingReleases(
CheckerContext &
C)
const {
492 if (!isInInstanceDealloc(
C, SelfVal))
504 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
508 SymbolSet NewUnreleased = *OldUnreleased;
509 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
513 for (
auto *IvarSymbol : *OldUnreleased) {
515 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
526 cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
531 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
533 if (State->getStateManager()
534 .getConstraintManager()
535 .isNull(State, IvarSymbol)
536 .isConstrainedTrue()) {
542 ErrNode =
C.generateNonFatalErrorNode();
549 llvm::raw_string_ostream OS(Buf);
556 if (classHasSeparateTeardown(Interface))
569 OS <<
"The '" << *IvarDecl <<
"' ivar in '" << *ImplDecl
577 OS <<
" by a synthesized property but not released"
578 " before '[super dealloc]'";
580 auto BR = std::make_unique<PathSensitiveBugReport>(*MissingReleaseBugType,
582 C.emitReport(std::move(BR));
585 if (NewUnreleased.isEmpty()) {
586 State = State->remove<UnreleasedIvarMap>(SelfSym);
588 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
592 C.addTransition(State, ErrNode);
593 }
else if (State != InitialState) {
594 C.addTransition(State);
600 assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
607ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
609 SVal DeallocedInstance;
610 if (!isInInstanceDealloc(
C, DeallocedInstance))
614 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
627 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
629 Container->FindPropertyImplIvarDecl(IvarDecl->
getIdentifier());
636bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
645 findPropertyOnDeallocatingInstance(ReleasedValue,
C);
652 if (getDeallocReleaseRequirement(PropImpl) !=
676 llvm::raw_string_ostream OS(Buf);
681 isReleasedByCIFilterDealloc(PropImpl)
684 const ObjCImplDecl *Container = getContainingObjCImpl(
C.getLocationContext());
686 <<
"' ivar in '" << *Container;
689 if (isReleasedByCIFilterDealloc(PropImpl)) {
690 OS <<
"' will be released by '-[CIFilter dealloc]' but also released here";
692 OS <<
"' was synthesized for ";
697 OS <<
"an assign, readwrite";
699 OS <<
" property but was released in 'dealloc'";
702 auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType,
706 C.emitReport(std::move(BR));
714bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
725 findPropertyOnDeallocatingInstance(DeallocedValue,
C);
729 if (getDeallocReleaseRequirement(PropImpl) !=
739 llvm::raw_string_ostream OS(Buf);
742 <<
"' should be released rather than deallocated";
744 auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType,
748 C.emitReport(std::move(BR));
753ObjCDeallocChecker::ObjCDeallocChecker()
754 : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr),
755 CIFilterII(nullptr) {
757 MissingReleaseBugType.reset(
758 new BugType(
this,
"Missing ivar release (leak)",
761 ExtraReleaseBugType.reset(
762 new BugType(
this,
"Extra ivar release",
765 MistakenDeallocBugType.reset(
766 new BugType(
this,
"Mistaken dealloc",
770void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
775 NSObjectII = &Ctx.
Idents.
get(
"NSObject");
776 SenTestCaseII = &Ctx.
Idents.
get(
"SenTestCase");
777 XCTestCaseII = &Ctx.
Idents.
get(
"XCTestCase");
778 Block_releaseII = &Ctx.
Idents.
get(
"_Block_release");
779 CIFilterII = &Ctx.
Idents.
get(
"CIFilter");
788bool ObjCDeallocChecker::isSuperDeallocMessage(
798ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
799 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
813 auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->
getDeclContext());
816 if (!CatDecl || !CatDecl->IsClassExtension())
822 auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I);
823 if (!ShadowedPropDecl)
826 if (ShadowedPropDecl->isInstanceProperty()) {
827 assert(ShadowedPropDecl->isReadOnly());
828 return ShadowedPropDecl;
845 removeValueRequiringRelease(InitialState, InstanceSym,
Value);
847 if (ReleasedState != InitialState) {
848 C.addTransition(ReleasedState);
862 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
867 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
868 SymbolSet NewUnreleased = *Unreleased;
869 for (
auto &Sym : *Unreleased) {
870 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
871 assert(UnreleasedRegion);
873 NewUnreleased = F.remove(NewUnreleased, Sym);
877 if (NewUnreleased.isEmpty()) {
878 return State->remove<UnreleasedIvarMap>(Instance);
881 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
900 if (isReleasedByCIFilterDealloc(PropImpl))
903 if (isNibLoadedIvarWithoutRetain(PropImpl))
920 llvm_unreachable(
"Unrecognized setter kind");
926ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
941 std::tie(notNilState, nilState) =
943 if (!(nilState && !notNilState))
956 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
957 std::optional<Loc> LValLoc = LVal.
getAs<
Loc>();
961 SVal CurrentValInIvar = State->getSVal(*LValLoc);
969 SVal &SelfValOut)
const {
970 return isInInstanceDealloc(
C,
C.getLocationContext(), SelfValOut);
978 SVal &SelfValOut)
const {
979 auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->
getDecl());
984 assert(SelfDecl &&
"No self in -dealloc?");
987 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
994bool ObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext &
C,
995 SVal &InstanceValOut)
const {
999 if (isInInstanceDealloc(
C, LCtx, InstanceValOut))
1011bool ObjCDeallocChecker::classHasSeparateTeardown(
1014 for ( ;
ID ;
ID =
ID->getSuperClass()) {
1017 if (II == NSObjectII)
1024 if (II == XCTestCaseII || II == SenTestCaseII)
1039bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1045 const char *ReleasePrefix =
"input";
1046 if (!(PropName.startswith(ReleasePrefix) ||
1047 IvarName.startswith(ReleasePrefix))) {
1053 for ( ;
ID ;
ID =
ID->getSuperClass()) {
1055 if (II == CIFilterII)
1070bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1073 if (!IvarDecl->
hasAttr<IBOutletAttr>())
1076 const llvm::Triple &
Target =
1092bool 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
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.
ObjCImplementationDecl * getImplementation() const
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.
SymbolRef getSymbol() const
It might return null.
TypedValueRegion - An abstract class representing regions having a typed value.
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.
bool Call(InterpState &S, CodePtr OpPC, const Function *Func)
@ C
Languages that the frontend can parse and compile.