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();
114 BugReporter &BR)
const;
115 void checkBeginFunction(CheckerContext &Ctx)
const;
116 void checkPreObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
117 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
118 void checkPostObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
121 bool Assumption)
const;
127 void checkPreStmt(
const ReturnStmt *RS, CheckerContext &C)
const;
128 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &Ctx)
const;
131 void diagnoseMissingReleases(CheckerContext &C)
const;
134 CheckerContext &C)
const;
136 bool diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
138 CheckerContext &C)
const;
141 CheckerContext &C)
const;
143 const ObjCIvarRegion *getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const;
147 findPropertyOnDeallocatingInstance(
SymbolRef IvarSym,
148 CheckerContext &C)
const;
153 bool isInInstanceDealloc(
const CheckerContext &C, SVal &SelfValOut)
const;
154 bool isInInstanceDealloc(
const CheckerContext &C,
const LocationContext *LCtx,
155 SVal &SelfValOut)
const;
156 bool instanceDeallocIsOnStack(
const CheckerContext &C,
157 SVal &InstanceValOut)
const;
166 void transitionToReleaseValue(CheckerContext &C,
SymbolRef Value)
const;
171 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
190 AnalysisManager &Mgr,
191 BugReporter &BR)
const {
193 assert(!Mgr.getLangOpts().ObjCAutoRefCount);
194 initIdentifierInfoAndSelectors(Mgr.getASTContext());
199 if (classHasSeparateTeardown(ID))
205 bool HasOthers =
false;
208 if (!PropImplRequiringRelease)
209 PropImplRequiringRelease = I;
217 if (!PropImplRequiringRelease)
224 if (I->getSelector() == DeallocSel) {
231 const char* Name =
"Missing -dealloc";
234 llvm::raw_string_ostream
OS(Buf);
235 OS <<
"'" << *D <<
"' lacks a 'dealloc' instance method but " 241 PathDiagnosticLocation DLoc =
253 void ObjCDeallocChecker::checkBeginFunction(
254 CheckerContext &C)
const {
255 initIdentifierInfoAndSelectors(C.getASTContext());
259 if (!isInInstanceDealloc(C, SelfVal))
262 SymbolRef SelfSymbol = SelfVal.getAsSymbol();
269 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
272 SymbolSet RequiredReleases = F.getEmptySet();
276 if (
const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
277 RequiredReleases = *CurrSet;
279 for (
auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
284 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
289 SVal InitialVal = State->getSVal(LValLoc.getValue());
290 SymbolRef Symbol = InitialVal.getAsSymbol();
291 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
295 RequiredReleases = F.add(RequiredReleases, Symbol);
298 if (!RequiredReleases.isEmpty()) {
299 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
302 if (State != InitialState) {
303 C.addTransition(State);
309 const ObjCIvarRegion *
310 ObjCDeallocChecker::getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const {
311 return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion());
317 ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(
SymbolRef IvarSym)
const {
319 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
323 return IvarRegion->getSymbolicBase()->getSymbol();
328 void ObjCDeallocChecker::checkPreObjCMessage(
331 SVal DeallocedInstance;
332 if (!instanceDeallocIsOnStack(C, DeallocedInstance))
347 if (diagnoseExtraRelease(ReleasedValue,M, C))
352 ReleasedValue = getValueReleasedByNillingOut(M, C);
358 transitionToReleaseValue(C, ReleasedValue);
363 void ObjCDeallocChecker::checkPreCall(
const CallEvent &Call,
364 CheckerContext &C)
const {
366 if (II != Block_releaseII)
369 if (Call.getNumArgs() != 1)
372 SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol();
376 transitionToReleaseValue(C, ReleasedValue);
380 void ObjCDeallocChecker::checkPostObjCMessage(
385 if (isSuperDeallocMessage(M))
386 diagnoseMissingReleases(C);
391 void ObjCDeallocChecker::checkEndFunction(
392 const ReturnStmt *RS, CheckerContext &C)
const {
393 diagnoseMissingReleases(C);
397 void ObjCDeallocChecker::checkPreStmt(
398 const ReturnStmt *RS, CheckerContext &C)
const {
399 diagnoseMissingReleases(C);
405 bool Assumption)
const {
406 if (
State->get<UnreleasedIvarMap>().isEmpty())
409 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
423 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
427 NullSymbol = SIE->getLHS();
428 }
else if (
auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
432 NullSymbol = SIE->getRHS();
437 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
441 State = removeValueRequiringRelease(
State, InstanceSymbol, NullSymbol);
451 if (
State->get<UnreleasedIvarMap>().isEmpty())
458 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
459 if (OMC && isSuperDeallocMessage(*OMC))
462 for (
const auto &Sym : Escaped) {
463 if (!Call || (Call && !Call->isInSystemHeader())) {
471 State =
State->remove<UnreleasedIvarMap>(Sym);
475 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
479 State = removeValueRequiringRelease(
State, InstanceSymbol, Sym);
487 void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C)
const {
491 if (!isInInstanceDealloc(C, SelfVal))
494 const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
497 ExplodedNode *ErrNode =
nullptr;
499 SymbolRef SelfSym = SelfVal.getAsSymbol();
503 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
507 SymbolSet NewUnreleased = *OldUnreleased;
508 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
512 for (
auto *IvarSymbol : *OldUnreleased) {
513 const TypedValueRegion *TVR =
514 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
515 const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
518 if (SelfRegion != IvarRegion->getSuperRegion())
525 cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
530 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
532 if (State->getStateManager()
533 .getConstraintManager()
534 .isNull(State, IvarSymbol)
535 .isConstrainedTrue()) {
541 ErrNode = C.generateNonFatalErrorNode();
548 llvm::raw_string_ostream
OS(Buf);
555 if (classHasSeparateTeardown(Interface))
568 OS <<
"The '" << *IvarDecl <<
"' ivar in '" << *ImplDecl
576 OS <<
" by a synthesized property but not released" 577 " before '[super dealloc]'";
579 auto BR = std::make_unique<PathSensitiveBugReport>(*MissingReleaseBugType,
581 C.emitReport(std::move(BR));
584 if (NewUnreleased.isEmpty()) {
585 State = State->remove<UnreleasedIvarMap>(SelfSym);
587 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
591 C.addTransition(State, ErrNode);
592 }
else if (State != InitialState) {
593 C.addTransition(State);
599 assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
606 ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
607 SymbolRef IvarSym, CheckerContext &C)
const {
608 SVal DeallocedInstance;
609 if (!isInInstanceDealloc(C, DeallocedInstance))
613 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
619 if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
620 IvarRegion->getSuperRegion())
626 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
635 bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
637 CheckerContext &C)
const {
644 findPropertyOnDeallocatingInstance(ReleasedValue, C);
651 if (getDeallocReleaseRequirement(PropImpl) !=
670 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
675 llvm::raw_string_ostream
OS(Buf);
680 isReleasedByCIFilterDealloc(PropImpl)
683 const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
685 <<
"' ivar in '" << *Container;
688 if (isReleasedByCIFilterDealloc(PropImpl)) {
689 OS <<
"' will be released by '-[CIFilter dealloc]' but also released here";
691 OS <<
"' was synthesized for ";
696 OS <<
"an assign, readwrite";
698 OS <<
" property but was released in 'dealloc'";
701 auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType,
705 C.emitReport(std::move(BR));
713 bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
715 CheckerContext &C)
const {
724 findPropertyOnDeallocatingInstance(DeallocedValue, C);
728 if (getDeallocReleaseRequirement(PropImpl) !=
733 ExplodedNode *ErrNode = C.generateErrorNode();
738 llvm::raw_string_ostream
OS(Buf);
741 <<
"' should be released rather than deallocated";
743 auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType,
747 C.emitReport(std::move(BR));
752 ObjCDeallocChecker::ObjCDeallocChecker()
753 : NSObjectII(
nullptr), SenTestCaseII(
nullptr), XCTestCaseII(
nullptr),
754 CIFilterII(
nullptr) {
756 MissingReleaseBugType.reset(
757 new BugType(
this,
"Missing ivar release (leak)",
760 ExtraReleaseBugType.reset(
761 new BugType(
this,
"Extra ivar release",
764 MistakenDeallocBugType.reset(
765 new BugType(
this,
"Mistaken dealloc",
769 void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
774 NSObjectII = &Ctx.
Idents.
get(
"NSObject");
775 SenTestCaseII = &Ctx.
Idents.
get(
"SenTestCase");
776 XCTestCaseII = &Ctx.
Idents.
get(
"XCTestCase");
777 Block_releaseII = &Ctx.
Idents.
get(
"_Block_release");
778 CIFilterII = &Ctx.
Idents.
get(
"CIFilter");
787 bool ObjCDeallocChecker::isSuperDeallocMessage(
797 ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
798 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
799 return cast<ObjCImplDecl>(MD->getDeclContext());
815 if (!CatDecl || !CatDecl->IsClassExtension())
822 if (!ShadowedPropDecl)
825 if (ShadowedPropDecl->isInstanceProperty()) {
826 assert(ShadowedPropDecl->isReadOnly());
827 return ShadowedPropDecl;
835 void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C,
838 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
844 removeValueRequiringRelease(InitialState, InstanceSym, Value);
846 if (ReleasedState != InitialState) {
847 C.addTransition(ReleasedState);
857 const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
861 const SymbolSet *Unreleased =
State->get<UnreleasedIvarMap>(Instance);
866 SymbolSet::Factory &F =
State->getStateManager().get_context<SymbolSet>();
867 SymbolSet NewUnreleased = *Unreleased;
868 for (
auto &Sym : *Unreleased) {
869 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
870 assert(UnreleasedRegion);
871 if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
872 NewUnreleased = F.remove(NewUnreleased, Sym);
876 if (NewUnreleased.isEmpty()) {
877 return State->remove<UnreleasedIvarMap>(Instance);
880 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
899 if (isReleasedByCIFilterDealloc(PropImpl))
902 if (isNibLoadedIvarWithoutRetain(PropImpl))
919 llvm_unreachable(
"Unrecognized setter kind");
925 ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
926 CheckerContext &C)
const {
928 if (!ReceiverVal.isValid())
938 SVal Arg = M.getArgSVal(0);
940 std::tie(notNilState, nilState) =
941 M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
942 if (!(nilState && !notNilState))
955 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
960 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
961 return CurrentValInIvar.getAsSymbol();
967 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
968 SVal &SelfValOut)
const {
969 return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
975 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
977 SVal &SelfValOut)
const {
979 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
983 assert(SelfDecl &&
"No self in -dealloc?");
986 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
993 bool ObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext &C,
994 SVal &InstanceValOut)
const {
998 if (isInInstanceDealloc(C, LCtx, InstanceValOut))
1010 bool ObjCDeallocChecker::classHasSeparateTeardown(
1016 if (II == NSObjectII)
1023 if (II == XCTestCaseII || II == SenTestCaseII)
1038 bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1044 const char *ReleasePrefix =
"input";
1045 if (!(PropName.startswith(ReleasePrefix) ||
1046 IvarName.startswith(ReleasePrefix))) {
1054 if (II == CIFilterII)
1069 bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1072 if (!IvarDecl->
hasAttr<IBOutletAttr>())
1075 const llvm::Triple &
Target =
1078 if (!Target.isMacOSX())
1087 void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
1088 Mgr.registerChecker<ObjCDeallocChecker>();
1091 bool ento::shouldRegisterObjCDeallocChecker(
const LangOptions &LO) {
The receiver is the instance of the superclass object.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
const char *const CoreFoundationObjectiveC
Smart pointer class that efficiently represents Objective-C method names.
A (possibly-)qualified type.
ObjCIvarDecl * getPropertyIvarDecl() const
llvm::DenseSet< SymbolRef > InvalidatedSymbols
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const TargetInfo & getTargetInfo() const
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...
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
One of these records is kept for each identifier that is lexed.
const SymExpr * SymbolRef
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.
SetterKind getSetterKind() const
getSetterKind - Return the method used for doing assignment in the property setter.
instmeth_range instance_methods() const
ObjCMethodDecl * getSetterMethodDecl() const
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 char *const MemoryRefCount
const ImplicitParamDecl * getSelfDecl() const
virtual bool inTopFrame() const
Return true if the current LocationContext has no caller context.
const LocationContext * getParent() const
bool isReadOnly() const
isReadOnly - Return true iff the property has a setter.
Represents an ObjC class declaration.
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
Defines the clang::LangOptions interface.
bool isObjCRetainableType() const
DeclContext * getDeclContext()
ObjCInterfaceDecl * getSuperClass() const
propimpl_range property_impls() const
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
SelectorTable & Selectors
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
#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...
ASTContext & getASTContext() const LLVM_READONLY
ObjCCategoryDecl - Represents a category declaration.
Represents one property declaration in an Objective-C interface.
ObjCPropertyImplDecl * FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const
FindPropertyImplIvarDecl - This method lookup the ivar in the list of properties implemented in this ...
ObjCImplementationDecl * getImplementation() const
virtual const ObjCMessageExpr * getOriginExpr() const
const ObjCInterfaceDecl * getClassInterface() const
Selector getSelector() const
Dataflow Directional Tag Classes.
Kind getPropertyImplementation() const
const ObjCInterfaceDecl * getContainingInterface() const
Return the class interface that this ivar is logically contained in; this is either the interface whe...
const Decl * getDecl() const
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
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.
unsigned getNumArgs() const override
The instance variable must not be directly released with -release.
const Expr * getArgExpr(unsigned Index) const override
ReleaseRequirement
Indicates whether an instance variable is required to be released in -dealloc.
ObjCIvarDecl - Represents an ObjC instance variable.
ObjCIvarDecl * getPropertyIvarDecl() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Defines the clang::TargetInfo interface.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
const ObjCPropertyDecl * getAccessedProperty() const
ObjCPropertyDecl * getPropertyDecl() const