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();
122 bool Assumption)
const;
137 bool diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
148 findPropertyOnDeallocatingInstance(
SymbolRef IvarSym,
156 SVal &SelfValOut)
const;
158 SVal &InstanceValOut)
const;
172 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
191 static void *
GDMIndex() {
static int index = 0;
return &index; }
208 if (classHasSeparateTeardown(ID))
214 bool HasOthers =
false;
217 if (!PropImplRequiringRelease)
218 PropImplRequiringRelease = I;
226 if (!PropImplRequiringRelease)
233 if (I->getSelector() == DeallocSel) {
240 const char* Name =
"Missing -dealloc";
243 llvm::raw_string_ostream OS(Buf);
244 OS <<
"'" << *D <<
"' lacks a 'dealloc' instance method but " 262 void ObjCDeallocChecker::checkBeginFunction(
268 if (!isInInstanceDealloc(C, SelfVal))
271 SymbolRef SelfSymbol = SelfVal.getAsSymbol();
281 SymbolSet RequiredReleases = F.getEmptySet();
285 if (
const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
286 RequiredReleases = *CurrSet;
288 for (
auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
293 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
298 SVal InitialVal = State->getSVal(LValLoc.getValue());
300 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
304 RequiredReleases = F.add(RequiredReleases, Symbol);
307 if (!RequiredReleases.isEmpty()) {
308 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
311 if (State != InitialState) {
319 ObjCDeallocChecker::getIvarRegionForIvarSymbol(
SymbolRef IvarSym)
const {
326 ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(
SymbolRef IvarSym)
const {
328 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
337 void ObjCDeallocChecker::checkPreObjCMessage(
340 SVal DeallocedInstance;
341 if (!instanceDeallocIsOnStack(C, DeallocedInstance))
356 if (diagnoseExtraRelease(ReleasedValue,M, C))
361 ReleasedValue = getValueReleasedByNillingOut(M, C);
367 transitionToReleaseValue(C, ReleasedValue);
372 void ObjCDeallocChecker::checkPreCall(
const CallEvent &Call,
375 if (II != Block_releaseII)
385 transitionToReleaseValue(C, ReleasedValue);
389 void ObjCDeallocChecker::checkPostObjCMessage(
394 if (isSuperDeallocMessage(M))
395 diagnoseMissingReleases(C);
400 void ObjCDeallocChecker::checkEndFunction(
402 diagnoseMissingReleases(C);
406 void ObjCDeallocChecker::checkPreStmt(
408 diagnoseMissingReleases(C);
414 bool Assumption)
const {
415 if (State->get<UnreleasedIvarMap>().isEmpty())
418 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.
getAsSymExpr());
432 if (
auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
433 const llvm::APInt &RHS = SIE->getRHS();
436 NullSymbol = SIE->getLHS();
437 }
else if (
auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
438 const llvm::APInt &LHS = SIE->getLHS();
441 NullSymbol = SIE->getRHS();
446 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
450 State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
460 if (State->get<UnreleasedIvarMap>().isEmpty())
467 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
468 if (OMC && isSuperDeallocMessage(*OMC))
471 for (
const auto &Sym : Escaped) {
480 State = State->remove<UnreleasedIvarMap>(Sym);
484 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
488 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
496 void ObjCDeallocChecker::diagnoseMissingReleases(
CheckerContext &C)
const {
500 if (!isInInstanceDealloc(C, SelfVal))
512 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
516 SymbolSet NewUnreleased = *OldUnreleased;
521 for (
auto *IvarSymbol : *OldUnreleased) {
523 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
534 cast<ObjCMethodDecl>(LCtx->
getDecl())->getClassInterface())
539 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
541 if (State->getStateManager()
542 .getConstraintManager()
543 .isNull(State, IvarSymbol)
544 .isConstrainedTrue()) {
557 llvm::raw_string_ostream OS(Buf);
564 if (classHasSeparateTeardown(Interface))
577 OS <<
"The '" << *IvarDecl <<
"' ivar in '" << *ImplDecl
585 OS <<
" by a synthesized property but not released" 586 " before '[super dealloc]'";
588 std::unique_ptr<BugReport> BR(
589 new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
594 if (NewUnreleased.isEmpty()) {
595 State = State->remove<UnreleasedIvarMap>(SelfSym);
597 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
602 }
else if (State != InitialState) {
609 assert(!LCtx->
inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
616 ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
618 SVal DeallocedInstance;
619 if (!isInInstanceDealloc(C, DeallocedInstance))
623 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
630 IvarRegion->getSuperRegion())
636 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
645 bool ObjCDeallocChecker::diagnoseExtraRelease(
SymbolRef ReleasedValue,
654 findPropertyOnDeallocatingInstance(ReleasedValue, C);
661 if (getDeallocReleaseRequirement(PropImpl) !=
685 llvm::raw_string_ostream OS(Buf);
690 isReleasedByCIFilterDealloc(PropImpl)
695 <<
"' ivar in '" << *Container;
698 if (isReleasedByCIFilterDealloc(PropImpl)) {
699 OS <<
"' will be released by '-[CIFilter dealloc]' but also released here";
701 OS <<
"' was synthesized for ";
706 OS <<
"an assign, readwrite";
708 OS <<
" property but was released in 'dealloc'";
711 std::unique_ptr<BugReport> BR(
712 new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
723 bool ObjCDeallocChecker::diagnoseMistakenDealloc(
SymbolRef DeallocedValue,
730 findPropertyOnDeallocatingInstance(DeallocedValue, C);
734 if (getDeallocReleaseRequirement(PropImpl) !=
744 llvm::raw_string_ostream OS(Buf);
747 <<
"' should be released rather than deallocated";
749 std::unique_ptr<BugReport> BR(
750 new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
758 ObjCDeallocChecker::ObjCDeallocChecker()
759 : NSObjectII(
nullptr), SenTestCaseII(
nullptr), XCTestCaseII(
nullptr),
760 CIFilterII(
nullptr) {
762 MissingReleaseBugType.reset(
763 new BugType(
this,
"Missing ivar release (leak)",
766 ExtraReleaseBugType.reset(
767 new BugType(
this,
"Extra ivar release",
770 MistakenDeallocBugType.reset(
771 new BugType(
this,
"Mistaken dealloc",
775 void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
780 NSObjectII = &Ctx.
Idents.
get(
"NSObject");
781 SenTestCaseII = &Ctx.
Idents.
get(
"SenTestCase");
782 XCTestCaseII = &Ctx.
Idents.
get(
"XCTestCase");
783 Block_releaseII = &Ctx.
Idents.
get(
"_Block_release");
784 CIFilterII = &Ctx.
Idents.
get(
"CIFilter");
793 bool ObjCDeallocChecker::isSuperDeallocMessage(
803 ObjCDeallocChecker::getContainingObjCImpl(
const LocationContext *LCtx)
const {
804 auto *MD = cast<ObjCMethodDecl>(LCtx->
getDecl());
805 return cast<ObjCImplDecl>(MD->getDeclContext());
821 if (!CatDecl || !CatDecl->IsClassExtension())
828 if (!ShadowedPropDecl)
831 if (ShadowedPropDecl->isInstanceProperty()) {
832 assert(ShadowedPropDecl->isReadOnly());
833 return ShadowedPropDecl;
841 void ObjCDeallocChecker::transitionToReleaseValue(
CheckerContext &C,
844 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
850 removeValueRequiringRelease(InitialState, InstanceSym, Value);
852 if (ReleasedState != InitialState) {
863 const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
867 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
874 for (
auto &Sym : *Unreleased) {
875 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
876 assert(UnreleasedRegion);
878 NewUnreleased = F.remove(NewUnreleased, Sym);
882 if (NewUnreleased.isEmpty()) {
883 return State->remove<UnreleasedIvarMap>(Instance);
886 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
905 if (isReleasedByCIFilterDealloc(PropImpl))
908 if (isNibLoadedIvarWithoutRetain(PropImpl))
925 llvm_unreachable(
"Unrecognized setter kind");
931 ObjCDeallocChecker::getValueReleasedByNillingOut(
const ObjCMethodCall &M,
944 SVal Arg = M.getArgSVal(0);
946 std::tie(notNilState, nilState) =
948 if (!(nilState && !notNilState))
961 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
966 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
973 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
974 SVal &SelfValOut)
const {
981 bool ObjCDeallocChecker::isInInstanceDealloc(
const CheckerContext &C,
983 SVal &SelfValOut)
const {
985 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
989 assert(SelfDecl &&
"No self in -dealloc?");
992 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
999 bool ObjCDeallocChecker::instanceDeallocIsOnStack(
const CheckerContext &C,
1000 SVal &InstanceValOut)
const {
1004 if (isInInstanceDealloc(C, LCtx, InstanceValOut))
1016 bool ObjCDeallocChecker::classHasSeparateTeardown(
1022 if (II == NSObjectII)
1029 if (II == XCTestCaseII || II == SenTestCaseII)
1044 bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1050 const char *ReleasePrefix =
"input";
1051 if (!(PropName.startswith(ReleasePrefix) ||
1052 IvarName.startswith(ReleasePrefix))) {
1060 if (II == CIFilterII)
1075 bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1078 if (!IvarDecl->
hasAttr<IBOutletAttr>())
1081 const llvm::Triple &
Target =
1084 if (!Target.isMacOSX())
The receiver is the instance of the superclass object.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
TypedValueRegion - An abstract class representing regions having a typed value.
const char *const CoreFoundationObjectiveC
Smart pointer class that efficiently represents Objective-C method names.
const ObjCIvarDecl * getDecl() const
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.
ObjCIvarDecl * getPropertyIvarDecl() const
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
const TargetInfo & getTargetInfo() const
ObjCMethodDecl - Represents an instance or class method declaration.
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
The instance variable must be released, either by calling -release on it directly or by nilling it ou...
const char *const MemoryCoreFoundationObjectiveC
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
const MemRegion * getSuperRegion() const
const SymbolicRegion * getSymbolicBase() const
If this is a symbolic region, returns the region.
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 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.
ASTContext & getASTContext() override
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
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'.
#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.
bool isObjCRetainableType() const
bool isInSystemHeader() const
Returns true if the callee is known to be from a system header.
DeclContext * getDeclContext()
ObjCInterfaceDecl * getSuperClass() const
const IdentifierInfo * getCalleeIdentifier() const
Returns the name of the callee, if its name is a simple identifier.
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.
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
CHECKER * registerChecker(AT... Args)
Used to register checkers.
const MemRegion * getRegion() const
Get the underlining region.
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
SymbolRef getSymbol() const
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
ASTContext & getASTContext() const LLVM_READONLY
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
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.
ASTContext & getASTContext()
Kind getPropertyImplementation() const
const LangOptions & getLangOpts() const
const ObjCInterfaceDecl * getContainingInterface() const
Return the class interface that this ivar is logically contained in; this is either the interface whe...
Represents an abstract call to a function or method along a particular path.
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...
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
The requirement for the instance variable could not be determined.
const ProgramStateRef & getState() const
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
const SymExpr * getAsSymExpr() 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.
ObjCIvarDecl - Represents an ObjC instance variable.
ObjCIvarDecl * getPropertyIvarDecl() const
virtual unsigned getNumArgs() const =0
Returns the number of arguments (explicit and implicit).
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
llvm::ImmutableSet< SymbolRef > SymbolSet
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
static llvm::ImmutableListFactory< const FieldRegion * > Factory
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
This class provides an interface through which checkers can create individual bug reports...
const LangOptions & getLangOpts() const
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=None)
const LocationContext * getLocationContext() const