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>> {
111 const BugType MissingReleaseBugType{
this,
"Missing ivar release (leak)",
113 const BugType ExtraReleaseBugType{
this,
"Extra ivar release",
115 const BugType MistakenDeallocBugType{
this,
"Mistaken dealloc",
127 bool Assumption)
const;
142 bool diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
153 findPropertyOnDeallocatingInstance(
SymbolRef IvarSym,
161 SVal &SelfValOut)
const;
163 SVal &InstanceValOut)
const;
177 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
198 assert(Mgr.
getLangOpts().getGC() != LangOptions::GCOnly);
205 if (classHasSeparateTeardown(ID))
211 bool HasOthers =
false;
212 for (
const auto *I :
D->property_impls()) {
214 if (!PropImplRequiringRelease)
215 PropImplRequiringRelease = I;
223 if (!PropImplRequiringRelease)
229 for (
const auto *I :
D->instance_methods()) {
230 if (I->getSelector() == DeallocSel) {
237 const char* Name =
"Missing -dealloc";
240 llvm::raw_string_ostream OS(Buf);
241 OS <<
"'" << *
D <<
"' lacks a 'dealloc' instance method but "
259void ObjCDeallocChecker::checkBeginFunction(
261 initIdentifierInfoAndSelectors(
C.getASTContext());
265 if (!isInInstanceDealloc(
C, SelfVal))
275 SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
278 SymbolSet RequiredReleases = F.getEmptySet();
282 if (
const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
283 RequiredReleases = *CurrSet;
285 for (
auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
290 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
291 std::optional<Loc> LValLoc = LVal.
getAs<
Loc>();
295 SVal InitialVal = State->getSVal(*LValLoc);
297 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
301 RequiredReleases = F.add(RequiredReleases, Symbol);
304 if (!RequiredReleases.isEmpty()) {
305 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
308 if (State != InitialState) {
309 C.addTransition(State);
316ObjCDeallocChecker::getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const {
323ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(
SymbolRef IvarSym)
const {
325 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
330 assert(SR &&
"Symbolic base should not be nullptr");
336void ObjCDeallocChecker::checkPreObjCMessage(
339 SVal DeallocedInstance;
340 if (!instanceDeallocIsOnStack(
C, DeallocedInstance))
355 if (diagnoseExtraRelease(ReleasedValue,M,
C))
360 ReleasedValue = getValueReleasedByNillingOut(M,
C);
366 transitionToReleaseValue(
C, ReleasedValue);
374 if (II != Block_releaseII)
377 if (
Call.getNumArgs() != 1)
384 transitionToReleaseValue(
C, ReleasedValue);
388void ObjCDeallocChecker::checkPostObjCMessage(
393 if (isSuperDeallocMessage(M))
394 diagnoseMissingReleases(
C);
399void ObjCDeallocChecker::checkEndFunction(
401 diagnoseMissingReleases(
C);
405void ObjCDeallocChecker::checkPreStmt(
407 diagnoseMissingReleases(
C);
413 bool Assumption)
const {
414 if (State->get<UnreleasedIvarMap>().isEmpty())
417 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.
getAsSymbol());
431 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
432 const llvm::APInt &RHS = SIE->getRHS();
435 NullSymbol = SIE->getLHS();
436 }
else if (
auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
437 const llvm::APInt &LHS = SIE->getLHS();
440 NullSymbol = SIE->getRHS();
445 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
449 State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
459 if (State->get<UnreleasedIvarMap>().isEmpty())
466 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(
Call);
467 if (OMC && isSuperDeallocMessage(*OMC))
470 for (
const auto &Sym : Escaped) {
479 State = State->remove<UnreleasedIvarMap>(Sym);
483 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
487 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
495void ObjCDeallocChecker::diagnoseMissingReleases(
CheckerContext &
C)
const {
499 if (!isInInstanceDealloc(
C, SelfVal))
511 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
515 SymbolSet NewUnreleased = *OldUnreleased;
516 SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
520 for (
auto *IvarSymbol : *OldUnreleased) {
522 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
533 cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
538 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
540 if (State->getStateManager()
541 .getConstraintManager()
542 .isNull(State, IvarSymbol)
543 .isConstrainedTrue()) {
549 ErrNode =
C.generateNonFatalErrorNode();
556 llvm::raw_string_ostream OS(Buf);
576 OS <<
"The '" << *IvarDecl <<
"' ivar in '" << *ImplDecl
584 OS <<
" by a synthesized property but not released"
585 " before '[super dealloc]'";
587 auto BR = std::make_unique<PathSensitiveBugReport>(MissingReleaseBugType,
589 C.emitReport(std::move(BR));
592 if (NewUnreleased.isEmpty()) {
593 State = State->remove<UnreleasedIvarMap>(SelfSym);
595 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
599 C.addTransition(State, ErrNode);
600 }
else if (State != InitialState) {
601 C.addTransition(State);
607 assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
614ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
616 SVal DeallocedInstance;
617 if (!isInInstanceDealloc(
C, DeallocedInstance))
621 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
634 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
636 Container->FindPropertyImplIvarDecl(IvarDecl->
getIdentifier());
643bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
652 findPropertyOnDeallocatingInstance(ReleasedValue,
C);
659 if (getDeallocReleaseRequirement(PropImpl) !=
683 llvm::raw_string_ostream OS(Buf);
688 isReleasedByCIFilterDealloc(PropImpl)
691 const ObjCImplDecl *Container = getContainingObjCImpl(
C.getLocationContext());
693 <<
"' ivar in '" << *Container;
696 if (isReleasedByCIFilterDealloc(PropImpl)) {
697 OS <<
"' will be released by '-[CIFilter dealloc]' but also released here";
699 OS <<
"' was synthesized for ";
704 OS <<
"an assign, readwrite";
706 OS <<
" property but was released in 'dealloc'";
709 auto BR = std::make_unique<PathSensitiveBugReport>(ExtraReleaseBugType, Buf,
713 C.emitReport(std::move(BR));
721bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
732 findPropertyOnDeallocatingInstance(DeallocedValue,
C);
736 if (getDeallocReleaseRequirement(PropImpl) !=
746 llvm::raw_string_ostream OS(Buf);
749 <<
"' should be released rather than deallocated";
751 auto BR = std::make_unique<PathSensitiveBugReport>(MistakenDeallocBugType,
755 C.emitReport(std::move(BR));
760void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
765 NSObjectII = &Ctx.
Idents.
get(
"NSObject");
766 SenTestCaseII = &Ctx.
Idents.
get(
"SenTestCase");
767 XCTestCaseII = &Ctx.
Idents.
get(
"XCTestCase");
768 Block_releaseII = &Ctx.
Idents.
get(
"_Block_release");
769 CIFilterII = &Ctx.
Idents.
get(
"CIFilter");
778bool ObjCDeallocChecker::isSuperDeallocMessage(
788ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
789 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
803 auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->
getDeclContext());
806 if (!CatDecl || !CatDecl->IsClassExtension())
812 auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(
D);
813 if (!ShadowedPropDecl)
816 if (ShadowedPropDecl->isInstanceProperty()) {
817 assert(ShadowedPropDecl->isReadOnly());
818 return ShadowedPropDecl;
835 removeValueRequiringRelease(InitialState, InstanceSym,
Value);
837 if (ReleasedState != InitialState) {
838 C.addTransition(ReleasedState);
852 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
857 SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
859 for (
auto &Sym : *Unreleased) {
860 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
861 assert(UnreleasedRegion);
863 NewUnreleased = F.remove(NewUnreleased, Sym);
867 if (NewUnreleased.isEmpty()) {
868 return State->remove<UnreleasedIvarMap>(Instance);
871 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
890 if (isReleasedByCIFilterDealloc(PropImpl))
893 if (isNibLoadedIvarWithoutRetain(PropImpl))
910 llvm_unreachable(
"Unrecognized setter kind");
916ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
931 std::tie(notNilState, nilState) =
933 if (!(nilState && !notNilState))
946 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
947 std::optional<Loc> LValLoc = LVal.
getAs<
Loc>();
951 SVal CurrentValInIvar = State->getSVal(*LValLoc);
959 SVal &SelfValOut)
const {
960 return isInInstanceDealloc(
C,
C.getLocationContext(), SelfValOut);
968 SVal &SelfValOut)
const {
969 auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->
getDecl());
974 assert(SelfDecl &&
"No self in -dealloc?");
977 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
984bool ObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext &
C,
985 SVal &InstanceValOut)
const {
989 if (isInInstanceDealloc(
C, LCtx, InstanceValOut))
1001bool ObjCDeallocChecker::classHasSeparateTeardown(
1004 for ( ;
ID ;
ID =
ID->getSuperClass()) {
1007 if (II == NSObjectII)
1014 if (II == XCTestCaseII || II == SenTestCaseII)
1029bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1035 const char *ReleasePrefix =
"input";
1036 if (!(PropName.starts_with(ReleasePrefix) ||
1037 IvarName.starts_with(ReleasePrefix))) {
1043 for ( ;
ID ;
ID =
ID->getSuperClass()) {
1045 if (II == CIFilterII)
1060bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1063 if (!IvarDecl->
hasAttr<IBOutletAttr>())
1066 const llvm::Triple &
Target =
1082bool ento::shouldRegisterObjCDeallocChecker(
const CheckerManager &mgr) {
1085 return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount;
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
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.
llvm::MachO::SymbolSet SymbolSet
llvm::MachO::Target Target
#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.
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, const 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.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.