47 #include "llvm/Support/raw_ostream.h"
49 using namespace clang;
92 class ObjCDeallocChecker
93 :
public Checker<check::ASTDecl<ObjCImplementationDecl>,
94 check::PreObjCMessage, check::PostObjCMessage,
96 check::BeginFunction, check::EndFunction,
99 check::PreStmt<ReturnStmt>> {
101 mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII,
102 *Block_releaseII, *CIFilterII;
104 mutable Selector DeallocSel, ReleaseSel;
106 std::unique_ptr<BugType> MissingReleaseBugType;
107 std::unique_ptr<BugType> ExtraReleaseBugType;
108 std::unique_ptr<BugType> MistakenDeallocBugType;
111 ObjCDeallocChecker();
121 bool Assumption)
const;
136 bool diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
147 findPropertyOnDeallocatingInstance(
SymbolRef IvarSym,
155 SVal &SelfValOut)
const;
157 SVal &InstanceValOut)
const;
171 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
189 static void *
GDMIndex() {
static int index = 0;
return &index; }
206 if (classHasSeparateTeardown(ID))
212 bool HasOthers =
false;
215 if (!PropImplRequiringRelease)
216 PropImplRequiringRelease =
I;
224 if (!PropImplRequiringRelease)
231 if (
I->getSelector() == DeallocSel) {
238 const char*
Name =
"Missing -dealloc";
241 llvm::raw_string_ostream OS(Buf);
242 OS <<
"'" << *D <<
"' lacks a 'dealloc' instance method but "
260 void ObjCDeallocChecker::checkBeginFunction(
266 if (!isInInstanceDealloc(C, SelfVal))
269 SymbolRef SelfSymbol = SelfVal.getAsSymbol();
276 SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
279 SymbolSet RequiredReleases = F.getEmptySet();
283 if (
const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
284 RequiredReleases = *CurrSet;
286 for (
auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
291 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
296 SVal InitialVal = State->getSVal(LValLoc.getValue());
298 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
302 RequiredReleases = F.add(RequiredReleases, Symbol);
305 if (!RequiredReleases.isEmpty()) {
306 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
309 if (State != InitialState) {
317 ObjCDeallocChecker::getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const {
324 ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(
SymbolRef IvarSym)
const {
326 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
335 void ObjCDeallocChecker::checkPreObjCMessage(
338 SVal DeallocedInstance;
339 if (!instanceDeallocIsOnStack(C, DeallocedInstance))
354 if (diagnoseExtraRelease(ReleasedValue,M, C))
359 ReleasedValue = getValueReleasedByNillingOut(M, C);
365 transitionToReleaseValue(C, ReleasedValue);
370 void ObjCDeallocChecker::checkPreCall(
const CallEvent &Call,
373 if (II != Block_releaseII)
383 transitionToReleaseValue(C, ReleasedValue);
387 void ObjCDeallocChecker::checkPostObjCMessage(
392 if (isSuperDeallocMessage(M))
393 diagnoseMissingReleases(C);
398 void ObjCDeallocChecker::checkEndFunction(
400 diagnoseMissingReleases(C);
404 void ObjCDeallocChecker::checkPreStmt(
406 diagnoseMissingReleases(C);
412 bool Assumption)
const {
413 if (State->get<UnreleasedIvarMap>().isEmpty())
416 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.
getAsSymExpr());
430 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
431 const llvm::APInt &RHS = SIE->getRHS();
434 NullSymbol = SIE->getLHS();
435 }
else if (
auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
436 const llvm::APInt &LHS = SIE->getLHS();
439 NullSymbol = SIE->getRHS();
444 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
448 State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
458 if (State->get<UnreleasedIvarMap>().isEmpty())
465 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
466 if (OMC && isSuperDeallocMessage(*OMC))
469 for (
const auto &Sym : Escaped) {
478 State = State->remove<UnreleasedIvarMap>(Sym);
482 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
486 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
494 void ObjCDeallocChecker::diagnoseMissingReleases(
CheckerContext &C)
const {
498 if (!isInInstanceDealloc(C, SelfVal))
510 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
514 SymbolSet NewUnreleased = *OldUnreleased;
515 SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
519 for (
auto *IvarSymbol : *OldUnreleased) {
521 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
532 cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
537 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
539 if (State->getStateManager()
540 .getConstraintManager()
541 .isNull(State, IvarSymbol)
542 .isConstrainedTrue()) {
555 llvm::raw_string_ostream OS(Buf);
562 if (classHasSeparateTeardown(Interface))
575 OS <<
"The '" << *IvarDecl <<
"' ivar in '" << *ImplDecl
583 OS <<
" by a synthesized property but not released"
584 " before '[super dealloc]'";
586 std::unique_ptr<BugReport> BR(
587 new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
592 if (NewUnreleased.isEmpty()) {
593 State = State->remove<UnreleasedIvarMap>(SelfSym);
595 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
600 }
else if (State != InitialState) {
607 assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
614 ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
616 SVal DeallocedInstance;
617 if (!isInInstanceDealloc(C, DeallocedInstance))
621 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
634 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
643 bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
652 findPropertyOnDeallocatingInstance(ReleasedValue, C);
659 if (getDeallocReleaseRequirement(PropImpl) !=
683 llvm::raw_string_ostream OS(Buf);
688 isReleasedByCIFilterDealloc(PropImpl)
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 std::unique_ptr<BugReport> BR(
710 new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
721 bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
728 findPropertyOnDeallocatingInstance(DeallocedValue, C);
732 if (getDeallocReleaseRequirement(PropImpl) !=
742 llvm::raw_string_ostream OS(Buf);
745 <<
"' should be released rather than deallocated";
747 std::unique_ptr<BugReport> BR(
748 new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
756 ObjCDeallocChecker::ObjCDeallocChecker()
757 : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr),
758 CIFilterII(nullptr) {
760 MissingReleaseBugType.reset(
761 new BugType(
this,
"Missing ivar release (leak)",
764 ExtraReleaseBugType.reset(
765 new BugType(
this,
"Extra ivar release",
768 MistakenDeallocBugType.reset(
769 new BugType(
this,
"Mistaken dealloc",
773 void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
778 NSObjectII = &Ctx.
Idents.
get(
"NSObject");
779 SenTestCaseII = &Ctx.
Idents.
get(
"SenTestCase");
780 XCTestCaseII = &Ctx.
Idents.
get(
"XCTestCase");
781 Block_releaseII = &Ctx.
Idents.
get(
"_Block_release");
782 CIFilterII = &Ctx.
Idents.
get(
"CIFilter");
791 bool ObjCDeallocChecker::isSuperDeallocMessage(
801 ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
802 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
803 return cast<ObjCImplDecl>(MD->getDeclContext());
819 if (!CatDecl || !CatDecl->IsClassExtension())
826 if (!ShadowedPropDecl)
829 if (ShadowedPropDecl->isInstanceProperty()) {
830 assert(ShadowedPropDecl->isReadOnly());
831 return ShadowedPropDecl;
839 void ObjCDeallocChecker::transitionToReleaseValue(
CheckerContext &C,
842 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
848 removeValueRequiringRelease(InitialState, InstanceSym, Value);
850 if (ReleasedState != InitialState) {
861 const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
865 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
870 SymbolSet::Factory &F = State->getStateManager().get_context<
SymbolSet>();
872 for (
auto &Sym : *Unreleased) {
873 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
874 assert(UnreleasedRegion);
876 NewUnreleased = F.remove(NewUnreleased, Sym);
880 if (NewUnreleased.isEmpty()) {
881 return State->remove<UnreleasedIvarMap>(Instance);
884 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
903 if (isReleasedByCIFilterDealloc(PropImpl))
920 llvm_unreachable(
"Unrecognized setter kind");
926 ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
939 SVal Arg = M.getArgSVal(0);
941 std::tie(notNilState, nilState) =
943 if (!(nilState && !notNilState))
956 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
961 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
968 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
969 SVal &SelfValOut)
const {
976 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
978 SVal &SelfValOut)
const {
984 assert(SelfDecl &&
"No self in -dealloc?");
987 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
994 bool ObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext &C,
995 SVal &InstanceValOut)
const {
999 if (isInInstanceDealloc(C, LCtx, InstanceValOut))
1011 bool ObjCDeallocChecker::classHasSeparateTeardown(
1017 if (II == NSObjectII)
1024 if (II == XCTestCaseII || II == SenTestCaseII)
1039 bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1045 const char *ReleasePrefix =
"input";
1046 if (!(PropName.startswith(ReleasePrefix) ||
1047 IvarName.startswith(ReleasePrefix))) {
1055 if (II == CIFilterII)
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
The receiver is the instance of the superclass object.
const SymExpr * getAsSymExpr() const
TypedValueRegion - An abstract class representing regions having a typed value.
const char *const CoreFoundationObjectiveC
StringRef getName() const
getName - Get the name of identifier for this declaration as a StringRef.
Smart pointer class that efficiently represents Objective-C method names.
A (possibly-)qualified type.
MemRegion - The root abstract class for all memory regions.
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
bool isInSystemHeader() const
Returns true if the callee is known to be from a system header.
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
virtual bool inTopFrame() const
Return true if the current LocationContext has no caller context.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
ObjCMethodDecl - Represents an instance or class method declaration.
The instance variable must be released, either by calling -release on it directly or by nilling it ou...
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
bool isObjCRetainableType() const
const char *const MemoryCoreFoundationObjectiveC
Kind getPropertyImplementation() const
One of these records is kept for each identifier that is lexed.
const ObjCInterfaceDecl * getContainingInterface() const
Return the class interface that this ivar is logically contained in; this is either the interface whe...
class LLVM_ALIGNAS(8) DependentTemplateSpecializationType const IdentifierInfo * Name
Represents a template specialization type whose template cannot be resolved, e.g. ...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
The results of name lookup within a DeclContext.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Represents any expression that calls an Objective-C method.
const SymbolicRegion * getSymbolicBase() const
If this is a symbolic region, returns the region.
const ObjCIvarDecl * getDecl() const
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
ObjCPropertyImplDecl * FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const
FindPropertyImplIvarDecl - This method lookup the ivar in the list of properties implemented in this ...
SymbolRef getSymbol() const
Represents an ObjC class declaration.
propimpl_range property_impls() const
detail::InMemoryDirectory::const_iterator I
ASTContext & getASTContext() override
const MemRegion * getSuperRegion() const
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Defines the clang::LangOptions interface.
const ProgramStateRef & getState() const
SetterKind getSetterKind() const
getSetterKind - Return the method used for doing assignment in the property setter.
ObjCIvarDecl * getPropertyIvarDecl() const
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
bool isInstanceMethod() const
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
BugReporter is a utility class for generating PathDiagnostics for analysis.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
SelectorTable & Selectors
CHECKER * registerChecker()
Used to register checkers.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=None)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Selector getSelector() const
ObjCCategoryDecl - Represents a category declaration.
const ObjCInterfaceDecl * getClassInterface() const
Represents one property declaration in an Objective-C interface.
const Decl * getDecl() const
const IdentifierInfo * getCalleeIdentifier() const
Returns the name of the callee, if its name is a simple identifier.
const LangOptions & getLangOpts() const
instmeth_range instance_methods() const
const LocationContext * getParent() const
ASTContext & getASTContext()
ObjCIvarDecl * getPropertyIvarDecl() const
Selector getSelector() const
detail::InMemoryDirectory::const_iterator E
Represents an abstract call to a function or method along a particular path.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
The requirement for the instance variable could not be determined.
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...
Selector getSelector(unsigned NumArgs, IdentifierInfo **IIV)
Can create any sort of selector.
ObjCImplementationDecl * getImplementation() const
unsigned getNumArgs() const override
const ImplicitParamDecl * getSelfDecl() const
ObjCPropertyDecl * getPropertyDecl() const
The instance variable must not be directly released with -release.
SourceManager & getSourceManager()
const Expr * getArgExpr(unsigned Index) const override
ReleaseRequirement
Indicates whether an instance variable is required to be released in -dealloc.
bool isReadOnly() const
isReadOnly - Return true iff the property has a setter.
ObjCIvarDecl - Represents an ObjC instance variable.
virtual unsigned getNumArgs() const =0
Returns the number of arguments (explicit and implicit).
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
llvm::ImmutableSet< SymbolRef > SymbolSet
ObjCInterfaceDecl * getSuperClass() const
const LangOptions & getLangOpts() const
This class provides an interface through which checkers can create individual bug reports...
virtual const ObjCMessageExpr * getOriginExpr() const
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
const ObjCPropertyDecl * getAccessedProperty() const
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
const MemRegion * getRegion() const
Get the underlining region.
const LocationContext * getLocationContext() const