48 #include "llvm/Support/raw_ostream.h" 50 using namespace clang;
93 class ObjCDeallocChecker
94 :
public Checker<check::ASTDecl<ObjCImplementationDecl>,
95 check::PreObjCMessage, check::PostObjCMessage,
97 check::BeginFunction, check::EndFunction,
100 check::PreStmt<ReturnStmt>> {
102 mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII,
103 *Block_releaseII, *CIFilterII;
105 mutable Selector DeallocSel, ReleaseSel;
107 std::unique_ptr<BugType> MissingReleaseBugType;
108 std::unique_ptr<BugType> ExtraReleaseBugType;
109 std::unique_ptr<BugType> MistakenDeallocBugType;
112 ObjCDeallocChecker();
115 BugReporter &BR)
const;
116 void checkBeginFunction(CheckerContext &Ctx)
const;
117 void checkPreObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
118 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
119 void checkPostObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
122 bool Assumption)
const;
128 void checkPreStmt(
const ReturnStmt *RS, CheckerContext &C)
const;
129 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &Ctx)
const;
132 void diagnoseMissingReleases(CheckerContext &C)
const;
135 CheckerContext &C)
const;
137 bool diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
139 CheckerContext &C)
const;
142 CheckerContext &C)
const;
144 const ObjCIvarRegion *getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const;
148 findPropertyOnDeallocatingInstance(
SymbolRef IvarSym,
149 CheckerContext &C)
const;
154 bool isInInstanceDealloc(
const CheckerContext &C, SVal &SelfValOut)
const;
155 bool isInInstanceDealloc(
const CheckerContext &C,
const LocationContext *LCtx,
156 SVal &SelfValOut)
const;
157 bool instanceDeallocIsOnStack(
const CheckerContext &C,
158 SVal &InstanceValOut)
const;
167 void transitionToReleaseValue(CheckerContext &C,
SymbolRef Value)
const;
172 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
191 AnalysisManager &Mgr,
192 BugReporter &BR)
const {
194 assert(!Mgr.getLangOpts().ObjCAutoRefCount);
195 initIdentifierInfoAndSelectors(Mgr.getASTContext());
200 if (classHasSeparateTeardown(ID))
206 bool HasOthers =
false;
209 if (!PropImplRequiringRelease)
210 PropImplRequiringRelease = I;
218 if (!PropImplRequiringRelease)
225 if (I->getSelector() == DeallocSel) {
232 const char* Name =
"Missing -dealloc";
235 llvm::raw_string_ostream
OS(Buf);
236 OS <<
"'" << *D <<
"' lacks a 'dealloc' instance method but " 242 PathDiagnosticLocation DLoc =
254 void ObjCDeallocChecker::checkBeginFunction(
255 CheckerContext &C)
const {
256 initIdentifierInfoAndSelectors(C.getASTContext());
260 if (!isInInstanceDealloc(C, SelfVal))
263 SymbolRef SelfSymbol = SelfVal.getAsSymbol();
270 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
273 SymbolSet RequiredReleases = F.getEmptySet();
277 if (
const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
278 RequiredReleases = *CurrSet;
280 for (
auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
285 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
290 SVal InitialVal = State->getSVal(LValLoc.getValue());
291 SymbolRef Symbol = InitialVal.getAsSymbol();
292 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
296 RequiredReleases = F.add(RequiredReleases, Symbol);
299 if (!RequiredReleases.isEmpty()) {
300 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
303 if (State != InitialState) {
304 C.addTransition(State);
310 const ObjCIvarRegion *
311 ObjCDeallocChecker::getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const {
312 return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion());
318 ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(
SymbolRef IvarSym)
const {
320 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
324 return IvarRegion->getSymbolicBase()->getSymbol();
329 void ObjCDeallocChecker::checkPreObjCMessage(
332 SVal DeallocedInstance;
333 if (!instanceDeallocIsOnStack(C, DeallocedInstance))
348 if (diagnoseExtraRelease(ReleasedValue,M, C))
353 ReleasedValue = getValueReleasedByNillingOut(M, C);
359 transitionToReleaseValue(C, ReleasedValue);
364 void ObjCDeallocChecker::checkPreCall(
const CallEvent &Call,
365 CheckerContext &C)
const {
367 if (II != Block_releaseII)
370 if (Call.getNumArgs() != 1)
373 SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol();
377 transitionToReleaseValue(C, ReleasedValue);
381 void ObjCDeallocChecker::checkPostObjCMessage(
386 if (isSuperDeallocMessage(M))
387 diagnoseMissingReleases(C);
392 void ObjCDeallocChecker::checkEndFunction(
393 const ReturnStmt *RS, CheckerContext &C)
const {
394 diagnoseMissingReleases(C);
398 void ObjCDeallocChecker::checkPreStmt(
399 const ReturnStmt *RS, CheckerContext &C)
const {
400 diagnoseMissingReleases(C);
406 bool Assumption)
const {
407 if (
State->get<UnreleasedIvarMap>().isEmpty())
410 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
424 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
425 const llvm::APInt &RHS = SIE->getRHS();
428 NullSymbol = SIE->getLHS();
429 }
else if (
auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
430 const llvm::APInt &LHS = SIE->getLHS();
433 NullSymbol = SIE->getRHS();
438 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
442 State = removeValueRequiringRelease(
State, InstanceSymbol, NullSymbol);
452 if (
State->get<UnreleasedIvarMap>().isEmpty())
459 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
460 if (OMC && isSuperDeallocMessage(*OMC))
463 for (
const auto &Sym : Escaped) {
464 if (!Call || (Call && !Call->isInSystemHeader())) {
472 State =
State->remove<UnreleasedIvarMap>(Sym);
476 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
480 State = removeValueRequiringRelease(
State, InstanceSymbol, Sym);
488 void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C)
const {
492 if (!isInInstanceDealloc(C, SelfVal))
495 const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
498 ExplodedNode *ErrNode =
nullptr;
500 SymbolRef SelfSym = SelfVal.getAsSymbol();
504 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
508 SymbolSet NewUnreleased = *OldUnreleased;
509 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
513 for (
auto *IvarSymbol : *OldUnreleased) {
514 const TypedValueRegion *TVR =
515 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
516 const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
519 if (SelfRegion != IvarRegion->getSuperRegion())
526 cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
531 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
533 if (State->getStateManager()
534 .getConstraintManager()
535 .isNull(State, IvarSymbol)
536 .isConstrainedTrue()) {
542 ErrNode = C.generateNonFatalErrorNode();
549 llvm::raw_string_ostream
OS(Buf);
556 if (classHasSeparateTeardown(Interface))
569 OS <<
"The '" << *IvarDecl <<
"' ivar in '" << *ImplDecl
577 OS <<
" by a synthesized property but not released" 578 " before '[super dealloc]'";
580 std::unique_ptr<BugReport> BR(
581 new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
583 C.emitReport(std::move(BR));
586 if (NewUnreleased.isEmpty()) {
587 State = State->remove<UnreleasedIvarMap>(SelfSym);
589 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
593 C.addTransition(State, ErrNode);
594 }
else if (State != InitialState) {
595 C.addTransition(State);
601 assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
608 ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
609 SymbolRef IvarSym, CheckerContext &C)
const {
610 SVal DeallocedInstance;
611 if (!isInInstanceDealloc(C, DeallocedInstance))
615 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
621 if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
622 IvarRegion->getSuperRegion())
628 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
637 bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
639 CheckerContext &C)
const {
646 findPropertyOnDeallocatingInstance(ReleasedValue, C);
653 if (getDeallocReleaseRequirement(PropImpl) !=
672 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
677 llvm::raw_string_ostream
OS(Buf);
682 isReleasedByCIFilterDealloc(PropImpl)
685 const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
687 <<
"' ivar in '" << *Container;
690 if (isReleasedByCIFilterDealloc(PropImpl)) {
691 OS <<
"' will be released by '-[CIFilter dealloc]' but also released here";
693 OS <<
"' was synthesized for ";
698 OS <<
"an assign, readwrite";
700 OS <<
" property but was released in 'dealloc'";
703 std::unique_ptr<BugReport> BR(
704 new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
707 C.emitReport(std::move(BR));
715 bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
717 CheckerContext &C)
const {
726 findPropertyOnDeallocatingInstance(DeallocedValue, C);
730 if (getDeallocReleaseRequirement(PropImpl) !=
735 ExplodedNode *ErrNode = C.generateErrorNode();
740 llvm::raw_string_ostream
OS(Buf);
743 <<
"' should be released rather than deallocated";
745 std::unique_ptr<BugReport> BR(
746 new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
749 C.emitReport(std::move(BR));
754 ObjCDeallocChecker::ObjCDeallocChecker()
755 : NSObjectII(
nullptr), SenTestCaseII(
nullptr), XCTestCaseII(
nullptr),
756 CIFilterII(
nullptr) {
758 MissingReleaseBugType.reset(
759 new BugType(
this,
"Missing ivar release (leak)",
762 ExtraReleaseBugType.reset(
763 new BugType(
this,
"Extra ivar release",
766 MistakenDeallocBugType.reset(
767 new BugType(
this,
"Mistaken dealloc",
771 void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
776 NSObjectII = &Ctx.
Idents.
get(
"NSObject");
777 SenTestCaseII = &Ctx.
Idents.
get(
"SenTestCase");
778 XCTestCaseII = &Ctx.
Idents.
get(
"XCTestCase");
779 Block_releaseII = &Ctx.
Idents.
get(
"_Block_release");
780 CIFilterII = &Ctx.
Idents.
get(
"CIFilter");
789 bool ObjCDeallocChecker::isSuperDeallocMessage(
799 ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
800 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
801 return cast<ObjCImplDecl>(MD->getDeclContext());
817 if (!CatDecl || !CatDecl->IsClassExtension())
824 if (!ShadowedPropDecl)
827 if (ShadowedPropDecl->isInstanceProperty()) {
828 assert(ShadowedPropDecl->isReadOnly());
829 return ShadowedPropDecl;
837 void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C,
840 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
846 removeValueRequiringRelease(InitialState, InstanceSym, Value);
848 if (ReleasedState != InitialState) {
849 C.addTransition(ReleasedState);
859 const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
863 const SymbolSet *Unreleased =
State->get<UnreleasedIvarMap>(Instance);
868 SymbolSet::Factory &F =
State->getStateManager().get_context<SymbolSet>();
869 SymbolSet NewUnreleased = *Unreleased;
870 for (
auto &Sym : *Unreleased) {
871 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
872 assert(UnreleasedRegion);
873 if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
874 NewUnreleased = F.remove(NewUnreleased, Sym);
878 if (NewUnreleased.isEmpty()) {
879 return State->remove<UnreleasedIvarMap>(Instance);
882 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
901 if (isReleasedByCIFilterDealloc(PropImpl))
904 if (isNibLoadedIvarWithoutRetain(PropImpl))
921 llvm_unreachable(
"Unrecognized setter kind");
927 ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
928 CheckerContext &C)
const {
930 if (!ReceiverVal.isValid())
940 SVal Arg = M.getArgSVal(0);
942 std::tie(notNilState, nilState) =
943 M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
944 if (!(nilState && !notNilState))
957 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
962 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
963 return CurrentValInIvar.getAsSymbol();
969 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
970 SVal &SelfValOut)
const {
971 return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
977 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
979 SVal &SelfValOut)
const {
981 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
985 assert(SelfDecl &&
"No self in -dealloc?");
988 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
995 bool ObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext &C,
996 SVal &InstanceValOut)
const {
1000 if (isInInstanceDealloc(C, LCtx, InstanceValOut))
1012 bool ObjCDeallocChecker::classHasSeparateTeardown(
1018 if (II == NSObjectII)
1025 if (II == XCTestCaseII || II == SenTestCaseII)
1040 bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1046 const char *ReleasePrefix =
"input";
1047 if (!(PropName.startswith(ReleasePrefix) ||
1048 IvarName.startswith(ReleasePrefix))) {
1056 if (II == CIFilterII)
1071 bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1074 if (!IvarDecl->
hasAttr<IBOutletAttr>())
1077 const llvm::Triple &
Target =
1080 if (!Target.isMacOSX())
1089 void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
1095 Mgr.registerChecker<ObjCDeallocChecker>();
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