;
93classObjCDeallocChecker
94:
public Checker<check::ASTDecl<ObjCImplementationDecl>,
95check::PreObjCMessage, check::PostObjCMessage,
97check::BeginFunction, check::EndFunction,
100check::PreStmt<ReturnStmt>> {
111 const BugTypeMissingReleaseBugType{
this,
"Missing ivar release (leak)",
113 const BugTypeExtraReleaseBugType{
this,
"Extra ivar release",
115 const BugTypeMistakenDeallocBugType{
this,
"Mistaken dealloc",
127 boolAssumption)
const;
142 booldiagnoseMistakenDealloc(
SymbolRefDeallocedValue,
153findPropertyOnDeallocatingInstance(
SymbolRefIvarSym,
161 SVal&SelfValOut)
const;
163 SVal&InstanceValOut)
const;
177 voidinitIdentifierInfoAndSelectors(
ASTContext&Ctx)
const;
198assert(Mgr.
getLangOpts().getGC() != LangOptions::GCOnly);
205 if(classHasSeparateTeardown(ID))
211 boolHasOthers =
false;
212 for(
const auto*I :
D->property_impls()) {
214 if(!PropImplRequiringRelease)
215PropImplRequiringRelease = I;
223 if(!PropImplRequiringRelease)
229 for(
const auto*I :
D->instance_methods()) {
230 if(I->getSelector() == DeallocSel) {
237 const char* Name =
"Missing -dealloc";
240llvm::raw_string_ostream OS(Buf);
241OS <<
"'"<< *
D<<
"' lacks a 'dealloc' instance method but " 246OS <<
" and others";
259voidObjCDeallocChecker::checkBeginFunction(
261initIdentifierInfoAndSelectors(
C.getASTContext());
265 if(!isInInstanceDealloc(
C, SelfVal))
275SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
278 SymbolSetRequiredReleases = F.getEmptySet();
282 if(
const SymbolSet*CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
283RequiredReleases = *CurrSet;
285 for(
auto*PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
290 SValLVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
291std::optional<Loc> LValLoc = LVal.
getAs<
Loc>();
295 SValInitialVal = State->getSVal(*LValLoc);
297 if(!Symbol || !isa<SymbolRegionValue>(Symbol))
301RequiredReleases = F.add(RequiredReleases, Symbol);
304 if(!RequiredReleases.isEmpty()) {
305State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
308 if(State != InitialState) {
309 C.addTransition(State);
316ObjCDeallocChecker::getIvarRegionForIvarSymbol(
SymbolRefIvarSym)
const{
323ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(
SymbolRefIvarSym)
const{
325 const ObjCIvarRegion*IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
330assert(SR &&
"Symbolic base should not be nullptr");
336voidObjCDeallocChecker::checkPreObjCMessage(
339 SValDeallocedInstance;
340 if(!instanceDeallocIsOnStack(
C, DeallocedInstance))
355 if(diagnoseExtraRelease(ReleasedValue,M,
C))
360ReleasedValue = getValueReleasedByNillingOut(M,
C);
366transitionToReleaseValue(
C, ReleasedValue);
371voidObjCDeallocChecker::checkPreCall(
const CallEvent&
Call,
374 if(II != Block_releaseII)
377 if(
Call.getNumArgs() != 1)
384transitionToReleaseValue(
C, ReleasedValue);
388voidObjCDeallocChecker::checkPostObjCMessage(
393 if(isSuperDeallocMessage(M))
394diagnoseMissingReleases(
C);
399voidObjCDeallocChecker::checkEndFunction(
401diagnoseMissingReleases(
C);
405voidObjCDeallocChecker::checkPreStmt(
407diagnoseMissingReleases(
C);
413 boolAssumption)
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 constllvm::APInt &RHS = SIE->getRHS();
435NullSymbol = SIE->getLHS();
436}
else if(
auto*SIE = dyn_cast<IntSymExpr>(CondBSE)) {
437 constllvm::APInt &LHS = SIE->getLHS();
440NullSymbol = SIE->getRHS();
445 SymbolRefInstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
449State = 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) {
479State = State->remove<UnreleasedIvarMap>(Sym);
483 SymbolRefInstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
487State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
495voidObjCDeallocChecker::diagnoseMissingReleases(
CheckerContext&
C)
const{
499 if(!isInInstanceDealloc(
C, SelfVal))
511 const SymbolSet*OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
515 SymbolSetNewUnreleased = *OldUnreleased;
516SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
520 for(
auto*IvarSymbol : *OldUnreleased) {
522cast<SymbolRegionValue>(IvarSymbol)->getRegion();
533cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
538NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
540 if(State->getStateManager()
541.getConstraintManager()
542.isNull(State, IvarSymbol)
543.isConstrainedTrue()) {
549ErrNode =
C.generateNonFatalErrorNode();
556llvm::raw_string_ostream OS(Buf);
563 if(classHasSeparateTeardown(
Interface))
576OS <<
"The '"<< *IvarDecl <<
"' ivar in '"<< *ImplDecl
584OS <<
" by a synthesized property but not released" 585 " before '[super dealloc]'";
587 autoBR = std::make_unique<PathSensitiveBugReport>(MissingReleaseBugType,
589 C.emitReport(std::move(BR));
592 if(NewUnreleased.isEmpty()) {
593State = State->remove<UnreleasedIvarMap>(SelfSym);
595State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
599 C.addTransition(State, ErrNode);
600}
else if(State != InitialState) {
601 C.addTransition(State);
607assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
614ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
616 SValDeallocedInstance;
617 if(!isInInstanceDealloc(
C, DeallocedInstance))
621 auto*IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
634 const ObjCImplDecl*Container = getContainingObjCImpl(LCtx);
636Container->FindPropertyImplIvarDecl(IvarDecl->
getIdentifier());
643boolObjCDeallocChecker::diagnoseExtraRelease(
SymbolRefReleasedValue,
652findPropertyOnDeallocatingInstance(ReleasedValue,
C);
659 if(getDeallocReleaseRequirement(PropImpl) !=
683llvm::raw_string_ostream OS(Buf);
688isReleasedByCIFilterDealloc(PropImpl)
691 const ObjCImplDecl*Container = getContainingObjCImpl(
C.getLocationContext());
693<<
"' ivar in '"<< *Container;
696 if(isReleasedByCIFilterDealloc(PropImpl)) {
697OS <<
"' will be released by '-[CIFilter dealloc]' but also released here";
699OS <<
"' was synthesized for ";
704OS <<
"an assign, readwrite";
706OS <<
" property but was released in 'dealloc'";
709 autoBR = std::make_unique<PathSensitiveBugReport>(ExtraReleaseBugType, Buf,
713 C.emitReport(std::move(BR));
721boolObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRefDeallocedValue,
732findPropertyOnDeallocatingInstance(DeallocedValue,
C);
736 if(getDeallocReleaseRequirement(PropImpl) !=
746llvm::raw_string_ostream OS(Buf);
749<<
"' should be released rather than deallocated";
751 autoBR = std::make_unique<PathSensitiveBugReport>(MistakenDeallocBugType,
755 C.emitReport(std::move(BR));
760voidObjCDeallocChecker::initIdentifierInfoAndSelectors(
765NSObjectII = &Ctx.
Idents.
get(
"NSObject");
766SenTestCaseII = &Ctx.
Idents.
get(
"SenTestCase");
767XCTestCaseII = &Ctx.
Idents.
get(
"XCTestCase");
768Block_releaseII = &Ctx.
Idents.
get(
"_Block_release");
769CIFilterII = &Ctx.
Idents.
get(
"CIFilter");
778boolObjCDeallocChecker::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()) {
817assert(ShadowedPropDecl->isReadOnly());
818 returnShadowedPropDecl;
835removeValueRequiringRelease(InitialState, InstanceSym,
Value);
837 if(ReleasedState != InitialState) {
838 C.addTransition(ReleasedState);
852 const SymbolSet*Unreleased = State->get<UnreleasedIvarMap>(Instance);
857SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
859 for(
auto&Sym : *Unreleased) {
860 const ObjCIvarRegion*UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
861assert(UnreleasedRegion);
862 if(RemovedRegion->
getDecl() == UnreleasedRegion->
getDecl()) {
863NewUnreleased = F.remove(NewUnreleased, Sym);
867 if(NewUnreleased.isEmpty()) {
868 returnState->remove<UnreleasedIvarMap>(Instance);
871 returnState->set<UnreleasedIvarMap>(Instance, NewUnreleased);
890 if(isReleasedByCIFilterDealloc(PropImpl))
893 if(isNibLoadedIvarWithoutRetain(PropImpl))
910llvm_unreachable(
"Unrecognized setter kind");
916ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall&M,
931std::tie(notNilState, nilState) =
933 if(!(nilState && !notNilState))
946 SValLVal = State->getLValue(PropIvarDecl, ReceiverVal);
947std::optional<Loc> LValLoc = LVal.
getAs<
Loc>();
951 SValCurrentValInIvar = State->getSVal(*LValLoc);
958boolObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext&
C,
959 SVal&SelfValOut)
const{
960 returnisInInstanceDealloc(
C,
C.getLocationContext(), SelfValOut);
966boolObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext&
C,
968 SVal&SelfValOut)
const{
969 auto*MD = dyn_cast<ObjCMethodDecl>(LCtx->
getDecl());
974assert(SelfDecl &&
"No self in -dealloc?");
977SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
984boolObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext&
C,
985 SVal&InstanceValOut)
const{
989 if(isInInstanceDealloc(
C, LCtx, InstanceValOut))
1001boolObjCDeallocChecker::classHasSeparateTeardown(
1004 for( ;
ID;
ID=
ID->getSuperClass()) {
1007 if(II == NSObjectII)
1014 if(II == XCTestCaseII || II == SenTestCaseII)
1029boolObjCDeallocChecker::isReleasedByCIFilterDealloc(
1035 const char*ReleasePrefix =
"input";
1036 if(!(PropName.starts_with(ReleasePrefix) ||
1037IvarName.starts_with(ReleasePrefix))) {
1043 for( ;
ID;
ID=
ID->getSuperClass()) {
1045 if(II == CIFilterII)
1060boolObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1063 if(!IvarDecl->
hasAttr<IBOutletAttr>())
1066 constllvm::Triple &
Target=
1069 if(!
Target.isMacOSX())
1082boolento::shouldRegisterObjCDeallocChecker(
const CheckerManager&mgr) {
1085 returnLO.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={}, ArrayRef< FixItHint > Fixits={})
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.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4