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)) {
424 const llvm::APInt &RHS = SIE->getRHS();
427 NullSymbol = SIE->getLHS();
428 }
else if (
auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
429 const llvm::APInt &LHS = SIE->getLHS();
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 std::unique_ptr<BugReport> BR(
580 new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
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());
607 ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
608 SymbolRef IvarSym, CheckerContext &C)
const {
609 SVal DeallocedInstance;
610 if (!isInInstanceDealloc(C, DeallocedInstance))
614 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
620 if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
621 IvarRegion->getSuperRegion())
627 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
636 bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
638 CheckerContext &C)
const {
645 findPropertyOnDeallocatingInstance(ReleasedValue, C);
652 if (getDeallocReleaseRequirement(PropImpl) !=
671 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
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 std::unique_ptr<BugReport> BR(
703 new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
706 C.emitReport(std::move(BR));
714 bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
716 CheckerContext &C)
const {
725 findPropertyOnDeallocatingInstance(DeallocedValue, C);
729 if (getDeallocReleaseRequirement(PropImpl) !=
734 ExplodedNode *ErrNode = C.generateErrorNode();
739 llvm::raw_string_ostream
OS(Buf);
742 <<
"' should be released rather than deallocated";
744 std::unique_ptr<BugReport> BR(
745 new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
748 C.emitReport(std::move(BR));
753 ObjCDeallocChecker::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",
770 void 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");
788 bool ObjCDeallocChecker::isSuperDeallocMessage(
798 ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
799 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
800 return cast<ObjCImplDecl>(MD->getDeclContext());
816 if (!CatDecl || !CatDecl->IsClassExtension())
823 if (!ShadowedPropDecl)
826 if (ShadowedPropDecl->isInstanceProperty()) {
827 assert(ShadowedPropDecl->isReadOnly());
828 return ShadowedPropDecl;
836 void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C,
839 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
845 removeValueRequiringRelease(InitialState, InstanceSym, Value);
847 if (ReleasedState != InitialState) {
848 C.addTransition(ReleasedState);
858 const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
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);
872 if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
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");
926 ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
927 CheckerContext &C)
const {
929 if (!ReceiverVal.isValid())
939 SVal Arg = M.getArgSVal(0);
941 std::tie(notNilState, nilState) =
942 M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
943 if (!(nilState && !notNilState))
956 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
961 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
962 return CurrentValInIvar.getAsSymbol();
968 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
969 SVal &SelfValOut)
const {
970 return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
976 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
978 SVal &SelfValOut)
const {
980 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
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)
1070 bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1073 if (!IvarDecl->
hasAttr<IBOutletAttr>())
1076 const llvm::Triple &
Target =
1079 if (!Target.isMacOSX())
1088 void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
1089 Mgr.registerChecker<ObjCDeallocChecker>();
1092 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 SymExpr * SymbolRef
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.
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